/* * File : signal.c * This file is part of RT-Thread RTOS * COPYRIGHT (C) 2006 - 2017, RT-Thread Development Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * Change Logs: * Date Author Notes * 2017/10/5 Bernard the first version */ #include #include #include #include #ifdef RT_USING_SIGNALS #ifndef RT_SIG_INFO_MAX #define RT_SIG_INFO_MAX 32 #endif // #define DBG_ENABLE #define DBG_SECTION_NAME "SIGN" #define DBG_COLOR #define DBG_LEVEL DBG_LOG #include #define sig_mask(sig_no) (1u << sig_no) #define sig_valid(sig_no) (sig_no >= 0 && sig_no < RT_SIG_MAX) struct siginfo_node { siginfo_t si; struct rt_slist_node list; }; static struct rt_mempool *_rt_siginfo_pool; static void _signal_deliver(rt_thread_t tid); void rt_thread_handle_sig(rt_bool_t clean_state); static void _signal_default_handler(int signo) { dbg_log(DBG_INFO, "handled signo[%d] with default action.\n", signo); return ; } static void _signal_entry(void *parameter) { rt_thread_t tid = rt_thread_self(); dbg_enter; /* handle signal */ rt_thread_handle_sig(RT_FALSE); /* never come back... */ rt_hw_interrupt_disable(); /* return to thread */ tid->sp = tid->sig_ret; tid->sig_ret = RT_NULL; dbg_log(DBG_LOG, "switch back to: 0x%08x\n", tid->sp); tid->stat &= ~RT_THREAD_STAT_SIGNAL; rt_hw_context_switch_to((rt_uint32_t) & (tid->sp)); } /* * To deliver a signal to thread, there are cases: * 1. When thread is suspended, function resumes thread and * set signal stat; * 2. When thread is ready: * - If function delivers a signal to self thread, just handle * it. * - If function delivers a signal to another ready thread, OS * should build a slice context to handle it. */ static void _signal_deliver(rt_thread_t tid) { rt_ubase_t level; /* thread is not interested in pended signals */ if (!(tid->sig_pending & tid->sig_mask)) return; level = rt_hw_interrupt_disable(); if ((tid->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND) { /* resume thread to handle signal */ rt_thread_resume(tid); /* add signal state */ tid->stat |= RT_THREAD_STAT_SIGNAL; rt_hw_interrupt_enable(level); /* re-schedule */ rt_schedule(); } else { if (tid == rt_thread_self()) { /* add signal state */ tid->stat |= RT_THREAD_STAT_SIGNAL; rt_hw_interrupt_enable(level); /* do signal action in self thread context */ rt_thread_handle_sig(RT_TRUE); } else if (!((tid->stat & RT_THREAD_STAT_MASK) & RT_THREAD_STAT_SIGNAL)) { /* add signal state */ tid->stat |= RT_THREAD_STAT_SIGNAL; /* point to the signal handle entry */ tid->sig_ret = tid->sp; tid->sp = rt_hw_stack_init((void *)_signal_entry, RT_NULL, (void *)((char *)tid->sig_ret - 32), RT_NULL); rt_hw_interrupt_enable(level); dbg_log(DBG_LOG, "signal stack pointer @ 0x%08x\n", tid->sp); /* re-schedule */ rt_schedule(); } else { rt_hw_interrupt_enable(level); } } } rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler) { rt_sighandler_t old; rt_thread_t tid = rt_thread_self(); if (!sig_valid(signo)) return SIG_ERR; rt_enter_critical(); if (tid->sig_vectors == RT_NULL) { rt_thread_alloc_sig(tid); } if (tid->sig_vectors) { old = tid->sig_vectors[signo]; if (handler == SIG_IGN) tid->sig_vectors[signo] = RT_NULL; else if (handler == SIG_DFL) tid->sig_vectors[signo] = _signal_default_handler; else tid->sig_vectors[signo] = handler; } rt_exit_critical(); return old; } void rt_signal_mask(int signo) { rt_base_t level; rt_thread_t tid = rt_thread_self(); level = rt_hw_interrupt_disable(); tid->sig_mask &= ~sig_mask(signo); rt_hw_interrupt_enable(level); } void rt_signal_unmask(int signo) { rt_base_t level; rt_thread_t tid = rt_thread_self(); level = rt_hw_interrupt_disable(); tid->sig_mask |= sig_mask(signo); /* let thread handle pended signals */ if (tid->sig_mask & tid->sig_pending) { rt_hw_interrupt_enable(level); _signal_deliver(tid); } else { rt_hw_interrupt_enable(level); } } int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout) { int ret = RT_EOK; rt_base_t level; rt_thread_t tid = rt_thread_self(); struct siginfo_node *si_node = RT_NULL, *si_prev = RT_NULL; /* current context checking */ RT_DEBUG_IN_THREAD_CONTEXT; /* parameters check */ if (set == NULL || *set == 0 || si == NULL ) { ret = -RT_EINVAL; goto __done_return; } /* clear siginfo to avoid unknown value */ memset(si, 0x0, sizeof(rt_siginfo_t)); level = rt_hw_interrupt_disable(); /* already pending */ if (tid->sig_pending & *set) goto __done; if (timeout == 0) { ret = -RT_ETIMEOUT; goto __done_int; } /* suspend self thread */ rt_thread_suspend(tid); /* set thread stat as waiting for signal */ tid->stat |= RT_THREAD_STAT_SIGNAL_WAIT; /* start timeout timer */ if (timeout != RT_WAITING_FOREVER) { /* reset the timeout of thread timer and start it */ rt_timer_control(&(tid->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout); rt_timer_start(&(tid->thread_timer)); } rt_hw_interrupt_enable(level); /* do thread scheduling */ rt_schedule(); level = rt_hw_interrupt_disable(); /* remove signal waiting flag */ tid->stat &= ~RT_THREAD_STAT_SIGNAL_WAIT; /* check errno of thread */ if (tid->error == -RT_ETIMEOUT) { tid->error = RT_EOK; rt_hw_interrupt_enable(level); /* timer timeout */ ret = -RT_ETIMEOUT; goto __done_return; } __done: /* to get the first matched pending signals */ si_node = (struct siginfo_node *)tid->si_list; while (si_node) { int signo; signo = si_node->si.si_signo; if (sig_mask(signo) & *set) { *si = si_node->si; dbg_log(DBG_LOG, "sigwait: %d sig raised!\n", signo); if (si_prev) si_prev->list.next = si_node->list.next; else tid->si_list = si_node->list.next; /* clear pending */ tid->sig_pending &= ~sig_mask(signo); rt_mp_free(si_node); break; } si_prev = si_node; si_node = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list); } __done_int: rt_hw_interrupt_enable(level); __done_return: return ret; } void rt_thread_handle_sig(rt_bool_t clean_state) { rt_base_t level; rt_thread_t tid = rt_thread_self(); struct siginfo_node *si_node; level = rt_hw_interrupt_disable(); if (tid->sig_pending & tid->sig_mask) { /* if thread is not waiting for signal */ if (!(tid->stat & RT_THREAD_STAT_SIGNAL_WAIT)) { while (tid->sig_pending & tid->sig_mask) { int signo, error; rt_sighandler_t handler; si_node = (struct siginfo_node *)tid->si_list; if (!si_node) break; /* remove this sig info node from list */ if (si_node->list.next == RT_NULL) tid->si_list = RT_NULL; else tid->si_list = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list); signo = si_node->si.si_signo; handler = tid->sig_vectors[signo]; rt_hw_interrupt_enable(level); dbg_log(DBG_LOG, "handle signal: %d, handler 0x%08x\n", signo, handler); if (handler) handler(signo); level = rt_hw_interrupt_disable(); tid->sig_pending &= ~sig_mask(signo); error = -RT_EINTR; rt_mp_free(si_node); /* release this siginfo node */ /* set errno in thread tcb */ tid->error = error; } /* whether clean signal status */ if (clean_state == RT_TRUE) tid->stat &= ~RT_THREAD_STAT_SIGNAL; } } rt_hw_interrupt_enable(level); } void rt_thread_alloc_sig(rt_thread_t tid) { int index; rt_base_t level; rt_sighandler_t *vectors; vectors = (rt_sighandler_t *)RT_KERNEL_MALLOC(sizeof(rt_sighandler_t) * RT_SIG_MAX); RT_ASSERT(vectors != RT_NULL); for (index = 0; index < RT_SIG_MAX; index ++) { vectors[index] = _signal_default_handler; } level = rt_hw_interrupt_disable(); tid->sig_vectors = vectors; rt_hw_interrupt_enable(level); } void rt_thread_free_sig(rt_thread_t tid) { rt_base_t level; struct siginfo_node *si_list; rt_sighandler_t *sig_vectors; level = rt_hw_interrupt_disable(); si_list = (struct siginfo_node *)tid->si_list; tid->si_list = RT_NULL; sig_vectors = tid->sig_vectors; tid->sig_vectors = RT_NULL; rt_hw_interrupt_enable(level); if (si_list) { struct rt_slist_node *node; struct siginfo_node *si_node; dbg_log(DBG_LOG, "free signal info list\n"); node = &(si_list->list); do { si_node = rt_slist_entry(node, struct siginfo_node, list); rt_mp_free(si_node); node = node->next; } while (node); } if (sig_vectors) { RT_KERNEL_FREE(sig_vectors); } } int rt_thread_kill(rt_thread_t tid, int sig) { siginfo_t si; rt_base_t level; struct siginfo_node *si_node; RT_ASSERT(tid != RT_NULL); if (!sig_valid(sig)) return -RT_EINVAL; dbg_log(DBG_INFO, "send signal: %d\n", sig); si.si_signo = sig; si.si_code = SI_USER; si.si_value.sival_ptr = RT_NULL; level = rt_hw_interrupt_disable(); if (tid->sig_pending & sig_mask(sig)) { /* whether already emits this signal? */ struct rt_slist_node *node; struct siginfo_node *entry; node = (struct rt_slist_node *)tid->si_list; rt_hw_interrupt_enable(level); /* update sig info */ rt_enter_critical(); for (; (node) != RT_NULL; node = node->next) { entry = rt_slist_entry(node, struct siginfo_node, list); if (entry->si.si_signo == sig) { memcpy(&(entry->si), &si, sizeof(siginfo_t)); rt_exit_critical(); return 0; } } rt_exit_critical(); /* disable interrupt to protect tcb */ level = rt_hw_interrupt_disable(); } else { /* a new signal */ tid->sig_pending |= sig_mask(sig); } rt_hw_interrupt_enable(level); si_node = (struct siginfo_node *) rt_mp_alloc(_rt_siginfo_pool, 0); if (si_node) { rt_slist_init(&(si_node->list)); memcpy(&(si_node->si), &si, sizeof(siginfo_t)); level = rt_hw_interrupt_disable(); if (!tid->si_list) tid->si_list = si_node; else { struct siginfo_node *si_list; si_list = (struct siginfo_node *)tid->si_list; rt_slist_append(&(si_list->list), &(si_node->list)); } rt_hw_interrupt_enable(level); } else { dbg_log(DBG_ERROR, "The allocation of signal info node failed.\n"); } /* deliver signal to this thread */ _signal_deliver(tid); return RT_EOK; } int rt_system_signal_init(void) { _rt_siginfo_pool = rt_mp_create("signal", RT_SIG_INFO_MAX, sizeof(struct siginfo_node)); if (_rt_siginfo_pool == RT_NULL) { dbg_log(DBG_ERROR, "create memory pool for signal info failed.\n"); RT_ASSERT(0); } return 0; } #endif