xref: /nrf52832-nimble/rt-thread/src/signal.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * Copyright (c) 2006-2018, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2017/10/5      Bernard      the first version
9  * 2018/09/17     Jesven       fix: in _signal_deliver RT_THREAD_STAT_MASK to RT_THREAD_STAT_SIGNAL_MASK
10  * 2018/11/22     Jesven       in smp version rt_hw_context_switch_to add a param
11  */
12 
13 #include <stdint.h>
14 #include <string.h>
15 
16 #include <rthw.h>
17 #include <rtthread.h>
18 
19 #ifdef RT_USING_SIGNALS
20 
21 #ifndef RT_SIG_INFO_MAX
22 #define RT_SIG_INFO_MAX 32
23 #endif
24 
25 // #define DBG_ENABLE
26 #define DBG_SECTION_NAME    "SIGN"
27 #define DBG_COLOR
28 #define DBG_LEVEL           DBG_LOG
29 #include <rtdbg.h>
30 
31 #define sig_mask(sig_no)    (1u << sig_no)
32 #define sig_valid(sig_no)   (sig_no >= 0 && sig_no < RT_SIG_MAX)
33 
34 struct siginfo_node
35 {
36     siginfo_t si;
37     struct rt_slist_node list;
38 };
39 
40 static struct rt_mempool *_rt_siginfo_pool;
41 static void _signal_deliver(rt_thread_t tid);
42 void rt_thread_handle_sig(rt_bool_t clean_state);
43 
_signal_default_handler(int signo)44 static void _signal_default_handler(int signo)
45 {
46     LOG_I("handled signo[%d] with default action.", signo);
47     return ;
48 }
49 
_signal_entry(void * parameter)50 static void _signal_entry(void *parameter)
51 {
52     rt_thread_t tid = rt_thread_self();
53 
54     dbg_enter;
55 
56     /* handle signal */
57     rt_thread_handle_sig(RT_FALSE);
58 
59     /* never come back... */
60     rt_hw_interrupt_disable();
61     /* return to thread */
62     tid->sp = tid->sig_ret;
63     tid->sig_ret = RT_NULL;
64 
65     LOG_D("switch back to: 0x%08x\n", tid->sp);
66     tid->stat &= ~RT_THREAD_STAT_SIGNAL;
67 
68 #ifdef RT_USING_SMP
69     rt_hw_context_switch_to((rt_ubase_t)&(tid->sp), tid);
70 #else
71     rt_hw_context_switch_to((rt_ubase_t)&(tid->sp));
72 #endif /*RT_USING_SMP*/
73 }
74 
75 /*
76  * To deliver a signal to thread, there are cases:
77  * 1. When thread is suspended, function resumes thread and
78  * set signal stat;
79  * 2. When thread is ready:
80  *   - If function delivers a signal to self thread, just handle
81  *    it.
82  *   - If function delivers a signal to another ready thread, OS
83  *    should build a slice context to handle it.
84  */
_signal_deliver(rt_thread_t tid)85 static void _signal_deliver(rt_thread_t tid)
86 {
87     rt_ubase_t level;
88 
89     /* thread is not interested in pended signals */
90     if (!(tid->sig_pending & tid->sig_mask)) return;
91 
92     level = rt_hw_interrupt_disable();
93     if ((tid->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND)
94     {
95         /* resume thread to handle signal */
96         rt_thread_resume(tid);
97         /* add signal state */
98         tid->stat |= RT_THREAD_STAT_SIGNAL;
99 
100         rt_hw_interrupt_enable(level);
101 
102         /* re-schedule */
103         rt_schedule();
104     }
105     else
106     {
107         if (tid == rt_thread_self())
108         {
109             /* add signal state */
110             tid->stat |= RT_THREAD_STAT_SIGNAL;
111 
112             rt_hw_interrupt_enable(level);
113 
114             /* do signal action in self thread context */
115             rt_thread_handle_sig(RT_TRUE);
116         }
117         else if (!((tid->stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL))
118         {
119             /* add signal state */
120             tid->stat |= RT_THREAD_STAT_SIGNAL;
121 
122             /* point to the signal handle entry */
123             tid->sig_ret = tid->sp;
124             tid->sp = rt_hw_stack_init((void *)_signal_entry, RT_NULL,
125                                        (void *)((char *)tid->sig_ret - 32), RT_NULL);
126 
127             rt_hw_interrupt_enable(level);
128             LOG_D("signal stack pointer @ 0x%08x", tid->sp);
129 
130             /* re-schedule */
131             rt_schedule();
132         }
133         else
134         {
135             rt_hw_interrupt_enable(level);
136         }
137     }
138 }
139 
rt_signal_install(int signo,rt_sighandler_t handler)140 rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler)
141 {
142     rt_sighandler_t old = RT_NULL;
143     rt_thread_t tid = rt_thread_self();
144 
145     if (!sig_valid(signo)) return SIG_ERR;
146 
147     rt_enter_critical();
148     if (tid->sig_vectors == RT_NULL)
149     {
150         rt_thread_alloc_sig(tid);
151     }
152 
153     if (tid->sig_vectors)
154     {
155         old = tid->sig_vectors[signo];
156 
157         if (handler == SIG_IGN) tid->sig_vectors[signo] = RT_NULL;
158         else if (handler == SIG_DFL) tid->sig_vectors[signo] = _signal_default_handler;
159         else tid->sig_vectors[signo] = handler;
160     }
161     rt_exit_critical();
162 
163     return old;
164 }
165 
rt_signal_mask(int signo)166 void rt_signal_mask(int signo)
167 {
168     rt_base_t level;
169     rt_thread_t tid = rt_thread_self();
170 
171     level = rt_hw_interrupt_disable();
172 
173     tid->sig_mask &= ~sig_mask(signo);
174 
175     rt_hw_interrupt_enable(level);
176 }
177 
rt_signal_unmask(int signo)178 void rt_signal_unmask(int signo)
179 {
180     rt_base_t level;
181     rt_thread_t tid = rt_thread_self();
182 
183     level = rt_hw_interrupt_disable();
184 
185     tid->sig_mask |= sig_mask(signo);
186 
187     /* let thread handle pended signals */
188     if (tid->sig_mask & tid->sig_pending)
189     {
190         rt_hw_interrupt_enable(level);
191         _signal_deliver(tid);
192     }
193     else
194     {
195         rt_hw_interrupt_enable(level);
196     }
197 }
198 
rt_signal_wait(const rt_sigset_t * set,rt_siginfo_t * si,rt_int32_t timeout)199 int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout)
200 {
201     int ret = RT_EOK;
202     rt_base_t   level;
203     rt_thread_t tid = rt_thread_self();
204     struct siginfo_node *si_node = RT_NULL, *si_prev = RT_NULL;
205 
206     /* current context checking */
207     RT_DEBUG_IN_THREAD_CONTEXT;
208 
209     /* parameters check */
210     if (set == NULL || *set == 0 || si == NULL )
211     {
212         ret = -RT_EINVAL;
213         goto __done_return;
214     }
215 
216     /* clear siginfo to avoid unknown value */
217     memset(si, 0x0, sizeof(rt_siginfo_t));
218 
219     level = rt_hw_interrupt_disable();
220 
221     /* already pending */
222     if (tid->sig_pending & *set) goto __done;
223 
224     if (timeout == 0)
225     {
226         ret = -RT_ETIMEOUT;
227         goto __done_int;
228     }
229 
230     /* suspend self thread */
231     rt_thread_suspend(tid);
232     /* set thread stat as waiting for signal */
233     tid->stat |= RT_THREAD_STAT_SIGNAL_WAIT;
234 
235     /* start timeout timer */
236     if (timeout != RT_WAITING_FOREVER)
237     {
238         /* reset the timeout of thread timer and start it */
239         rt_timer_control(&(tid->thread_timer),
240                          RT_TIMER_CTRL_SET_TIME,
241                          &timeout);
242         rt_timer_start(&(tid->thread_timer));
243     }
244     rt_hw_interrupt_enable(level);
245 
246     /* do thread scheduling */
247     rt_schedule();
248 
249     level = rt_hw_interrupt_disable();
250 
251     /* remove signal waiting flag */
252     tid->stat &= ~RT_THREAD_STAT_SIGNAL_WAIT;
253 
254     /* check errno of thread */
255     if (tid->error == -RT_ETIMEOUT)
256     {
257         tid->error = RT_EOK;
258         rt_hw_interrupt_enable(level);
259 
260         /* timer timeout */
261         ret = -RT_ETIMEOUT;
262         goto __done_return;
263     }
264 
265 __done:
266     /* to get the first matched pending signals */
267     si_node = (struct siginfo_node *)tid->si_list;
268     while (si_node)
269     {
270         int signo;
271 
272         signo = si_node->si.si_signo;
273         if (sig_mask(signo) & *set)
274         {
275             *si  = si_node->si;
276 
277             LOG_D("sigwait: %d sig raised!", signo);
278             if (si_prev) si_prev->list.next = si_node->list.next;
279             else tid->si_list = si_node->list.next;
280 
281             /* clear pending */
282             tid->sig_pending &= ~sig_mask(signo);
283             rt_mp_free(si_node);
284             break;
285         }
286 
287         si_prev = si_node;
288         si_node = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);
289      }
290 
291 __done_int:
292     rt_hw_interrupt_enable(level);
293 
294 __done_return:
295     return ret;
296 }
297 
rt_thread_handle_sig(rt_bool_t clean_state)298 void rt_thread_handle_sig(rt_bool_t clean_state)
299 {
300     rt_base_t level;
301 
302     rt_thread_t tid = rt_thread_self();
303     struct siginfo_node *si_node;
304 
305     level = rt_hw_interrupt_disable();
306     if (tid->sig_pending & tid->sig_mask)
307     {
308         /* if thread is not waiting for signal */
309         if (!(tid->stat & RT_THREAD_STAT_SIGNAL_WAIT))
310         {
311             while (tid->sig_pending & tid->sig_mask)
312             {
313                 int signo, error;
314                 rt_sighandler_t handler;
315 
316                 si_node = (struct siginfo_node *)tid->si_list;
317                 if (!si_node) break;
318 
319                 /* remove this sig info node from list */
320                 if (si_node->list.next == RT_NULL)
321                     tid->si_list = RT_NULL;
322                 else
323                     tid->si_list = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);
324 
325                 signo   = si_node->si.si_signo;
326                 handler = tid->sig_vectors[signo];
327                 rt_hw_interrupt_enable(level);
328 
329                 LOG_D("handle signal: %d, handler 0x%08x", signo, handler);
330                 if (handler) handler(signo);
331 
332                 level = rt_hw_interrupt_disable();
333                 tid->sig_pending &= ~sig_mask(signo);
334                 error = -RT_EINTR;
335 
336                 rt_mp_free(si_node); /* release this siginfo node */
337                 /* set errno in thread tcb */
338                 tid->error = error;
339             }
340 
341             /* whether clean signal status */
342             if (clean_state == RT_TRUE) tid->stat &= ~RT_THREAD_STAT_SIGNAL;
343         }
344     }
345 
346     rt_hw_interrupt_enable(level);
347 }
348 
rt_thread_alloc_sig(rt_thread_t tid)349 void rt_thread_alloc_sig(rt_thread_t tid)
350 {
351     int index;
352     rt_base_t level;
353     rt_sighandler_t *vectors;
354 
355     vectors = (rt_sighandler_t *)RT_KERNEL_MALLOC(sizeof(rt_sighandler_t) * RT_SIG_MAX);
356     RT_ASSERT(vectors != RT_NULL);
357 
358     for (index = 0; index < RT_SIG_MAX; index ++)
359     {
360         vectors[index] = _signal_default_handler;
361     }
362 
363     level = rt_hw_interrupt_disable();
364     tid->sig_vectors = vectors;
365     rt_hw_interrupt_enable(level);
366 }
367 
rt_thread_free_sig(rt_thread_t tid)368 void rt_thread_free_sig(rt_thread_t tid)
369 {
370     rt_base_t level;
371     struct siginfo_node *si_list;
372     rt_sighandler_t *sig_vectors;
373 
374     level = rt_hw_interrupt_disable();
375     si_list = (struct siginfo_node *)tid->si_list;
376     tid->si_list = RT_NULL;
377 
378     sig_vectors = tid->sig_vectors;
379     tid->sig_vectors = RT_NULL;
380     rt_hw_interrupt_enable(level);
381 
382     if (si_list)
383     {
384         struct rt_slist_node *node;
385         struct siginfo_node  *si_node;
386 
387         LOG_D("free signal info list");
388         node = &(si_list->list);
389         do
390         {
391             si_node = rt_slist_entry(node, struct siginfo_node, list);
392             rt_mp_free(si_node);
393 
394             node = node->next;
395         } while (node);
396     }
397 
398     if (sig_vectors)
399     {
400         RT_KERNEL_FREE(sig_vectors);
401     }
402 }
403 
rt_thread_kill(rt_thread_t tid,int sig)404 int rt_thread_kill(rt_thread_t tid, int sig)
405 {
406     siginfo_t si;
407     rt_base_t level;
408     struct siginfo_node *si_node;
409 
410     RT_ASSERT(tid != RT_NULL);
411     if (!sig_valid(sig)) return -RT_EINVAL;
412 
413     LOG_I("send signal: %d", sig);
414     si.si_signo = sig;
415     si.si_code  = SI_USER;
416     si.si_value.sival_ptr = RT_NULL;
417 
418     level = rt_hw_interrupt_disable();
419     if (tid->sig_pending & sig_mask(sig))
420     {
421         /* whether already emits this signal? */
422         struct rt_slist_node *node;
423         struct siginfo_node  *entry;
424 
425         node = (struct rt_slist_node *)tid->si_list;
426         rt_hw_interrupt_enable(level);
427 
428         /* update sig info */
429         rt_enter_critical();
430         for (; (node) != RT_NULL; node = node->next)
431         {
432             entry = rt_slist_entry(node, struct siginfo_node, list);
433             if (entry->si.si_signo == sig)
434             {
435                 memcpy(&(entry->si), &si, sizeof(siginfo_t));
436                 rt_exit_critical();
437                 return 0;
438             }
439         }
440         rt_exit_critical();
441 
442         /* disable interrupt to protect tcb */
443         level = rt_hw_interrupt_disable();
444     }
445     else
446     {
447         /* a new signal */
448         tid->sig_pending |= sig_mask(sig);
449     }
450     rt_hw_interrupt_enable(level);
451 
452     si_node = (struct siginfo_node *) rt_mp_alloc(_rt_siginfo_pool, 0);
453     if (si_node)
454     {
455         rt_slist_init(&(si_node->list));
456         memcpy(&(si_node->si), &si, sizeof(siginfo_t));
457 
458         level = rt_hw_interrupt_disable();
459         if (!tid->si_list) tid->si_list = si_node;
460         else
461         {
462             struct siginfo_node *si_list;
463 
464             si_list = (struct siginfo_node *)tid->si_list;
465             rt_slist_append(&(si_list->list), &(si_node->list));
466         }
467         rt_hw_interrupt_enable(level);
468     }
469     else
470     {
471         LOG_E("The allocation of signal info node failed.");
472     }
473 
474     /* deliver signal to this thread */
475     _signal_deliver(tid);
476 
477     return RT_EOK;
478 }
479 
rt_system_signal_init(void)480 int rt_system_signal_init(void)
481 {
482     _rt_siginfo_pool = rt_mp_create("signal", RT_SIG_INFO_MAX, sizeof(struct siginfo_node));
483     if (_rt_siginfo_pool == RT_NULL)
484     {
485         LOG_E("create memory pool for signal info failed.");
486         RT_ASSERT(0);
487     }
488 
489     return 0;
490 }
491 
492 #endif
493