In POSIX, there are two pthread cancellation types:
PTHREAD_CANCEL_DEFERRED is the default and the normal kind of pthread cancellation that should be used.
PTHREAD_CANCEL_ASYNCHRONOUS is brutal; it simply kills the pthread immediately without regard for what it is doing.
PTHREAD_CANCEL_DEFERRED works differently; nothing happens immediately. Instead, the cancellation will be deferred until the pthread is at (or a better preposition would be within) a cancellation point. You cannot have the
PTHREAD_CANCEL_DEFERRED cancellation type without cancellation points and you will not have cancellation points unless you enable them with
Cancellation points add special instrumentation to a specific set of interface functions. Those interface functions are listed here: OpenGroup.org.
In NuttX, each of these functions
CONFIG_CANCELLATION_POINTS=y will enable a special call to
enter_cancellation_point() at the beginning of the function and a matching call to
leave_cancellation_point() before the interface function returns. These may be nested: One cancellation point function may call another which may call another, etc. The
select() function, for example, calls
poll() which calls
sem_wait() so the nesting will be three deep.
These two cancellation point hooks behave in a complementary way.
enter_cancellation_point() increments the nesting level and
leave_cancellation_point() decrements the nesting level. No action is taken unless the nesting level is zero then, in that case, if there is a pending cancellation then the thread exists normally by simply calling
pthread_cancel() is called, it will check if the deferred cancellation mode is enabled for the target pthread. If so it will (1) mark the cancellation as pending, and (2) if the thread is waiting on a semaphore or message queue (and is in a cancelable state), it will wake it just in the same was as if the thread received a single (except with
ECANCELED instead of
If, on the other hand, the asynchronous mode is enabled (and the thread is in a cancelable state),
pthread_cancel() will, instead, kill the thread immediately.
NOTE: For pthreads, the cancelable state is controlled by the interface
pthread_setcancelstate(). This allows the pthread to disable or re-enable cancelability. If the thread is not cancelable when
pthread_cancel() is called, it will never do more than just mark the cancellation as pending. When the cancelable state is re-enabled, then any pending cancellation will take effect
Let's walk through the case of
select() to see how would work:
select()is called, it allocates some memory that it needs to handle the conversion to
- It then calls
poll()which sets up the poll with all of the relevant drivers and, eventually, calls
So this pthread will spend almost all of its time waiting in sem_wait() and that is the most likely state of the pthread when pthread_cancel() is called with deferred cancellation eanbled.
pthread_cancel()is called, it will set the pending cancellation state and wake up
sem_wait()will see the pending cancellation, but will do nothing because the nesting level is decremented only to two. It will return to
ECANCELEDerror. NOTE that the semaphore is left in a healthy state.
poll()will tear-down down the poll from all of the drivers and call
leave_cancellation_point()before it exits. Again, it will see the pending cancellation but will do nothing because the nesting level decrements only to 1. NOTE that since
poll()does the complete tear=down and all of the drivers are left in a healthy state.
select()cleans up all of its memory allocations and calls
leave_cancellation_point(). This time, the nesting level decrements to zero and
pthread_exit()with everything in a proper state.
If asynchronous pthread cancellation were selected, then the behavior would be very different. The first two steps would be the same but then:
pthread_cancel()is called, the thread would be immediately killed. The semaphore would be left in the locked state; the memory allocated by
select()would be stranded; drivers would be left with stale pointers to stranded memory. Not a good state to leave the system!
select() will not return to the caller in either case.
The following pthread application interfaces are available to manage cancellation:
pthread_cancel()cancels a thread.
pthread_setcancelstate()can be used to enable or disable a cancellation.
pthread_setcanceltype()can be used to switched between asynchronous and deferred thread cancellation.
pthread_testcancel()can be used to force a cancellation point.
NuttX also supports some non-standard interfaces for task deletion (i.e., cancellation) of tasks as well. The cancellation point logic is identical and the task application interfaces are very similar:
task_delete()deletes (cancels) a task.
task_setcancelstate()can be used to enable or disable a cancellation.
task_setcanceltype()can be used to switched between asynchronous and deferred task cancellation.
task_testcancel()can be used to force a cancellation point.