task_delete() now obeys all cancellation point semantics.
This commit is contained in:
parent
6997cda1b5
commit
698597a838
@ -13,7 +13,7 @@
|
|||||||
<h1><big><font color="#3c34ec"><i>NuttX Operating System<p>User's Manual</i></font></big></h1>
|
<h1><big><font color="#3c34ec"><i>NuttX Operating System<p>User's Manual</i></font></big></h1>
|
||||||
<p><small>by</small></p>
|
<p><small>by</small></p>
|
||||||
<p>Gregory Nutt<p>
|
<p>Gregory Nutt<p>
|
||||||
<p>Last Updated: December 9, 2016</p>
|
<p>Last Updated: December 10, 2016</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@ -469,8 +469,21 @@ int task_delete(pid_t pid);
|
|||||||
|
|
||||||
<p>
|
<p>
|
||||||
<b>Description:</b>
|
<b>Description:</b>
|
||||||
This function causes a specified task to cease to exist -- its stack and TCB will be deallocated.
|
This function causes a specified task to cease to exist.
|
||||||
This function is the companion to task_create().
|
Its stack and TCB will be deallocated.
|
||||||
|
This function is the companion to <code>task_create()</code>.
|
||||||
|
This is the version of the function exposed to the user;
|
||||||
|
it is simply a wrapper around the internal, <code>task_terminate()</code> function.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The logic in this function only deletes non-running tasks.
|
||||||
|
If the <code>pid</code> parameter refers to to the currently runing task, then processing is redirected to <code>exit()</code>.
|
||||||
|
This can only happen if a task calls <code>task_delete()</code> in order to delete itself.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This function obeys the semantics of pthread cancellation:
|
||||||
|
task deletion is deferred if cancellation is disabled or if deferred cancellation is supported (with cancellation points enabled).
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>Input Parameters:</b>
|
<b>Input Parameters:</b>
|
||||||
<ul>
|
<ul>
|
||||||
@ -485,7 +498,8 @@ int task_delete(pid_t pid);
|
|||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<code>OK</code>, or <code>ERROR</code> if the task cannot be deleted.
|
<code>OK</code>, or <code>ERROR</code> if the task cannot be deleted.
|
||||||
This function can fail if the provided pid does not correspond to a task (<a href="#ErrnoAccess"><code>errno</code></a> is not set).
|
The <a href="#ErrnoAccess"><code>errno</code></a> is set to indicate the nature of the failure.
|
||||||
|
This function can fail, for example, if the provided pid does not correspond to a currently executing task.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
|
@ -58,7 +58,7 @@ int pthread_cancel(pthread_t thread)
|
|||||||
|
|
||||||
/* First, make sure that the handle references a valid thread */
|
/* First, make sure that the handle references a valid thread */
|
||||||
|
|
||||||
if (!thread)
|
if (thread == 0)
|
||||||
{
|
{
|
||||||
/* pid == 0 is the IDLE task. Callers cannot cancel the
|
/* pid == 0 is the IDLE task. Callers cannot cancel the
|
||||||
* IDLE task.
|
* IDLE task.
|
||||||
@ -77,11 +77,13 @@ int pthread_cancel(pthread_t thread)
|
|||||||
return ESRCH;
|
return ESRCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Only pthreads should use this interface */
|
||||||
|
|
||||||
DEBUGASSERT((tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD);
|
DEBUGASSERT((tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD);
|
||||||
|
|
||||||
/* Check to see if this thread has the non-cancelable bit set in its
|
/* 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.
|
* flags. Suppress context changes for a bit so that the flags are stable.
|
||||||
* (the flags should not change in interrupt handling.
|
* (the flags should not change in interrupt handling).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
sched_lock();
|
sched_lock();
|
||||||
@ -110,7 +112,7 @@ int pthread_cancel(pthread_t thread)
|
|||||||
|
|
||||||
if ((tcb->cmn.flags & TCB_FLAG_CANCEL_DEFERRED) != 0)
|
if ((tcb->cmn.flags & TCB_FLAG_CANCEL_DEFERRED) != 0)
|
||||||
{
|
{
|
||||||
/* Then we cannot cancel the thread asynchronoulsy. Mark the cancellation
|
/* Then we cannot cancel the thread asynchronously. Mark the cancellation
|
||||||
* as pending.
|
* as pending.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -162,9 +164,7 @@ int pthread_cancel(pthread_t thread)
|
|||||||
|
|
||||||
(void)pthread_completejoin((pid_t)thread, PTHREAD_CANCELED);
|
(void)pthread_completejoin((pid_t)thread, PTHREAD_CANCELED);
|
||||||
|
|
||||||
/* Then let pthread_delete do the real work */
|
/* Then let task_terminate do the real work */
|
||||||
|
|
||||||
task_delete((pid_t)thread);
|
return task_terminate((pid_t)thread, false);
|
||||||
return OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* sched/task/task_delete.c
|
* sched/task/task_delete.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 2007-2009, 2011-2013 Gregory Nutt. All rights reserved.
|
* Copyright (C) 2007-2009, 2011-2013, 2016 Gregory Nutt. All rights reserved.
|
||||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@ -40,6 +40,7 @@
|
|||||||
#include <nuttx/config.h>
|
#include <nuttx/config.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#include <nuttx/sched.h>
|
#include <nuttx/sched.h>
|
||||||
|
|
||||||
@ -64,37 +65,111 @@
|
|||||||
* redirected to exit(). This can only happen if a task calls task_delete()
|
* redirected to exit(). This can only happen if a task calls task_delete()
|
||||||
* in order to delete itself.
|
* in order to delete itself.
|
||||||
*
|
*
|
||||||
* In fact, this function (and task_terminate) are the final functions
|
* This function obeys the semantics of pthread cancellation: task
|
||||||
* called all task termination sequences. task_delete may be called
|
* deletion is deferred if cancellation is disabled or if deferred
|
||||||
* from:
|
* cancellation is supported (with cancellation points enabled).
|
||||||
*
|
|
||||||
* - task_restart(),
|
|
||||||
* - pthread_cancel(),
|
|
||||||
* - and directly from user code.
|
|
||||||
*
|
|
||||||
* Other exit paths (exit(), _eixt(), and pthread_exit()) will go through
|
|
||||||
* task_terminate()
|
|
||||||
*
|
*
|
||||||
* Inputs:
|
* Inputs:
|
||||||
* pid - The task ID of the task to delete. A pid of zero
|
* pid - The task ID of the task to delete. A pid of zero
|
||||||
* signifies the calling task.
|
* signifies the calling task.
|
||||||
*
|
*
|
||||||
* Return Value:
|
* Return Value:
|
||||||
* OK on success; or ERROR on failure
|
* OK on success; or ERROR on failure with the errno variable set
|
||||||
*
|
* appropriately.
|
||||||
* This function can fail if the provided pid does not correspond to a
|
|
||||||
* task (errno is not set)
|
|
||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
int task_delete(pid_t pid)
|
int task_delete(pid_t pid)
|
||||||
{
|
{
|
||||||
|
FAR struct tcb_s *dtcb;
|
||||||
FAR struct tcb_s *rtcb;
|
FAR struct tcb_s *rtcb;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Check if the task to delete is the calling task: PID=0 means to delete
|
||||||
|
* the calling task. In this case, task_delete() is much like exit()
|
||||||
|
* except that it obeys the cancellation semantics.
|
||||||
|
*/
|
||||||
|
|
||||||
|
rtcb = this_task();
|
||||||
|
if (pid == 0)
|
||||||
|
{
|
||||||
|
pid = rtcb->pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the TCB of the task to be deleted */
|
||||||
|
|
||||||
|
dtcb = (FAR struct tcb_s *)sched_gettcb(pid);
|
||||||
|
if (dtcb == NULL)
|
||||||
|
{
|
||||||
|
/* The pid does not correspond to any known thread. The task
|
||||||
|
* has probably already exited.
|
||||||
|
*/
|
||||||
|
|
||||||
|
set_errno(ESRCH);
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only tasks and kernel threads should use this interface */
|
||||||
|
|
||||||
|
DEBUGASSERT((dtcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD);
|
||||||
|
|
||||||
|
/* 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).
|
||||||
|
*/
|
||||||
|
|
||||||
|
sched_lock();
|
||||||
|
if ((dtcb->flags & TCB_FLAG_NONCANCELABLE) != 0)
|
||||||
|
{
|
||||||
|
/* Then we cannot cancel the thread now. Here is how this is
|
||||||
|
* supposed to work:
|
||||||
|
*
|
||||||
|
* "When cancelability is disabled, all cancels are held pending
|
||||||
|
* in the target thread until the thread changes the cancelability.
|
||||||
|
* When cancelability is deferred, all cancels are held pending in
|
||||||
|
* the target thread until the thread changes the cancelability, calls
|
||||||
|
* a function which is a cancellation point or calls pthread_testcancel(),
|
||||||
|
* thus creating a cancellation point. When cancelability 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.
|
||||||
|
*
|
||||||
|
* REVISIT: is locking the scheduler sufficent in SMP mode?
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (dtcb->cpcount > 0)
|
||||||
|
{
|
||||||
|
notify_cancellation(dtcb);
|
||||||
|
}
|
||||||
|
|
||||||
|
sched_unlock();
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Check if the task to delete is the calling task */
|
/* Check if the task to delete is the calling task */
|
||||||
|
|
||||||
rtcb = this_task();
|
sched_unlock();
|
||||||
if (pid == 0 || pid == rtcb->pid)
|
if (pid == rtcb->pid)
|
||||||
{
|
{
|
||||||
/* If it is, then what we really wanted to do was exit. Note that we
|
/* If it is, then what we really wanted to do was exit. Note that we
|
||||||
* don't bother to unlock the TCB since it will be going away.
|
* don't bother to unlock the TCB since it will be going away.
|
||||||
@ -103,37 +178,16 @@ int task_delete(pid_t pid)
|
|||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
#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
|
/* Otherwise, perform the asynchronous cancellation, letting
|
||||||
* task_terminate() do all of the heavy lifting.
|
* task_terminate() do all of the heavy lifting.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return task_terminate(pid, false);
|
ret = task_terminate(pid, false);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
set_errno(-ret);
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@ int task_restart(pid_t pid)
|
|||||||
ret = task_activate((FAR struct tcb_s *)tcb);
|
ret = task_activate((FAR struct tcb_s *)tcb);
|
||||||
if (ret != OK)
|
if (ret != OK)
|
||||||
{
|
{
|
||||||
(void)task_delete(pid);
|
(void)task_terminate(pid, true);
|
||||||
errcode = -ret;
|
errcode = -ret;
|
||||||
goto errout_with_lock;
|
goto errout_with_lock;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user