From c9ca97b4b531dbd9adf1e4ad6a9c09acb5763ded Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Fri, 9 Dec 2016 12:01:18 -0600 Subject: [PATCH] cancellation points are basically function. More tested is needed and additional cancellation points must be implemented before this can be merged back to master. --- fs/vfs/fs_poll.c | 6 ++++++ fs/vfs/fs_select.c | 6 ++++++ sched/pthread/pthread_cancel.c | 30 ++++++++++++++++++++++++++++++ sched/pthread/pthread_exit.c | 11 +++++++++++ sched/sched/sched_waitid.c | 1 + sched/semaphore/sem_wait.c | 1 + sched/task/task_cancelpt.c | 8 ++++++-- sched/task/task_delete.c | 32 +++++++++++++++++++++++++++++++- sched/task/task_exithook.c | 11 +++++++++++ 9 files changed, 103 insertions(+), 3 deletions(-) diff --git a/fs/vfs/fs_poll.c b/fs/vfs/fs_poll.c index e2c66abf7b..bccd7d4f14 100644 --- a/fs/vfs/fs_poll.c +++ b/fs/vfs/fs_poll.c @@ -48,6 +48,7 @@ #include #include +#include #include #include @@ -365,6 +366,10 @@ int poll(FAR struct pollfd *fds, nfds_t nfds, int timeout) int errcode; int ret; + /* poll() is a cancellation point */ + + enter_cancellation_point(); + /* This semaphore is used for signaling and, hence, should not have * priority inheritance enabled. */ @@ -425,6 +430,7 @@ int poll(FAR struct pollfd *fds, nfds_t nfds, int timeout) } sem_destroy(&sem); + leave_cancellation_point(); /* Check for errors */ diff --git a/fs/vfs/fs_select.c b/fs/vfs/fs_select.c index d71d8e28a1..8a9cd9ae60 100644 --- a/fs/vfs/fs_select.c +++ b/fs/vfs/fs_select.c @@ -110,6 +110,10 @@ int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds, int ndx; int ret; + /* select() is cancellation point */ + + enter_cancellation_point(); + /* How many pollfd structures do we need to allocate? */ /* Initialize the descriptor list for poll() */ @@ -134,6 +138,7 @@ int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds, if (!pollset) { set_errno(ENOMEM); + leave_cancellation_point(); return ERROR; } @@ -280,6 +285,7 @@ int select(int nfds, FAR fd_set *readfds, FAR fd_set *writefds, set_errno(errcode); } + leave_cancellation_point(); return ret; } diff --git a/sched/pthread/pthread_cancel.c b/sched/pthread/pthread_cancel.c index fcaba69079..5b6fa04db8 100644 --- a/sched/pthread/pthread_cancel.c +++ b/sched/pthread/pthread_cancel.c @@ -45,6 +45,7 @@ #include #include "sched/sched.h" +#include "task/task.h" #include "pthread/pthread.h" /**************************************************************************** @@ -104,6 +105,35 @@ int pthread_cancel(pthread_t thread) return OK; } +#ifdef CONFIG_CANCELLATION_POINTS + /* Check if this thread supports deferred cancellation */ + + if ((tcb->cmn.flags & TCB_FLAG_CANCEL_DEFERRED) != 0) + { + /* Then we cannot cancel the thread asynchronoulsy. Mark the cancellation + * as pending. + */ + + tcb->cmn.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. + * + * REVISIT: is locking the scheduler sufficent in SMP mode? + */ + + if (tcb->cmn.cpcount > 0) + { + notify_cancellation(&tcb->cmn); + } + + 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 diff --git a/sched/pthread/pthread_exit.c b/sched/pthread/pthread_exit.c index 545148a4eb..40a5b7fb0d 100644 --- a/sched/pthread/pthread_exit.c +++ b/sched/pthread/pthread_exit.c @@ -94,6 +94,17 @@ void pthread_exit(FAR void *exit_value) } #endif +#ifdef CONFIG_CANCELLATION_POINTS + /* Mark the pthread as non-cancelable to avoid additional calls to + * pthread_exit() due to any cancellation point logic that might get + * kicked off by actions taken during pthread_exit processing. + */ + + tcb->flags |= TCB_FLAG_NONCANCELABLE; + tcb->flags &= ~TCB_FLAG_CANCEL_PENDING; + tcb->cpcount = 0; +#endif + #ifdef CONFIG_PTHREAD_CLEANUP /* Perform any stack pthread clean-up callbacks */ diff --git a/sched/sched/sched_waitid.c b/sched/sched/sched_waitid.c index 3fb3a329a2..439fe0b8ac 100644 --- a/sched/sched/sched_waitid.c +++ b/sched/sched/sched_waitid.c @@ -44,6 +44,7 @@ #include #include +#include #include "sched/sched.h" #include "group/group.h" diff --git a/sched/semaphore/sem_wait.c b/sched/semaphore/sem_wait.c index d0b6c8661a..8bfe95254b 100644 --- a/sched/semaphore/sem_wait.c +++ b/sched/semaphore/sem_wait.c @@ -46,6 +46,7 @@ #include #include +#include #include "sched/sched.h" #include "semaphore/semaphore.h" diff --git a/sched/task/task_cancelpt.c b/sched/task/task_cancelpt.c index 162bbc139c..fcf6745c65 100644 --- a/sched/task/task_cancelpt.c +++ b/sched/task/task_cancelpt.c @@ -108,6 +108,8 @@ void enter_cancellation_point(void) /* Disabling pre-emption should provide sufficient protection. We only * need the TCB to be stationary (no interrupt level modification is * anticipated). + * + * REVISIT: is locking the scheduler sufficent in SMP mode? */ sched_lock(); @@ -135,7 +137,7 @@ void enter_cancellation_point(void) #ifndef CONFIG_DISABLE_PTHREAD if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD) { - pthread_exit(NULL); + pthread_exit(PTHREAD_CANCELED); } else #endif @@ -180,6 +182,8 @@ void leave_cancellation_point(void) /* Disabling pre-emption should provide sufficient protection. We only * need the TCB to be stationary (no interrupt level modification is * anticipated). + * + * REVISIT: is locking the scheduler sufficent in SMP mode? */ sched_lock(); @@ -211,7 +215,7 @@ void leave_cancellation_point(void) #ifndef CONFIG_DISABLE_PTHREAD if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD) { - pthread_exit(NULL); + pthread_exit(PTHREAD_CANCELED); } else #endif diff --git a/sched/task/task_delete.c b/sched/task/task_delete.c index 5b4c0f747d..50746f2387 100644 --- a/sched/task/task_delete.c +++ b/sched/task/task_delete.c @@ -103,7 +103,37 @@ int task_delete(pid_t pid) exit(EXIT_SUCCESS); } - /* Then let task_terminate do the heavy lifting */ +#ifdef CONFIG_CANCELLATION_POINTS + /* Check if this task supports deferred cancellation */ + + sched_lock(); + if ((rtcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0) + { + /* Then we cannot cancel the task asynchronoulsy. 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. + * + * REVISIT: is locking the scheduler sufficent in SMP mode? + */ + + if (rtcb->cpcount > 0) + { + notify_cancellation(rtcb); + } + + sched_unlock(); + return OK; + } +#endif + + /* Otherwise, perform the asynchronous cancellation, letting + * task_terminate() do all of the heavy lifting. + */ return task_terminate(pid, false); } diff --git a/sched/task/task_exithook.c b/sched/task/task_exithook.c index 96856d8792..e384238365 100644 --- a/sched/task/task_exithook.c +++ b/sched/task/task_exithook.c @@ -612,6 +612,17 @@ void task_exithook(FAR struct tcb_s *tcb, int status, bool nonblocking) return; } +#ifdef CONFIG_CANCELLATION_POINTS + /* Mark the task as non-cancelable to avoid additional calls to exit() + * due to any cancellation point logic that might get kicked off by + * actions taken during exit processing. + */ + + tcb->flags |= TCB_FLAG_NONCANCELABLE; + tcb->flags &= ~TCB_FLAG_CANCEL_PENDING; + tcb->cpcount = 0; +#endif + #if defined(CONFIG_SCHED_ATEXIT) || defined(CONFIG_SCHED_ONEXIT) /* If exit function(s) were registered, call them now before we do any un- * initialization.