sched/signal: Add logic to wake up a thread that is waiting on a signal if it is canceled.

This commit is contained in:
Gregory Nutt 2017-10-12 08:55:19 -06:00
parent d0102bdd4c
commit fd58bfd9c0
3 changed files with 97 additions and 11 deletions

View File

@ -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 */

View File

@ -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);

View File

@ -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);
}