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
48
ChangeLog
48
ChangeLog
@ -563,7 +563,7 @@
|
||||
types.
|
||||
|
||||
0.3.19 2008-11-26 Gregory Nutt <gnutt@nuttx.org>
|
||||
* Add poll() and select() APIs (in the initial check-in, these work only with character devices)
|
||||
* Add poll() and select() APIs (in the initial check-in, these work only with character devices)
|
||||
* Add poll() methods to /dev/null, /dev/zero, pipes, fifos, and serial drivers.
|
||||
* Add examples/poll for testing poll() and select()
|
||||
* Fix hostile behavior of getc, fgetc, getchar, etc.: the serial driver was waiting for a
|
||||
@ -612,8 +612,8 @@
|
||||
|
||||
0.4.2 2009-02-28 Gregory Nutt <gnutt@nuttx.org>
|
||||
|
||||
* M16C: Add support for the Renesas M16C MCU and the SKP16C26 StarterKit. However,
|
||||
the target cannot be built because the GNU m16c-elf-ld link fails with
|
||||
* M16C: Add support for the Renesas M16C MCU and the SKP16C26 StarterKit. However,
|
||||
the target cannot be built because the GNU m16c-elf-ld link fails with
|
||||
the following message:
|
||||
|
||||
m32c-elf-ld: BFD (GNU Binutils) 2.19 assertion fail /home/Owner/projects/nuttx/buildroot/toolchain_build_m32c/binutils-2.19/bfd/elf32-m32c.c:482
|
||||
@ -711,7 +711,7 @@
|
||||
-R .note.gnu.build-id -R .comment" This has been fixed in arch/arm/src/Makefile,
|
||||
but other architectures may have the same problem. Thanks to Dave Marples
|
||||
for verifying this.
|
||||
* configs/eagle100/ostest: Added support for the MicroMint Eagle100 board.
|
||||
* configs/eagle100/ostest: Added support for the MicroMint Eagle100 board.
|
||||
This board has a Luminary LM3S6918 Cortex-M3. Added a configuration to build
|
||||
examples/ostest.
|
||||
* arch/arm/src/lpc214x: Add configuration option to enable fast GPIO (vs.
|
||||
@ -1045,7 +1045,7 @@
|
||||
* configs/sam3u-3k/ostest: Completed verification of the basic NuttX
|
||||
OS test for the SAM3U.
|
||||
* arch/arm/src/common/up_createstack: stack was always been cleared
|
||||
when it was allocated. This is a good feature for monitoring the
|
||||
when it was allocated. This is a good feature for monitoring the
|
||||
stack during debug, but really hurts thread start-up performance.
|
||||
Clearing is now done if CONFIG_DEBUG=y only. Changes was only made
|
||||
for arm, but really should be made for all architectures.
|
||||
@ -1186,7 +1186,7 @@
|
||||
* examples/nsh/nsh_telnetd.c: Fix compilation errors that happen
|
||||
when both DHCPC and TELNETD are enabled in the Nuttshell.
|
||||
* graphics/nxglib/fb/nxglib_moverectangle.c: Fix a logic error
|
||||
that caused an uninitialized variable warning. I still don't
|
||||
that caused an uninitialized variable warning. I still don't
|
||||
have a test to prove that the changes are correct.
|
||||
* configs/olimex-lpc2378: Add support for the CodeSourcery toolchain
|
||||
under Linux (contributed by Alan Carvalho de Assis).
|
||||
@ -1669,14 +1669,14 @@
|
||||
* arch/arm/src/lpc17xx/lpc17_gpioint.c: Correct errors in logic that maps
|
||||
and IRQ number into a register bit number.
|
||||
* Makefile: Fix an error introduced in the top-level Makefile in NuttX-6.1.
|
||||
This error only shows up if you have a /tftpboot directory. Then the
|
||||
This error only shows up if you have a /tftpboot directory. Then the
|
||||
make will fail with an obscure error about not being able to stat pass2.
|
||||
* configs/lpcxpresso-lpc1768/nsh: Add an NSH configuration for the
|
||||
LPCXpresso board.
|
||||
* configs/*/ld.script: Removed 'sh_link not set for section .ARM.edix' for
|
||||
a few of the builds. In you have this warning, it can be removed with the
|
||||
following change to the ld.script file:
|
||||
|
||||
|
||||
+ __exidx_start = ABSOLUTE(.);
|
||||
.ARM.exidx : {
|
||||
- __exidx_start = ABSOLUTE(.);
|
||||
@ -1802,7 +1802,7 @@
|
||||
already disconnected and the buffered data is stranded. Now, recvfrom
|
||||
will continue to return success after the socket is disconnected until
|
||||
the readahead buffer is drained.
|
||||
* olimex-lp1766stk/ftpc/defconfig: Many configurations have the MTU
|
||||
* olimex-lp1766stk/ftpc/defconfig: Many configurations have the MTU
|
||||
(CONFIG_NET_BUFSIZE) set to very small numbers, less then the minimum
|
||||
MTU size that must be supported -- 576. This can cause problems in
|
||||
some networks: CONFIG_NET_BUFSIZE should be set to at least 576 in
|
||||
@ -1915,7 +1915,7 @@
|
||||
new file; Implement VFAT long file name support.
|
||||
* fs/fat/fat_fat32dirent.c: The configuration CONFIG_FAT_LCNAMES has
|
||||
been around for some time but never tested until now. This setting
|
||||
will mimic the NT 8.3 file name behavior: File names or extensions
|
||||
will mimic the NT 8.3 file name behavior: File names or extensions
|
||||
may be all upper or all lower case (but not mixed). If
|
||||
CONFIG_FAT_LCNAMES is not selected, all filenames are strictly upper
|
||||
case.
|
||||
@ -1943,7 +1943,7 @@
|
||||
program. The bdf-converter program be used to convert fonts in Bitmap
|
||||
Distribution Format (BDF) into fonts that can be used in the NX graphics
|
||||
system.
|
||||
* include/nuttx/nx: Move all NX header files from include/nuttx to
|
||||
* include/nuttx/nx: Move all NX header files from include/nuttx to
|
||||
include/nuttx/nx.
|
||||
* drivers/usbdev/usbdev_usbstorage.c and arch/arm/src/stm32/stm32_usbdev.c:
|
||||
Correct a memory leak when the USB mass storage driver is connected and
|
||||
@ -2051,7 +2051,7 @@
|
||||
* arch/*/src/Makefile: Use of -print-libgcc-file-name to get path to
|
||||
libgcc.a may select the wrong libgcc.a if a multilib toolchain (like
|
||||
CodeSourcery) is used. This can be a serious problem and can cause
|
||||
crashes on Cortex-M3 if the ARM libgcc is used, for example. The fix
|
||||
crashes on Cortex-M3 if the ARM libgcc is used, for example. The fix
|
||||
is to include ARCHCPUFLAGS on the gcc command line when asking it to
|
||||
-print-libgcc-file-name.
|
||||
* lib/time/lib_gmtimer.c: Correct several calculations that could lead
|
||||
@ -2280,7 +2280,7 @@
|
||||
the particular condition that revealed the bug occurred. My impression is
|
||||
that this latter bugfix also fixes some STM32 USB performance problems.
|
||||
* configs/hymini-stm32v: A configuration for the HY-Mini STM32v board contributed
|
||||
by Laurent Latil. These changes also include support for the STM32F103VCT6.
|
||||
by Laurent Latil. These changes also include support for the STM32F103VCT6.
|
||||
* arch/configs/stm3240g-eval/src/up_pwm.c: Add hooks needed to use the new
|
||||
apps/examples/pwm test of the STM32 PWM driver.
|
||||
* drivers/mtd/mp25x.c: Add ability to use different SPI modes and different
|
||||
@ -2394,7 +2394,7 @@
|
||||
common upper half battery driver.
|
||||
* drivers/power/max1704x.c: Add a driver for MAX17040x battery "fuel gauge"
|
||||
* arch/arm/src/stm32/stm32_i2c.c: Add support for I2C3
|
||||
* drivers/usbdev/: Lots of name changes: cdc_serial->cdcacm, usbstrg->usbmsc,
|
||||
* drivers/usbdev/: Lots of name changes: cdc_serial->cdcacm, usbstrg->usbmsc,
|
||||
usbser->pl2303
|
||||
* drivers/usbdev/composite: Fleshed out support for a composite USB device.
|
||||
* drivers/stm3210e-eval/composite and drivers/stm3210e-eval/src/up_composite.c:
|
||||
@ -2704,7 +2704,7 @@
|
||||
platform did not make into all of the Make.defs files.
|
||||
* graphics/nxmu/nx_move.c: Wrong opcode was being used in the server message;
|
||||
Also there was an error in the offset calculation.
|
||||
* graphics/nxglib/fb/nxglib_moverectangle.c: Offset argument is really a
|
||||
* graphics/nxglib/fb/nxglib_moverectangle.c: Offset argument is really a
|
||||
position, not an offset.
|
||||
* graphics/nxtk/nxtk_drawframe.c: Framed windows are now drawn in three
|
||||
colors (instead of just two).
|
||||
@ -2938,7 +2938,7 @@
|
||||
is slowly evolving in these directories.
|
||||
* configs/stm3210e-eval/pm: Add a new configuration for testing STM32 power
|
||||
management.
|
||||
* configs/stm3210e-eval/scripts: Moved all of the duplicate ST3210-EVAL
|
||||
* configs/stm3210e-eval/scripts: Moved all of the duplicate ST3210-EVAL
|
||||
linker scripts into one set of linker scripts at this location.
|
||||
* configs/stm3210e-eval/src/up_buttons.c, up_lcd.c, and up_pm.c: New logic
|
||||
for testing STM32 power management.
|
||||
@ -3139,7 +3139,7 @@
|
||||
customize the behavior of NSH.
|
||||
* arch/arm/src/stm32/chip/stm32f1*_pinmap.h: STM32 CAN TX/RX pins reversed;
|
||||
inconsistent conditional compilation. Reported by Max Holtzberg.
|
||||
* arch/arm/*/stm32: Add support for STM32 F107 "Connectivity Line"
|
||||
* arch/arm/*/stm32: Add support for STM32 F107 "Connectivity Line"
|
||||
Ethernet (contributed by Max Holtzberg).
|
||||
* configs/olimex-stm32-p107: Add board support for the Olimiex STM32-P107
|
||||
board (contributed by Max Holtzberg).
|
||||
@ -3317,7 +3317,7 @@
|
||||
to allocate the block driver I/O buffers.
|
||||
* CONFIG_NET_ENC28J60 renamed CONFIG_ENC28J60 to be consistent
|
||||
in all places.
|
||||
* drivers/enc28j60.c, include/nuttx/net/enc28j60.h, and
|
||||
* drivers/enc28j60.c, include/nuttx/net/enc28j60.h, and
|
||||
olimex-strp711/src/up_enc28j60.c: No longer passes IRQ number
|
||||
as a parameter. Instead now passes a call table to manage
|
||||
ENC28J60 GPIO interrupts. That is because GPIO interrupts are
|
||||
@ -3630,7 +3630,7 @@
|
||||
just needs re-implemented.
|
||||
* configs/mirtoo: Update Mirtoo pin definitions for Release 2. Provided
|
||||
by Konstantin Dimitrov.
|
||||
* Fixed an uninitialized variable in the file system that can cause
|
||||
* Fixed an uninitialized variable in the file system that can cause
|
||||
assertions if DEBUG on (contributed by Lorenz Meier).
|
||||
* Config.mk: Defined DELIM to be either / or \, depending upon
|
||||
CONFIG_WINDOWS_NATIVE. This will allow me to eliminate a lot of
|
||||
@ -4088,7 +4088,7 @@
|
||||
switched to SPI mode for first time. Having a pull-up resistor on
|
||||
MISO may avoid this problem, but this patch makes it work also
|
||||
without pull-up. From Petteri Aimonen.
|
||||
* fs/fat/fs_fat32.c: Fix a compilation error when FAT_DMAMEMORY=y.
|
||||
* fs/fat/fs_fat32.c: Fix a compilation error when FAT_DMAMEMORY=y.
|
||||
From Petteri Aimonen.
|
||||
* arch/arm/src/stm32/chip/stm32_spi.h: STM32F4 max SPI clock freq is
|
||||
37.5 MHz. Patch from Petteri Aimonen.
|
||||
@ -4599,4 +4599,10 @@
|
||||
* tools/kconfig2html.c: Improve behavior of Expand/Collapse
|
||||
Table of Contents; Handle errors in parsing of strings and in
|
||||
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).
|
||||
|
@ -289,8 +289,8 @@ struct usbdev_s
|
||||
{
|
||||
const struct usbdev_ops_s *ops; /* Access to hardware specific features */
|
||||
struct usbdev_ep_s *ep0; /* Endpoint zero */
|
||||
uint8_t speed; /* Current speed of the host connection */
|
||||
uint8_t dualspeed:1; /* 1:supports high and full speed operation */
|
||||
uint8_t speed; /* Current speed of the host connection */
|
||||
uint8_t dualspeed:1; /* 1:supports high and full speed operation */
|
||||
};
|
||||
|
||||
/* USB Device Class Implementations *************************************************/
|
||||
|
@ -100,10 +100,11 @@ void exit(int status)
|
||||
|
||||
/* 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 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. */
|
||||
|
||||
|
@ -265,7 +265,8 @@ int task_schedsetup(FAR struct task_tcb_s *tcb, int priority, start_t start,
|
||||
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_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);
|
||||
|
||||
#ifndef CONFIG_CUSTOM_STACK
|
||||
|
@ -1,7 +1,7 @@
|
||||
/************************************************************************
|
||||
* 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>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -49,6 +49,7 @@
|
||||
|
||||
#include <nuttx/arch.h>
|
||||
|
||||
#include "os_internal.h"
|
||||
#include "pthread_internal.h"
|
||||
|
||||
/************************************************************************
|
||||
@ -93,7 +94,7 @@
|
||||
|
||||
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;
|
||||
|
||||
sdbg("exit_value=%p\n", exit_value);
|
||||
@ -115,23 +116,25 @@ void pthread_exit(FAR void *exit_value)
|
||||
if (status != OK)
|
||||
{
|
||||
/* Assume that the join completion failured because this
|
||||
* not really a pthread. Exit by calling exit() to flush
|
||||
* and close all file descriptors and calling atexit()
|
||||
* functions.
|
||||
* not really a pthread. Exit by calling exit().
|
||||
*/
|
||||
|
||||
if (error_code == EXIT_SUCCESS)
|
||||
{
|
||||
error_code = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
exit(error_code);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* 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
|
||||
* calling atexit() functions.
|
||||
*/
|
||||
|
||||
_exit(error_code);
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
* 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>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -77,30 +77,36 @@
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: task_delete
|
||||
* Name: task_terminate
|
||||
*
|
||||
* 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().
|
||||
* 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'
|
||||
* parameter refers to to the currently runing task, then processing is
|
||||
* redirected to exit().
|
||||
* This function is the final function called all task termination
|
||||
* sequences. task_terminate() is called only from task_delete() (with
|
||||
* nonblocking == false) and from task_exit() (with nonblocking == true).
|
||||
*
|
||||
* Control will still be returned to task_delete() after the exit() logic
|
||||
* finishes. In fact, this function is the final function called all task
|
||||
* termination sequences. Here are all possible exit scenarios:
|
||||
* The path through task_exit() supports the final stops of the exit(),
|
||||
* _exit(), and pthread_exit
|
||||
*
|
||||
* - pthread_exit(). Calls exit()
|
||||
* - pthread_exit(). Calls _exit()
|
||||
* - exit(). Calls _exit()
|
||||
* - _exit(). Calls task_exit() making the currently running task
|
||||
* non-running then calls task_delete() to terminate the non-running
|
||||
* task.
|
||||
* - task_delete()
|
||||
* non-running. task_exit then calls task_terminate() (with nonblocking
|
||||
* == true) to terminate the non-running task.
|
||||
*
|
||||
* 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:
|
||||
* pid - The task ID of the task to delete. A pid of zero
|
||||
* signifies the calling task.
|
||||
* nonblocking - True: The task is an unhealthy, partially torn down
|
||||
* state and is not permitted to block.
|
||||
*
|
||||
* Return Value:
|
||||
* 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;
|
||||
irqstate_t saved_state;
|
||||
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
|
||||
* 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???
|
||||
*/
|
||||
|
||||
task_exithook(dtcb, EXIT_SUCCESS);
|
||||
task_exithook(dtcb, EXIT_SUCCESS, nonblocking);
|
||||
|
||||
/* Remove the task from the OS's tasks lists. */
|
||||
|
||||
@ -192,3 +185,61 @@ int task_delete(pid_t pid)
|
||||
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
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* 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
|
||||
****************************************************************************/
|
||||
@ -227,14 +117,6 @@ int task_exit(void)
|
||||
(void)sched_removereadytorun(dtcb);
|
||||
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
|
||||
* 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
|
||||
@ -247,10 +129,14 @@ int task_exit(void)
|
||||
rtcb->lockcount++;
|
||||
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);
|
||||
task_delete(dtcb->pid);
|
||||
task_terminate(dtcb->pid, true);
|
||||
rtcb->task_state = TSTATE_TASK_RUNNING;
|
||||
|
||||
/* If there are any pending tasks, then add them to the ready-to-run
|
||||
|
@ -143,7 +143,7 @@ static inline void task_atexit(FAR struct tcb_s *tcb)
|
||||
* Call any registerd on_exit function(s)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifdef CONFIG_SCHED_ONEXIT
|
||||
static inline void task_onexit(FAR struct tcb_s *tcb, int status)
|
||||
{
|
||||
@ -298,7 +298,7 @@ static inline void task_sigchild(gid_t pgid, FAR struct tcb_s *ctcb, int status)
|
||||
DEBUGASSERT(chgrp);
|
||||
|
||||
/* Get the parent task group. It is possible that all of the members of
|
||||
* the parent task group have exited. This would not be an error. In
|
||||
* the parent task group have exited. This would not be an error. In
|
||||
* this case, the child task group has been orphaned.
|
||||
*/
|
||||
|
||||
@ -508,8 +508,8 @@ static inline void task_exitwakeup(FAR struct tcb_s *tcb, int status)
|
||||
*
|
||||
* "If more than one thread is suspended in waitpid() awaiting
|
||||
* termination of the same process, exactly one thread will
|
||||
* return the process status at the time of the target process
|
||||
* termination."
|
||||
* return the process status at the time of the target process
|
||||
* termination."
|
||||
*
|
||||
* Hmmm.. what do we return to the others?
|
||||
*/
|
||||
@ -528,7 +528,7 @@ static inline void task_exitwakeup(FAR struct tcb_s *tcb, int status)
|
||||
|
||||
group->tg_statloc = NULL;
|
||||
while (group->tg_exitsem.semcount < 0)
|
||||
{
|
||||
{
|
||||
/* Wake up the thread */
|
||||
|
||||
sem_post(&group->tg_exitsem);
|
||||
@ -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
|
||||
* 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
|
||||
* will have already removed the tcb from the ready-to-run list to prevent
|
||||
* 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.
|
||||
* 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-
|
||||
* initialization. NOTE: In the case of task_delete(), the exit function
|
||||
* will *not* be called on the thread execution of the task being deleted!
|
||||
* initialization.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
task_atexit(tcb);
|
||||
if (!nonblocking)
|
||||
{
|
||||
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
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
|
||||
task_flushstreams(tcb);
|
||||
if (!nonblocking)
|
||||
{
|
||||
task_flushstreams(tcb);
|
||||
}
|
||||
|
||||
/* Leave the task group. Perhaps discarding any un-reaped child
|
||||
* status (no zombies here!)
|
||||
|
Loading…
Reference in New Issue
Block a user