From fd58bfd9c0a4068aafa9edfec40d12ff9a80a416 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 12 Oct 2017 08:55:19 -0600 Subject: [PATCH] sched/signal: Add logic to wake up a thread that is waiting on a signal if it is canceled. --- sched/signal/sig_timedwait.c | 84 ++++++++++++++++++++++++++++++++---- sched/signal/signal.h | 6 +++ sched/task/task_cancelpt.c | 18 ++++++-- 3 files changed, 97 insertions(+), 11 deletions(-) diff --git a/sched/signal/sig_timedwait.c b/sched/signal/sig_timedwait.c index 5699db8414e..d8d50b6f5f7 100644 --- a/sched/signal/sig_timedwait.c +++ b/sched/signal/sig_timedwait.c @@ -63,11 +63,13 @@ * Pre-processor Definitions ****************************************************************************/ -/* This is a special value of si_signo that means that it was the timeout - * that awakened the wait... not the receipt of a signal. +/* These are special values of si_signo that mean that either the wait was + * awakened with a timeout, or the wait was canceled... not the receipt of a + * signal. */ -#define SIG_WAIT_TIMEOUT 0xff +#define SIG_CANCEL_TIMEOUT 0xfe +#define SIG_WAIT_TIMEOUT 0xff /**************************************************************************** * Private Functions @@ -104,7 +106,7 @@ static void nxsig_timeout(int argc, wdparm_t itcb) } u; u.itcb = itcb; - ASSERT(u.wtcb); + DEBUGASSERT(u.wtcb); #ifdef CONFIG_SMP /* We must be in a critical section in order to call up_unblock_task() @@ -145,6 +147,56 @@ static void nxsig_timeout(int argc, wdparm_t itcb) * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: nxsig_wait_irq + * + * Description: + * An error event has occurred and the signal wait must be terminated with + * an error. + * + ****************************************************************************/ + +#ifdef CONFIG_CANCELLATION_POINTS +void nxsig_wait_irq(FAR struct tcb_s *wtcb, int errcode) +{ +#ifdef CONFIG_SMP + irqstate_t flags; + + /* We must be in a critical section in order to call up_unblock_task() + * below. If we are running on a single CPU architecture, then we know + * interrupts a disabled an there is no need to explicitly call + * enter_critical_section(). However, in the SMP case, + * enter_critical_section() does much more than just disable interrupts on + * the local CPU; it also manages spinlocks to assure the stability of the + * TCB that we are manipulating. + */ + + flags = enter_critical_section(); +#endif + + /* There may be a race condition -- make sure the task is + * still waiting for a signal + */ + + if (wtcb->task_state == TSTATE_WAIT_SIG) + { + wtcb->sigunbinfo.si_signo = SIG_CANCEL_TIMEOUT; + wtcb->sigunbinfo.si_code = SI_USER; + wtcb->sigunbinfo.si_errno = errcode; + wtcb->sigunbinfo.si_value.sival_int = 0; +#ifdef CONFIG_SCHED_HAVE_PARENT + wtcb->sigunbinfo.si_pid = 0; /* Not applicable */ + wtcb->sigunbinfo.si_status = OK; +#endif + up_unblock_task(wtcb); + } + +#ifdef CONFIG_SMP + leave_critical_section(flags); +#endif +} +#endif /* CONFIG_CANCELLATION_POINTS */ + /**************************************************************************** * Name: nxsig_timedwait * @@ -345,12 +397,28 @@ int nxsig_timedwait(FAR const sigset_t *set, FAR struct siginfo *info, } else { - /* Otherwise, we must have been awakened by the timeout. Set EGAIN - * and return an error. + /* Otherwise, we must have been awakened by the timeout or, + * perhaps, the wait was cancelled. */ - DEBUGASSERT(rtcb->sigunbinfo.si_signo == SIG_WAIT_TIMEOUT); - ret = -EAGAIN; +#ifdef CONFIG_CANCELLATION_POINTS + if (rtcb->sigunbinfo.si_signo == SIG_CANCEL_TIMEOUT) + { + /* The wait was canceled */ + + ret = -rtcb->sigunbinfo.si_errno; + DEBUGASSERT(ret < 0); + } + else +#endif + { + /* We were awakened by a timeout. Set EAGAIN and return an + * error. + */ + + DEBUGASSERT(rtcb->sigunbinfo.si_signo == SIG_WAIT_TIMEOUT); + ret = -EAGAIN; + } } /* Return the signal info to the caller if so requested */ diff --git a/sched/signal/signal.h b/sched/signal/signal.h index b8bad69c3fd..7d9d678c24f 100644 --- a/sched/signal/signal.h +++ b/sched/signal/signal.h @@ -183,6 +183,12 @@ int nxsig_dispatch(pid_t pid, FAR siginfo_t *info); void nxsig_cleanup(FAR struct tcb_s *stcb); void nxsig_release(FAR struct task_group_s *group); +/* sig_timedwait.c */ + +#ifdef CONFIG_CANCELLATION_POINTS +void nxsig_wait_irq(FAR struct tcb_s *wtcb, int errcode); +#endif + /* In files of the same name */ FAR sigq_t *nxsig_alloc_pendingsigaction(void); diff --git a/sched/task/task_cancelpt.c b/sched/task/task_cancelpt.c index a08e5d82662..bcd03621a90 100644 --- a/sched/task/task_cancelpt.c +++ b/sched/task/task_cancelpt.c @@ -75,6 +75,7 @@ #include "sched/sched.h" #include "semaphore/semaphore.h" +#include "signal/signal.h" #include "mqueue/mqueue.h" #include "task/task.h" @@ -358,13 +359,24 @@ void notify_cancellation(FAR struct tcb_s *tcb) nxsem_wait_irq(tcb, ECANCELED); } +#ifndef CONFIG_DISABLE_SIGNALS + /* If the thread is blocked waiting on a signal, then the + * thread must be unblocked to handle the cancellation. + */ + + else if (tcb->task_state == TSTATE_WAIT_SIG) + { + nxsig_wait_irq(tcb, ECANCELED); + } +#endif + +#ifndef CONFIG_DISABLE_MQUEUE /* If the thread is blocked waiting on a message queue, then the * thread must be unblocked to handle the cancellation. */ -#ifndef CONFIG_DISABLE_MQUEUE - if (tcb->task_state == TSTATE_WAIT_MQNOTEMPTY || - tcb->task_state == TSTATE_WAIT_MQNOTFULL) + else if (tcb->task_state == TSTATE_WAIT_MQNOTEMPTY || + tcb->task_state == TSTATE_WAIT_MQNOTFULL) { nxmq_wait_irq(tcb, ECANCELED); }