From 4d634b9006760555c48ce70375190dc57fb594e2 Mon Sep 17 00:00:00 2001 From: Xiang Xiao Date: Sun, 7 Jun 2020 00:41:47 +0800 Subject: [PATCH] sched: Consolidate the cancellation notification logic to avoid the code duplication Signed-off-by: Xiang Xiao Change-Id: Ie2ba55c382eb3eb7c8d9f04bba1b9e294aaf6196 --- sched/pthread/pthread_cancel.c | 50 +------------- sched/signal/sig_default.c | 53 +-------------- sched/task/Make.defs | 6 +- sched/task/task.h | 4 +- sched/task/task_cancelpt.c | 116 ++++++++++++++++++++++----------- sched/task/task_delete.c | 50 +------------- 6 files changed, 86 insertions(+), 193 deletions(-) diff --git a/sched/pthread/pthread_cancel.c b/sched/pthread/pthread_cancel.c index 02b8087dd3..dcc58f6390 100644 --- a/sched/pthread/pthread_cancel.c +++ b/sched/pthread/pthread_cancel.c @@ -67,61 +67,15 @@ int pthread_cancel(pthread_t thread) DEBUGASSERT((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD); - /* Check to see if this thread has the non-cancelable bit set in its - * flags. Suppress context changes for a bit so that the flags are stable. - * (the flags should not change in interrupt handling). - */ + /* Notify the target if the non-cancelable or deferred cancellation set */ - sched_lock(); - if ((tcb->flags & TCB_FLAG_NONCANCELABLE) != 0) + if (nxnotify_cancellation(tcb)) { - /* Then we cannot cancel the thread now. Here is how this is - * supposed to work: - * - * "When cancellability is disabled, all cancels are held pending - * in the target thread until the thread changes the cancellability. - * When cancellability is deferred, all cancels are held pending in - * the target thread until the thread changes the cancellability, - * calls a function which is a cancellation point or calls - * pthread_testcancel(), thus creating a cancellation point. When - * cancellability is asynchronous, all cancels are acted upon - * immediately, interrupting the thread with its processing." - */ - - tcb->flags |= TCB_FLAG_CANCEL_PENDING; - sched_unlock(); return OK; } -#ifdef CONFIG_CANCELLATION_POINTS - /* Check if this thread supports deferred cancellation */ - - if ((tcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0) - { - /* Then we cannot cancel the thread asynchronously. Mark the - * cancellation as pending. - */ - - tcb->flags |= TCB_FLAG_CANCEL_PENDING; - - /* If the thread is waiting at a cancellation point, then notify of the - * cancellation thereby waking the task up with an ECANCELED error. - */ - - if (tcb->cpcount > 0) - { - nxnotify_cancellation(tcb); - } - - sched_unlock(); - return OK; - } -#endif - /* Otherwise, perform the asyncrhonous cancellation */ - sched_unlock(); - /* Check to see if the ID refers to ourselves.. this would be the * same as pthread_exit(PTHREAD_CANCELED). */ diff --git a/sched/signal/sig_default.c b/sched/signal/sig_default.c index 28bad31b08..70a2238fb9 100644 --- a/sched/signal/sig_default.c +++ b/sched/signal/sig_default.c @@ -207,62 +207,13 @@ static void nxsig_abnormal_termination(int signo) { FAR struct tcb_s *rtcb = (FAR struct tcb_s *)this_task(); - /* Check to see if this task has the non-cancelable bit set in its - * flags. Suppress context changes for a bit so that the flags are stable. - * (the flags should not change in interrupt handling). - */ + /* Notify the target if the non-cancelable or deferred cancellation set */ - sched_lock(); - if ((rtcb->flags & TCB_FLAG_NONCANCELABLE) != 0) + if (nxnotify_cancellation(rtcb)) { - /* Then we cannot cancel the thread now. Here is how this is - * supposed to work: - * - * "When cancellability is disabled, all cancels are held pending - * in the target thread until the thread changes the cancellability. - * When cancellability is deferred, all cancels are held pending in - * the target thread until the thread changes the cancellability, - * calls a function which is a cancellation point or calls - * pthread_testcancel(), thus creating a cancellation point. When - * cancellability is asynchronous, all cancels are acted upon - * immediately, interrupting the thread with its processing." - * - * REVISIT: Does this rule apply to equally to both SIGKILL and - * SIGINT? - */ - - rtcb->flags |= TCB_FLAG_CANCEL_PENDING; - sched_unlock(); return; } -#ifdef CONFIG_CANCELLATION_POINTS - /* Check if this task supports deferred cancellation */ - - if ((rtcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0) - { - /* Then we cannot cancel the task asynchronously. - * Mark the cancellation as pending. - */ - - rtcb->flags |= TCB_FLAG_CANCEL_PENDING; - - /* If the task is waiting at a cancellation point, then notify of the - * cancellation thereby waking the task up with an ECANCELED error. - */ - - if (rtcb->cpcount > 0) - { - nxnotify_cancellation(rtcb); - } - - sched_unlock(); - return; - } -#endif - - sched_unlock(); - /* Careful: In the multi-threaded task, the signal may be handled on a * child pthread. */ diff --git a/sched/task/Make.defs b/sched/task/Make.defs index a5c2295692..1fa00c7834 100644 --- a/sched/task/Make.defs +++ b/sched/task/Make.defs @@ -37,7 +37,7 @@ CSRCS += task_create.c task_init.c task_setup.c task_activate.c CSRCS += task_start.c task_delete.c task_exit.c task_exithook.c CSRCS += task_getgroup.c task_getpid.c task_prctl.c task_recover.c CSRCS += task_restart.c task_spawnparms.c task_setcancelstate.c -CSRCS += task_terminate.c exit.c +CSRCS += task_cancelpt.c task_terminate.c exit.c ifeq ($(CONFIG_ARCH_HAVE_VFORK),y) ifeq ($(CONFIG_SCHED_WAITPID),y) @@ -71,10 +71,6 @@ ifeq ($(CONFIG_SCHED_ONEXIT),y) CSRCS += task_onexit.c endif -ifeq ($(CONFIG_CANCELLATION_POINTS),y) -CSRCS += task_cancelpt.c -endif - # Include task build support DEPPATH += --dep-path task diff --git a/sched/task/task.h b/sched/task/task.h index 80279a7065..3de2d42f24 100644 --- a/sched/task/task.h +++ b/sched/task/task.h @@ -71,8 +71,6 @@ void nxtask_recover(FAR struct tcb_s *tcb); /* Cancellation points */ -#ifdef CONFIG_CANCELLATION_POINTS -void nxnotify_cancellation(FAR struct tcb_s *tcb); -#endif +bool nxnotify_cancellation(FAR struct tcb_s *tcb); #endif /* __SCHED_TASK_TASK_H */ diff --git a/sched/task/task_cancelpt.c b/sched/task/task_cancelpt.c index 988e9bf8aa..cad59dfa08 100644 --- a/sched/task/task_cancelpt.c +++ b/sched/task/task_cancelpt.c @@ -316,6 +316,8 @@ bool check_cancellation_point(void) return ret; } +#endif /* CONFIG_CANCELLATION_POINTS */ + /**************************************************************************** * Name: nxnotify_cancellation * @@ -327,9 +329,12 @@ bool check_cancellation_point(void) * leave_cancellation_point() would then follow, causing the thread to * exit. * + * Returned Value: + * Indicate whether the notification delivery to the target + * ****************************************************************************/ -void nxnotify_cancellation(FAR struct tcb_s *tcb) +bool nxnotify_cancellation(FAR struct tcb_s *tcb) { irqstate_t flags; @@ -339,51 +344,86 @@ void nxnotify_cancellation(FAR struct tcb_s *tcb) flags = enter_critical_section(); - /* Make sure that the cancellation pending indication is set. */ - - tcb->flags |= TCB_FLAG_CANCEL_PENDING; - /* We only notify the cancellation if (1) the thread has not disabled * cancellation, (2) the thread uses the deferred cancellation mode, * (3) the thread is waiting within a cancellation point. */ - if (((tcb->flags & TCB_FLAG_NONCANCELABLE) == 0 && - (tcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0) || - tcb->cpcount > 0) + /* Check to see if this task has the non-cancelable bit set. */ + + if ((tcb->flags & TCB_FLAG_NONCANCELABLE) != 0) { - /* If the thread is blocked waiting for a semaphore, then the thread - * must be unblocked to handle the cancellation. + /* Then we cannot cancel the thread now. Here is how this is + * supposed to work: + * + * "When cancellability is disabled, all cancels are held pending + * in the target thread until the thread changes the cancellability. + * When cancellability is deferred, all cancels are held pending in + * the target thread until the thread changes the cancellability, + * calls a function which is a cancellation point or calls + * pthread_testcancel(), thus creating a cancellation point. When + * cancellability is asynchronous, all cancels are acted upon + * immediately, interrupting the thread with its processing." */ - if (tcb->task_state == TSTATE_WAIT_SEM) - { - nxsem_wait_irq(tcb, ECANCELED); - } - - /* 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); - } - -#ifndef CONFIG_DISABLE_MQUEUE - /* If the thread is blocked waiting on a message queue, then the - * thread must be unblocked to handle the cancellation. - */ - - else if (tcb->task_state == TSTATE_WAIT_MQNOTEMPTY || - tcb->task_state == TSTATE_WAIT_MQNOTFULL) - { - nxmq_wait_irq(tcb, ECANCELED); - } -#endif + tcb->flags |= TCB_FLAG_CANCEL_PENDING; + leave_critical_section(flags); + return true; } - leave_critical_section(flags); -} +#ifdef CONFIG_CANCELLATION_POINTS + /* Check if this task supports deferred cancellation */ -#endif /* CONFIG_CANCELLATION_POINTS */ + if ((tcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0) + { + /* Then we cannot cancel the task asynchronously. + * Mark the cancellation as pending. + */ + + tcb->flags |= TCB_FLAG_CANCEL_PENDING; + + /* If the task is waiting at a cancellation point, then notify of the + * cancellation thereby waking the task up with an ECANCELED error. + */ + + if (tcb->cpcount > 0) + { + /* If the thread is blocked waiting for a semaphore, then the + * thread must be unblocked to handle the cancellation. + */ + + if (tcb->task_state == TSTATE_WAIT_SEM) + { + nxsem_wait_irq(tcb, ECANCELED); + } + + /* 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); + } + +#ifndef CONFIG_DISABLE_MQUEUE + /* If the thread is blocked waiting on a message queue, then + * the thread must be unblocked to handle the cancellation. + */ + + else if (tcb->task_state == TSTATE_WAIT_MQNOTEMPTY || + tcb->task_state == TSTATE_WAIT_MQNOTFULL) + { + nxmq_wait_irq(tcb, ECANCELED); + } +#endif + } + + leave_critical_section(flags); + return true; + } +#endif + + leave_critical_section(flags); + return false; +} diff --git a/sched/task/task_delete.c b/sched/task/task_delete.c index 8a6fe5b8fd..eef94c1ed2 100644 --- a/sched/task/task_delete.c +++ b/sched/task/task_delete.c @@ -143,59 +143,13 @@ int task_delete(pid_t pid) exit(EXIT_SUCCESS); } - /* Check to see if this task has the non-cancelable bit set in its - * flags. Suppress context changes for a bit so that the flags are stable. - * (the flags should not change in interrupt handling). - */ + /* Notify the target if the non-cancelable or deferred cancellation set */ - sched_lock(); - if ((dtcb->flags & TCB_FLAG_NONCANCELABLE) != 0) + if (nxnotify_cancellation(dtcb)) { - /* Then we cannot cancel the thread now. Here is how this is - * supposed to work: - * - * "When cancellability is disabled, all cancels are held pending - * in the target thread until the thread changes the cancellability. - * When cancellability is deferred, all cancels are held pending in - * the target thread until the thread changes the cancellability, - * calls a function which is a cancellation point or calls - * pthread_testcancel(), thus creating a cancellation point. When - * cancellability is asynchronous, all cancels are acted upon - * immediately, interrupting the thread with its processing." - */ - - dtcb->flags |= TCB_FLAG_CANCEL_PENDING; - sched_unlock(); return OK; } -#ifdef CONFIG_CANCELLATION_POINTS - /* Check if this task supports deferred cancellation */ - - if ((dtcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0) - { - /* Then we cannot cancel the task asynchronously. Mark the - * cancellation as pending. - */ - - dtcb->flags |= TCB_FLAG_CANCEL_PENDING; - - /* If the task is waiting at a cancellation point, then notify of the - * cancellation thereby waking the task up with an ECANCELED error. - */ - - if (dtcb->cpcount > 0) - { - nxnotify_cancellation(dtcb); - } - - sched_unlock(); - return OK; - } -#endif - - sched_unlock(); - /* Otherwise, perform the asynchronous cancellation, letting * nxtask_terminate() do all of the heavy lifting. */