From 38b9ed3118f7df9fd683b9d3bd4425ab604ee2bc Mon Sep 17 00:00:00 2001 From: BernardXiong Date: Thu, 1 Mar 2018 13:36:22 +0800 Subject: [PATCH] [Kernel] add sigwait implementation --- components/libc/signal/posix_signal.c | 85 ++++++++----- components/libc/signal/posix_signal.h | 51 +------- include/libc/libc_signal.h | 38 +++--- include/rtdef.h | 4 +- include/rtthread.h | 1 + src/scheduler.c | 4 +- src/signal.c | 176 +++++++++++++++++++++----- src/thread.c | 4 +- src/timer.c | 2 +- 9 files changed, 227 insertions(+), 138 deletions(-) diff --git a/components/libc/signal/posix_signal.c b/components/libc/signal/posix_signal.c index 1bf61aad70..b950fc956c 100644 --- a/components/libc/signal/posix_signal.c +++ b/components/libc/signal/posix_signal.c @@ -31,37 +31,37 @@ void (*signal(int sig, void (*func)(int))) (int) { - return rt_signal_install(sig, func); + return rt_signal_install(sig, func); } int sigprocmask (int how, const sigset_t *set, sigset_t *oset) { - rt_base_t level; + rt_base_t level; rt_thread_t tid; tid = rt_thread_self(); - level = rt_hw_interrupt_disable(); - if (oset) *oset = tid->sig_mask; + level = rt_hw_interrupt_disable(); + if (oset) *oset = tid->sig_mask; - if (set) - { - switch(how) - { - case SIG_BLOCK: - tid->sig_mask |= *set; - break; - case SIG_UNBLOCK: - tid->sig_mask &= ~*set; - break; - case SIG_SETMASK: - tid->sig_mask = *set; - break; - default: - break; - } - } - rt_hw_interrupt_enable(level); + if (set) + { + switch(how) + { + case SIG_BLOCK: + tid->sig_mask |= *set; + break; + case SIG_UNBLOCK: + tid->sig_mask &= ~*set; + break; + case SIG_SETMASK: + tid->sig_mask = *set; + break; + default: + break; + } + } + rt_hw_interrupt_enable(level); return 0; } @@ -70,18 +70,18 @@ int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { rt_sighandler_t old = RT_NULL; - if (!sig_valid(signum)) return -RT_ERROR; + if (!sig_valid(signum)) return -RT_ERROR; - if (act) - old = rt_signal_install(signum, act->sa_handler); - else - { - old = rt_signal_install(signum, RT_NULL); - rt_signal_install(signum, old); - } + if (act) + old = rt_signal_install(signum, act->sa_handler); + else + { + old = rt_signal_install(signum, RT_NULL); + rt_signal_install(signum, old); + } - if (oldact) - oldact->sa_handler = old; + if (oldact) + oldact->sa_handler = old; return 0; } @@ -89,7 +89,22 @@ int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) int sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *timeout) { - return 0; + int ret = 0; + int tick = RT_WAITING_FOREVER; + +#ifdef RT_USING_PTHREADS + if (timeout) + { + extern int clock_time_to_tick(const struct timespec *time); + tick = clock_time_to_tick(timeout); + } +#endif + + ret = rt_signal_wait(set, info, tick); + if (ret == 0) return 0; + + errno = ret; + return -1; } int sigwait(const sigset_t *set, int *sig) @@ -104,12 +119,12 @@ int sigwait(const sigset_t *set, int *sig) int sigwaitinfo(const sigset_t *set, siginfo_t *info) { - return sigtimedwait(set, info, NULL); + return sigtimedwait(set, info, NULL); } int raise(int sig) { - rt_thread_kill(rt_thread_self(), sig); + rt_thread_kill(rt_thread_self(), sig); return 0; } diff --git a/components/libc/signal/posix_signal.h b/components/libc/signal/posix_signal.h index fbb33e7821..5ec721c921 100644 --- a/components/libc/signal/posix_signal.h +++ b/components/libc/signal/posix_signal.h @@ -30,6 +30,7 @@ extern "C" { #endif #include +#include enum rt_signal_value{ SIG1 = SIGHUP, @@ -68,56 +69,6 @@ enum rt_signal_value{ SIGMAX = NSIG, }; -/* -The structure definitions on newlib: - -typedef void (*_sig_func_ptr)(int); - -struct sigaction -{ - _sig_func_ptr sa_handler; - sigset_t sa_mask; - int sa_flags; -}; - -typedef int sig_atomic_t; - -typedef _sig_func_ptr sig_t; -typedef _sig_func_ptr sighandler_t; - -When enable POSIX_REALTIME_SIGNALS/POSIX_THREADS: - -union sigval { - int sival_int; - void *sival_ptr; -}; - -struct sigevent { - int sigev_notify; - int sigev_signo; - union sigval sigev_value; - - void (*sigev_notify_function)( union sigval ); - - pthread_attr_t *sigev_notify_attributes; - -}; - -typedef struct { - int si_signo; - int si_code; - union sigval si_value; -} siginfo_t; - -*/ - -rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler); -void rt_signal_mask(int signo); -void rt_signal_unmask(int signo); -int rt_thread_kill(rt_thread_t tid, int sig); - -int rt_system_signal_init(void); - #ifdef __cplusplus } #endif diff --git a/include/libc/libc_signal.h b/include/libc/libc_signal.h index cc4552f990..dabcad1a34 100644 --- a/include/libc/libc_signal.h +++ b/include/libc/libc_signal.h @@ -30,6 +30,7 @@ extern "C" { #endif +#ifndef HAVE_SYS_SIGNALS /* Signal Generation and Delivery, P1003.1b-1993, p. 63 NOTE: P1003.1c/D10, p. 34 adds sigev_notify_function and sigev_notify_attributes to the sigevent structure. */ @@ -59,14 +60,15 @@ struct siginfo union sigval si_value; }; typedef struct siginfo siginfo_t; +#endif -#define SI_USER 0x01 /* Signal sent by kill(). */ -#define SI_QUEUE 0x02 /* Signal sent by sigqueue(). */ -#define SI_TIMER 0x03 /* Signal generated by expiration of a +#define SI_USER 0x01 /* Signal sent by kill(). */ +#define SI_QUEUE 0x02 /* Signal sent by sigqueue(). */ +#define SI_TIMER 0x03 /* Signal generated by expiration of a timer set by timer_settime(). */ -#define SI_ASYNCIO 0x04 /* Signal generated by completion of an - asynchronous I/O request. */ -#define SI_MESGQ 0x05 /* Signal generated by arrival of a +#define SI_ASYNCIO 0x04 /* Signal generated by completion of an + asynchronous I/O request. */ +#define SI_MESGQ 0x05 /* Signal generated by arrival of a message on an empty message queue. */ #ifdef RT_USING_NEWLIB @@ -107,17 +109,17 @@ typedef unsigned long sigset_t; #define SIGRTMAX 31 #define NSIG 32 -#define SIG_SETMASK 0 /* set mask with sigprocmask() */ -#define SIG_BLOCK 1 /* set of signals to block */ -#define SIG_UNBLOCK 2 /* set of signals to, well, unblock */ +#define SIG_SETMASK 0 /* set mask with sigprocmask() */ +#define SIG_BLOCK 1 /* set of signals to block */ +#define SIG_UNBLOCK 2 /* set of signals to, well, unblock */ typedef void (*_sig_func_ptr)(int); struct sigaction { - _sig_func_ptr sa_handler; - sigset_t sa_mask; - int sa_flags; + _sig_func_ptr sa_handler; + sigset_t sa_mask; + int sa_flags; }; #define sigaddset(what,sig) (*(what) |= (1<<(sig)), 0) @@ -163,17 +165,17 @@ typedef unsigned long sigset_t; #define SIGRTMAX 31 #define NSIG 32 -#define SIG_SETMASK 0 /* set mask with sigprocmask() */ -#define SIG_BLOCK 1 /* set of signals to block */ -#define SIG_UNBLOCK 2 /* set of signals to, well, unblock */ +#define SIG_SETMASK 0 /* set mask with sigprocmask() */ +#define SIG_BLOCK 1 /* set of signals to block */ +#define SIG_UNBLOCK 2 /* set of signals to, well, unblock */ typedef void (*_sig_func_ptr)(int); struct sigaction { - _sig_func_ptr sa_handler; - sigset_t sa_mask; - int sa_flags; + _sig_func_ptr sa_handler; + sigset_t sa_mask; + int sa_flags; }; #define sigaddset(what,sig) (*(what) |= (1<<(sig)), 0) diff --git a/include/rtdef.h b/include/rtdef.h index f2da3ac7bc..2404f7a364 100644 --- a/include/rtdef.h +++ b/include/rtdef.h @@ -451,8 +451,10 @@ typedef struct rt_timer *rt_timer_t; * @addtogroup Signal */ #ifdef RT_USING_SIGNALS +#include typedef unsigned long rt_sigset_t; typedef void (*rt_sighandler_t)(int signo); +typedef siginfo_t rt_siginfo_t; #define RT_SIG_MAX 32 #endif @@ -481,7 +483,7 @@ typedef void (*rt_sighandler_t)(int signo); #define RT_THREAD_STAT_SIGNAL 0x10 #define RT_THREAD_STAT_SIGNAL_READY (RT_THREAD_STAT_SIGNAL | RT_THREAD_READY) -#define RT_THREAD_STAT_SIGNAL_SUSPEND 0x20 +#define RT_THREAD_STAT_SIGNAL_WAIT 0x20 #define RT_THREAD_STAT_SIGNAL_MASK 0xf0 /** diff --git a/include/rtthread.h b/include/rtthread.h index 61cd82d97e..c92f7f012f 100644 --- a/include/rtthread.h +++ b/include/rtthread.h @@ -204,6 +204,7 @@ void rt_scheduler_sethook(void (*hook)(rt_thread_t from, rt_thread_t to)); void rt_signal_mask(int signo); void rt_signal_unmask(int signo); rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler); +int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout); int rt_system_signal_init(void); #endif diff --git a/src/scheduler.c b/src/scheduler.c index fa9348cd25..1be1816bc2 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -263,7 +263,7 @@ void rt_schedule(void) rt_hw_interrupt_enable(level); } } - else + else { /* enable interrupt */ rt_hw_interrupt_enable(level); @@ -293,7 +293,7 @@ void rt_schedule_insert_thread(struct rt_thread *thread) temp = rt_hw_interrupt_disable(); /* change stat */ - thread->stat = RT_THREAD_READY; + thread->stat = RT_THREAD_READY | (thread->stat & ~RT_THREAD_STAT_MASK); /* insert thread to ready list */ rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]), diff --git a/src/signal.c b/src/signal.c index 53d44bdcd4..68b3836a85 100644 --- a/src/signal.c +++ b/src/signal.c @@ -80,6 +80,16 @@ static void _signal_entry(void *parameter) 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; @@ -88,7 +98,7 @@ static void _signal_deliver(rt_thread_t tid) if (!(tid->sig_pending & tid->sig_mask)) return; level = rt_hw_interrupt_disable(); - if (tid->stat == RT_THREAD_SUSPEND) + if ((tid->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND) { /* resume thread to handle signal */ rt_thread_resume(tid); @@ -106,14 +116,17 @@ static void _signal_deliver(rt_thread_t tid) { /* 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_SIGNAL)) + 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, @@ -191,6 +204,105 @@ void rt_signal_unmask(int signo) } } +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; @@ -199,39 +311,46 @@ void rt_thread_handle_sig(rt_bool_t clean_state) struct siginfo_node *si_node; level = rt_hw_interrupt_disable(); - while (tid->sig_pending & tid->sig_mask) + if (tid->sig_pending & tid->sig_mask) { - int signo, error; - rt_sighandler_t handler; + /* 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; + 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); + /* 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); + 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); + 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 = si_node->si.si_errno; + level = rt_hw_interrupt_disable(); + tid->sig_pending &= ~sig_mask(signo); + error = si_node->si.si_errno; - rt_mp_free(si_node); /* release this siginfo node */ - /* set errno in thread tcb */ - tid->error = error; + 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; + } } - /* whether clean signal status */ - if (clean_state == RT_TRUE) tid->stat &= ~RT_THREAD_STAT_SIGNAL; - rt_hw_interrupt_enable(level); } @@ -315,7 +434,7 @@ int rt_thread_kill(rt_thread_t tid, int sig) node = (struct rt_slist_node *)tid->si_list; rt_hw_interrupt_enable(level); - /* update sig infor */ + /* update sig info */ rt_enter_critical(); for (; (node) != RT_NULL; node = node->next) { @@ -358,7 +477,7 @@ int rt_thread_kill(rt_thread_t tid, int sig) } else { - dbg_log(DBG_ERROR, "The allocation of signal infor node failed.\n"); + dbg_log(DBG_ERROR, "The allocation of signal info node failed.\n"); } /* deliver signal to this thread */ @@ -380,4 +499,3 @@ int rt_system_signal_init(void) } #endif - diff --git a/src/thread.c b/src/thread.c index 3a3e55302e..ea0ba0bfc9 100644 --- a/src/thread.c +++ b/src/thread.c @@ -638,7 +638,7 @@ rt_err_t rt_thread_suspend(rt_thread_t thread) if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_READY) { - RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: thread disorder, %d\n", + RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: thread disorder, 0x%2x\n", thread->stat)); return -RT_ERROR; @@ -648,7 +648,7 @@ rt_err_t rt_thread_suspend(rt_thread_t thread) temp = rt_hw_interrupt_disable(); /* change thread stat */ - thread->stat = RT_THREAD_SUSPEND; + thread->stat = RT_THREAD_SUSPEND | (thread->stat & ~RT_THREAD_STAT_MASK); rt_schedule_remove_thread(thread); /* stop thread timer anyway */ diff --git a/src/timer.c b/src/timer.c index c56a453a3c..d6cf486931 100644 --- a/src/timer.c +++ b/src/timer.c @@ -396,7 +396,7 @@ rt_err_t rt_timer_start(rt_timer_t timer) if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) { /* check whether timer thread is ready */ - if (timer_thread.stat != RT_THREAD_READY) + if ((timer_thread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_READY) { /* resume timer thread to check soft timer */ rt_thread_resume(&timer_thread);