Reviewed all task exit logic. For pthread_exit() moved some logic higher in the exit sequence that could be required to block. For lower level logic kicked off by _exit(), add logic to prevent blocking when the task is not in a healthy state.
This commit is contained in:
parent
2072e6be94
commit
927dd57ef2
@ -4599,4 +4599,10 @@
|
|||||||
* tools/kconfig2html.c: Improve behavior of Expand/Collapse
|
* tools/kconfig2html.c: Improve behavior of Expand/Collapse
|
||||||
Table of Contents; Handle errors in parsing of strings and in
|
Table of Contents; Handle errors in parsing of strings and in
|
||||||
some uninitialized variables. Add an option to use jQuery.
|
some uninitialized variables. Add an option to use jQuery.
|
||||||
* tools/mkconfigvar.sh: Fix make target.
|
* tools/mkconfigvar.sh: Fix make target (2014-4-23).
|
||||||
|
* sched/exit.c, pthread_exit.c, task_exit.c, task_delete,c and
|
||||||
|
task_exithook.c: For pthread_exit(), move some logic to an early
|
||||||
|
point in the exit sequence where the task may need to block. Add
|
||||||
|
conditional logic in the lower end of the eixt logic kicked off by
|
||||||
|
_exit() to prohibit blocking after the task has been torn down and is
|
||||||
|
no longer cabable of blocking (2014-4-23).
|
||||||
|
@ -100,10 +100,11 @@ void exit(int status)
|
|||||||
|
|
||||||
/* Perform common task termination logic. This will get called again later
|
/* Perform common task termination logic. This will get called again later
|
||||||
* through logic kicked off by _exit(). However, we need to call it before
|
* through logic kicked off by _exit(). However, we need to call it before
|
||||||
* calling _exit() in order to handle atexit() and on_exit() callbacks.
|
* calling _exit() in order to handle atexit() and on_exit() callbacks and
|
||||||
|
* so that we can flush buffered I/O (both of which may required suspending).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
task_exithook(tcb, status);
|
task_exithook(tcb, status, false);
|
||||||
|
|
||||||
/* Then "really" exit. Only the lower 8 bits of the exit status are used. */
|
/* Then "really" exit. Only the lower 8 bits of the exit status are used. */
|
||||||
|
|
||||||
|
@ -265,7 +265,8 @@ int task_schedsetup(FAR struct task_tcb_s *tcb, int priority, start_t start,
|
|||||||
main_t main, uint8_t ttype);
|
main_t main, uint8_t ttype);
|
||||||
int task_argsetup(FAR struct task_tcb_s *tcb, FAR const char *name, FAR char * const argv[]);
|
int task_argsetup(FAR struct task_tcb_s *tcb, FAR const char *name, FAR char * const argv[]);
|
||||||
int task_exit(void);
|
int task_exit(void);
|
||||||
void task_exithook(FAR struct tcb_s *tcb, int status);
|
int task_terminate(pid_t pid, bool nonblocking);
|
||||||
|
void task_exithook(FAR struct tcb_s *tcb, int status, bool nonblocking);
|
||||||
void task_recover(FAR struct tcb_s *tcb);
|
void task_recover(FAR struct tcb_s *tcb);
|
||||||
|
|
||||||
#ifndef CONFIG_CUSTOM_STACK
|
#ifndef CONFIG_CUSTOM_STACK
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/************************************************************************
|
/************************************************************************
|
||||||
* sched/pthread_exit.c
|
* sched/pthread_exit.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 2007, 2009, 2011-2012 Gregory Nutt. All rights reserved.
|
* Copyright (C) 2007, 2009, 2011-2013 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
|
||||||
@ -49,6 +49,7 @@
|
|||||||
|
|
||||||
#include <nuttx/arch.h>
|
#include <nuttx/arch.h>
|
||||||
|
|
||||||
|
#include "os_internal.h"
|
||||||
#include "pthread_internal.h"
|
#include "pthread_internal.h"
|
||||||
|
|
||||||
/************************************************************************
|
/************************************************************************
|
||||||
@ -93,7 +94,7 @@
|
|||||||
|
|
||||||
void pthread_exit(FAR void *exit_value)
|
void pthread_exit(FAR void *exit_value)
|
||||||
{
|
{
|
||||||
int error_code = (int)((intptr_t)exit_value);
|
struct tcb_s *tcb = (struct tcb_s*)g_readytorun.head;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
sdbg("exit_value=%p\n", exit_value);
|
sdbg("exit_value=%p\n", exit_value);
|
||||||
@ -115,23 +116,25 @@ void pthread_exit(FAR void *exit_value)
|
|||||||
if (status != OK)
|
if (status != OK)
|
||||||
{
|
{
|
||||||
/* Assume that the join completion failured because this
|
/* Assume that the join completion failured because this
|
||||||
* not really a pthread. Exit by calling exit() to flush
|
* not really a pthread. Exit by calling exit().
|
||||||
* and close all file descriptors and calling atexit()
|
|
||||||
* functions.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (error_code == EXIT_SUCCESS)
|
exit(EXIT_FAILURE);
|
||||||
{
|
|
||||||
error_code = EXIT_FAILURE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(error_code);
|
/* Perform common task termination logic. This will get called again later
|
||||||
}
|
* through logic kicked off by _exit(). However, we need to call it before
|
||||||
|
* calling _exit() in order certain operations if this is the last thread
|
||||||
|
* of a task group: (2) To handle atexit() and on_exit() callbacks and
|
||||||
|
* (2) so that we can flush buffered I/O (which may required suspending).
|
||||||
|
*/
|
||||||
|
|
||||||
|
task_exithook(tcb, EXIT_SUCCESS, false);
|
||||||
|
|
||||||
/* Then just exit, retaining all file descriptors and without
|
/* Then just exit, retaining all file descriptors and without
|
||||||
* calling atexit() functions.
|
* calling atexit() functions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
_exit(error_code);
|
_exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* sched/task_delete.c
|
* sched/task_delete.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 2007-2009, 2011-2012 Gregory Nutt. All rights reserved.
|
* Copyright (C) 2007-2009, 2011-2013 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
|
||||||
@ -77,30 +77,36 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: task_delete
|
* Name: task_terminate
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* This function causes a specified task to cease to exist. Its stack and
|
* This function causes a specified task to cease to exist. Its stack and
|
||||||
* TCB will be deallocated. This function is the companion to task_create().
|
* TCB will be deallocated. This function is the internal implementation
|
||||||
|
* of the task_delete() function. It includes and additional parameter
|
||||||
|
* to determine if blocking is permitted or not.
|
||||||
*
|
*
|
||||||
* The logic in this function only deletes non-running tasks. If the 'pid'
|
* This function is the final function called all task termination
|
||||||
* parameter refers to to the currently runing task, then processing is
|
* sequences. task_terminate() is called only from task_delete() (with
|
||||||
* redirected to exit().
|
* nonblocking == false) and from task_exit() (with nonblocking == true).
|
||||||
*
|
*
|
||||||
* Control will still be returned to task_delete() after the exit() logic
|
* The path through task_exit() supports the final stops of the exit(),
|
||||||
* finishes. In fact, this function is the final function called all task
|
* _exit(), and pthread_exit
|
||||||
* termination sequences. Here are all possible exit scenarios:
|
|
||||||
*
|
*
|
||||||
* - pthread_exit(). Calls exit()
|
* - pthread_exit(). Calls _exit()
|
||||||
* - exit(). Calls _exit()
|
* - exit(). Calls _exit()
|
||||||
* - _exit(). Calls task_exit() making the currently running task
|
* - _exit(). Calls task_exit() making the currently running task
|
||||||
* non-running then calls task_delete() to terminate the non-running
|
* non-running. task_exit then calls task_terminate() (with nonblocking
|
||||||
* task.
|
* == true) to terminate the non-running task.
|
||||||
* - task_delete()
|
*
|
||||||
|
* NOTE: that the state of non-blocking is irrelevant when called through
|
||||||
|
* exit() and pthread_exit(). In those cases task_exithook() has already
|
||||||
|
* been called with nonblocking == false;
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
|
* nonblocking - True: The task is an unhealthy, partially torn down
|
||||||
|
* state and is not permitted to block.
|
||||||
*
|
*
|
||||||
* Return Value:
|
* Return Value:
|
||||||
* OK on success; or ERROR on failure
|
* OK on success; or ERROR on failure
|
||||||
@ -110,25 +116,12 @@
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
int task_delete(pid_t pid)
|
int task_terminate(pid_t pid, bool nonblocking)
|
||||||
{
|
{
|
||||||
FAR struct tcb_s *rtcb;
|
|
||||||
FAR struct tcb_s *dtcb;
|
FAR struct tcb_s *dtcb;
|
||||||
irqstate_t saved_state;
|
irqstate_t saved_state;
|
||||||
int ret = ERROR;
|
int ret = ERROR;
|
||||||
|
|
||||||
/* Check if the task to delete is the calling task */
|
|
||||||
|
|
||||||
rtcb = (FAR struct tcb_s*)g_readytorun.head;
|
|
||||||
if (pid == 0 || pid == rtcb->pid)
|
|
||||||
{
|
|
||||||
/* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure the task does not become ready-to-run while we are futzing with
|
/* Make sure the task does not become ready-to-run while we are futzing with
|
||||||
* its TCB by locking ourselves as the executing task.
|
* its TCB by locking ourselves as the executing task.
|
||||||
*/
|
*/
|
||||||
@ -166,7 +159,7 @@ int task_delete(pid_t pid)
|
|||||||
* I suppose EXIT_SUCCESS is an appropriate return value???
|
* I suppose EXIT_SUCCESS is an appropriate return value???
|
||||||
*/
|
*/
|
||||||
|
|
||||||
task_exithook(dtcb, EXIT_SUCCESS);
|
task_exithook(dtcb, EXIT_SUCCESS, nonblocking);
|
||||||
|
|
||||||
/* Remove the task from the OS's tasks lists. */
|
/* Remove the task from the OS's tasks lists. */
|
||||||
|
|
||||||
@ -192,3 +185,61 @@ int task_delete(pid_t pid)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: task_delete
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* This function causes a specified task to cease to exist. Its stack and
|
||||||
|
* TCB will be deallocated. This function is the companion to task_create().
|
||||||
|
* This is the version of the function exposed to the user; it is simply
|
||||||
|
* a wrapper around the internal, task_terminate function.
|
||||||
|
*
|
||||||
|
* The logic in this function only deletes non-running tasks. If the 'pid'
|
||||||
|
* parameter refers to to the currently runing task, then processing is
|
||||||
|
* redirected to exit(). This can only happen if a task calls task_delete()
|
||||||
|
* in order to delete itself.
|
||||||
|
*
|
||||||
|
* In fact, this function (and task_terminate) are the final functions
|
||||||
|
* called all task termination sequences. task_delete may be called
|
||||||
|
* from:
|
||||||
|
*
|
||||||
|
* - task_restart(),
|
||||||
|
* - pthread_cancel(),
|
||||||
|
* - and directly from user code.
|
||||||
|
*
|
||||||
|
* Other exit paths (exit(), _eixt(), and pthread_exit()) will go through
|
||||||
|
* task_terminate()
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* pid - The task ID of the task to delete. A pid of zero
|
||||||
|
* signifies the calling task.
|
||||||
|
*
|
||||||
|
* Return Value:
|
||||||
|
* OK on success; or ERROR on failure
|
||||||
|
*
|
||||||
|
* This function can fail if the provided pid does not correspond to a
|
||||||
|
* task (errno is not set)
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
int task_delete(pid_t pid)
|
||||||
|
{
|
||||||
|
FAR struct tcb_s *rtcb;
|
||||||
|
|
||||||
|
/* Check if the task to delete is the calling task */
|
||||||
|
|
||||||
|
rtcb = (FAR struct tcb_s*)g_readytorun.head;
|
||||||
|
if (pid == 0 || pid == rtcb->pid)
|
||||||
|
{
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
DEBUGASSERT(!nonblocking);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Then let task_terminate do the heavy lifting */
|
||||||
|
|
||||||
|
return task_terminate(pid, false);
|
||||||
|
}
|
||||||
|
@ -69,116 +69,6 @@
|
|||||||
* Private Functions
|
* Private Functions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
* Name: task_cancel_atexit
|
|
||||||
*
|
|
||||||
* Description:
|
|
||||||
* Cncel any registerd atexit function(s)
|
|
||||||
*
|
|
||||||
* This function is called from task_exit() which implements the processor-
|
|
||||||
* independent part of _exit(). _exit() is, in turn, used to implement
|
|
||||||
* the bottom half of exit() and pthread_exit(). These cases are
|
|
||||||
* distinguished as follows:
|
|
||||||
*
|
|
||||||
* 1) _exit() should be called by user logic only from tasks. In this
|
|
||||||
* case, atexit() calls will be canceled by this function.
|
|
||||||
* 2) If the user calls exit(), the exit() function will call task_exithook()
|
|
||||||
* which will process all pending atexit() call. In that case, this
|
|
||||||
* function will have no effect.
|
|
||||||
* 3) If the user called pthread_exit(), the logic in this function will
|
|
||||||
* do nothing. Only a task can legitimately called _exit(). atexit
|
|
||||||
* calls will not be cleared. task_exithook() will be called later (from
|
|
||||||
* task_delete()) and if this is the final thread of the group, any
|
|
||||||
* registered atexit() calls will be performed.
|
|
||||||
*
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#if defined(CONFIG_SCHED_ATEXIT) && !defined(CONFIG_SCHED_ONEXIT)
|
|
||||||
static inline void task_cancel_atexit(FAR struct tcb_s *tcb)
|
|
||||||
{
|
|
||||||
FAR struct task_group_s *group = tcb->group;
|
|
||||||
DEBUGASSERT(group);
|
|
||||||
|
|
||||||
/* This behavior applies only to tasks that call _exit() */
|
|
||||||
|
|
||||||
#ifndef CONFIG_DISABLE_PTHREAD
|
|
||||||
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
#if defined(CONFIG_SCHED_ATEXIT_MAX) && CONFIG_SCHED_ATEXIT_MAX > 1
|
|
||||||
int index;
|
|
||||||
|
|
||||||
/* Nullify each atexit function pointer */
|
|
||||||
|
|
||||||
for (index = 0; index < CONFIG_SCHED_ATEXIT_MAX; index++)
|
|
||||||
{
|
|
||||||
group->tg_atexitfunc[index] = NULL;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
/* Nullify the atexit function to prevent its reuse. */
|
|
||||||
|
|
||||||
group->tg_atexitfunc = NULL;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
# define task_cancel_atexit(tcb)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
* Name: task_cancel_onexit
|
|
||||||
*
|
|
||||||
* Description:
|
|
||||||
* Cancel any registerd on)exit function(s).
|
|
||||||
*
|
|
||||||
* This function is called from task_exit() which implements the processor-
|
|
||||||
* independent part of _exit(). _exit() is, in turn, used to implement
|
|
||||||
* the bottom half of exit() and pthread_exit(). These cases are
|
|
||||||
* distinguished as follows:
|
|
||||||
*
|
|
||||||
* 1) _exit() should be called by user logic only from tasks. In this
|
|
||||||
* case, on_exit() calls will be canceled by this function.
|
|
||||||
* 2) If the user calls exit(), the exit() function will call task_exithook()
|
|
||||||
* which will process all pending on_exit() call. In that case, this
|
|
||||||
* function will have no effect.
|
|
||||||
* 3) If the user called pthread_exit(), the logic in this function will
|
|
||||||
* do nothing. Only a task can legitimately called _exit(). on_exit
|
|
||||||
* calls will not be cleared. task_exithook() will be called later (from
|
|
||||||
* task_delete()) and if this is the final thread of the group, any
|
|
||||||
* registered on_exit() calls will be performed.
|
|
||||||
*
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifdef CONFIG_SCHED_ONEXIT
|
|
||||||
static inline void task_cancel_onexit(FAR struct tcb_s *tcb)
|
|
||||||
{
|
|
||||||
FAR struct task_group_s *group = tcb->group;
|
|
||||||
DEBUGASSERT(group);
|
|
||||||
|
|
||||||
/* This behavior applies only to tasks that call _exit() */
|
|
||||||
|
|
||||||
#ifndef CONFIG_DISABLE_PTHREAD
|
|
||||||
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
#if defined(CONFIG_SCHED_ONEXIT_MAX) && CONFIG_SCHED_ONEXIT_MAX > 1
|
|
||||||
int index;
|
|
||||||
|
|
||||||
/* Nullify each atexit function pointer */
|
|
||||||
|
|
||||||
for (index = 0; index < CONFIG_SCHED_ONEXIT_MAX; index++)
|
|
||||||
{
|
|
||||||
group->tg_onexitfunc[index] = NULL;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
group->tg_onexitfunc = NULL;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
# define task_cancel_onexit(tcb)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Public Functions
|
* Public Functions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@ -227,14 +117,6 @@ int task_exit(void)
|
|||||||
(void)sched_removereadytorun(dtcb);
|
(void)sched_removereadytorun(dtcb);
|
||||||
rtcb = (FAR struct tcb_s*)g_readytorun.head;
|
rtcb = (FAR struct tcb_s*)g_readytorun.head;
|
||||||
|
|
||||||
/* Cancel any pending atexit() or on_exit() calls. These are not performed
|
|
||||||
* when performing _exit(). Different implementations of _exit() may or may
|
|
||||||
* not* flush buffered I/O. This implemenation *will* flush buffered I/O.
|
|
||||||
*/
|
|
||||||
|
|
||||||
task_cancel_atexit(rtcb);
|
|
||||||
task_cancel_onexit(rtcb);
|
|
||||||
|
|
||||||
/* We are now in a bad state -- the head of the ready to run task list
|
/* We are now in a bad state -- the head of the ready to run task list
|
||||||
* does not correspond to the thread that is running. Disabling pre-
|
* does not correspond to the thread that is running. Disabling pre-
|
||||||
* emption on this TCB and marking the new ready-to-run task as not
|
* emption on this TCB and marking the new ready-to-run task as not
|
||||||
@ -247,10 +129,14 @@ int task_exit(void)
|
|||||||
rtcb->lockcount++;
|
rtcb->lockcount++;
|
||||||
rtcb->task_state = TSTATE_TASK_READYTORUN;
|
rtcb->task_state = TSTATE_TASK_READYTORUN;
|
||||||
|
|
||||||
/* Move the TCB to the specified blocked task list and delete it */
|
/* Move the TCB to the specified blocked task list and delete it. Calling
|
||||||
|
* task_terminate with non-blocking true will suppress atexit() and on-exit()
|
||||||
|
* calls and will cause buffered I/O to fail to be flushed. The former
|
||||||
|
* is required _exit() behavior; the latter is optional _exit() behavior.
|
||||||
|
*/
|
||||||
|
|
||||||
sched_addblocked(dtcb, TSTATE_TASK_INACTIVE);
|
sched_addblocked(dtcb, TSTATE_TASK_INACTIVE);
|
||||||
task_delete(dtcb->pid);
|
task_terminate(dtcb->pid, true);
|
||||||
rtcb->task_state = TSTATE_TASK_RUNNING;
|
rtcb->task_state = TSTATE_TASK_RUNNING;
|
||||||
|
|
||||||
/* If there are any pending tasks, then add them to the ready-to-run
|
/* If there are any pending tasks, then add them to the ready-to-run
|
||||||
|
@ -588,14 +588,18 @@ static inline void task_flushstreams(FAR struct tcb_s *tcb)
|
|||||||
* to-run list. The following logic is safe because we will not be
|
* to-run list. The following logic is safe because we will not be
|
||||||
* returning from the exit() call.
|
* returning from the exit() call.
|
||||||
*
|
*
|
||||||
* When called from task_delete() we are operating on a different thread;
|
* When called from task_terminate() we are operating on a different thread;
|
||||||
* on the thread that called task_delete(). In this case, task_delete
|
* on the thread that called task_delete(). In this case, task_delete
|
||||||
* will have already removed the tcb from the ready-to-run list to prevent
|
* will have already removed the tcb from the ready-to-run list to prevent
|
||||||
* any further action on this task.
|
* any further action on this task.
|
||||||
*
|
*
|
||||||
|
* nonblocking will be set true only when we are called from task_terminate()
|
||||||
|
* via _exit(). In that case, we must be careful to do nothing that can
|
||||||
|
* cause the cause the thread to block.
|
||||||
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
void task_exithook(FAR struct tcb_s *tcb, int status)
|
void task_exithook(FAR struct tcb_s *tcb, int status, bool nonblocking)
|
||||||
{
|
{
|
||||||
/* Under certain conditions, task_exithook() can be called multiple times.
|
/* Under certain conditions, task_exithook() can be called multiple times.
|
||||||
* A bit in the TCB was set the first time this function was called. If
|
* A bit in the TCB was set the first time this function was called. If
|
||||||
@ -608,15 +612,28 @@ void task_exithook(FAR struct tcb_s *tcb, int status)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If exit function(s) were registered, call them now before we do any un-
|
/* If exit function(s) were registered, call them now before we do any un-
|
||||||
* initialization. NOTE: In the case of task_delete(), the exit function
|
* initialization.
|
||||||
* will *not* be called on the thread execution of the task being deleted!
|
*
|
||||||
|
* NOTES:
|
||||||
|
*
|
||||||
|
* 1. In the case of task_delete(), the exit function will *not* be called
|
||||||
|
* on the thread execution of the task being deleted! That is probably
|
||||||
|
* a bug.
|
||||||
|
* 2. We cannot call the exit functions if nonblocking is requested: These
|
||||||
|
* functions might block.
|
||||||
|
* 3. This function will only be called with with non-blocking == true
|
||||||
|
* only when called through _exit(). _exit() behaviors requires that
|
||||||
|
* the exit functions *not* be called.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if (!nonblocking)
|
||||||
|
{
|
||||||
task_atexit(tcb);
|
task_atexit(tcb);
|
||||||
|
|
||||||
/* Call any registered on_exit function(s) */
|
/* Call any registered on_exit function(s) */
|
||||||
|
|
||||||
task_onexit(tcb, status);
|
task_onexit(tcb, status);
|
||||||
|
}
|
||||||
|
|
||||||
/* If the task was terminated by another task, it may be in an unknown
|
/* If the task was terminated by another task, it may be in an unknown
|
||||||
* state. Make some feeble effort to recover the state.
|
* state. Make some feeble effort to recover the state.
|
||||||
@ -634,9 +651,19 @@ void task_exithook(FAR struct tcb_s *tcb, int status)
|
|||||||
|
|
||||||
/* If this is the last thread in the group, then flush all streams (File
|
/* If this is the last thread in the group, then flush all streams (File
|
||||||
* descriptors will be closed when the TCB is deallocated).
|
* descriptors will be closed when the TCB is deallocated).
|
||||||
|
*
|
||||||
|
* NOTES:
|
||||||
|
* 1. We cannot flush the buffered I/O if nonblocking is requested.
|
||||||
|
* that might cause this logic to block.
|
||||||
|
* 2. This function will only be called with with non-blocking == true
|
||||||
|
* only when called through _exit(). _exit() behavior does not
|
||||||
|
* require that the streams be flushed
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
if (!nonblocking)
|
||||||
|
{
|
||||||
task_flushstreams(tcb);
|
task_flushstreams(tcb);
|
||||||
|
}
|
||||||
|
|
||||||
/* Leave the task group. Perhaps discarding any un-reaped child
|
/* Leave the task group. Perhaps discarding any un-reaped child
|
||||||
* status (no zombies here!)
|
* status (no zombies here!)
|
||||||
|
Loading…
Reference in New Issue
Block a user