diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2013-01-23 22:23:46 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2013-01-23 22:23:46 +0000 |
commit | 7080c71f2b2007926d5d7728f2ff443968ebe1fc (patch) | |
tree | dbc9c30e5aec5ddcdf6f0abd2b9a9416721c9bce | |
parent | 7866bca6aac68f69426b9433fa5c972b5143dde1 (diff) | |
download | px4-nuttx-7080c71f2b2007926d5d7728f2ff443968ebe1fc.tar.gz px4-nuttx-7080c71f2b2007926d5d7728f2ff443968ebe1fc.tar.bz2 px4-nuttx-7080c71f2b2007926d5d7728f2ff443968ebe1fc.zip |
Add logic to retain child task exit status if so configured
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5553 42af7a65-404d-4744-a932-0658087f49c3
-rw-r--r-- | nuttx/ChangeLog | 6 | ||||
-rw-r--r-- | nuttx/Documentation/NuttxPortingGuide.html | 66 | ||||
-rw-r--r-- | nuttx/Documentation/NuttxUserGuide.html | 128 | ||||
-rw-r--r-- | nuttx/arch/arm/src/stm32/Kconfig | 2 | ||||
-rw-r--r-- | nuttx/configs/README.txt | 71 | ||||
-rw-r--r-- | nuttx/configs/sim/ostest/defconfig | 11 | ||||
-rw-r--r-- | nuttx/include/nuttx/sched.h | 3 | ||||
-rw-r--r-- | nuttx/include/signal.h | 12 | ||||
-rw-r--r-- | nuttx/include/sys/types.h | 4 | ||||
-rw-r--r-- | nuttx/sched/Kconfig | 79 | ||||
-rw-r--r-- | nuttx/sched/os_internal.h | 7 | ||||
-rw-r--r-- | nuttx/sched/os_start.c | 2 | ||||
-rw-r--r-- | nuttx/sched/pthread_create.c | 11 | ||||
-rw-r--r-- | nuttx/sched/sched_waitid.c | 75 | ||||
-rw-r--r-- | nuttx/sched/sched_waitpid.c | 62 | ||||
-rw-r--r-- | nuttx/sched/sig_action.c | 157 | ||||
-rw-r--r-- | nuttx/sched/task_childstatus.c | 125 | ||||
-rw-r--r-- | nuttx/sched/task_create.c | 16 | ||||
-rw-r--r-- | nuttx/sched/task_exithook.c | 109 | ||||
-rw-r--r-- | nuttx/sched/task_init.c | 3 | ||||
-rw-r--r-- | nuttx/sched/task_reparent.c | 23 | ||||
-rw-r--r-- | nuttx/sched/task_setup.c | 68 | ||||
-rw-r--r-- | nuttx/sched/task_vfork.c | 9 |
23 files changed, 836 insertions, 213 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index 6c176c601..38b1b6b1f 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -4002,7 +4002,7 @@ Start of support of LCD1602 alphanumeric LCD. I need a few more parts before I can finish integrating this one. * arch/arm/src/*/chip.h and arch/arm/include/*/chip.h: Move all - priority ragnes from the src to the include chip.h header file. + priority ranges from the src to the include chip.h header file. * arch/arm/include/armv7-m/irq.h: Add inline functions to enable and disable interrupts via the BASEPRI register. * arch/arm/Kconfig: Add new option CONFIG_ARM7VM_USEBASEI @@ -4012,4 +4012,8 @@ while CONFIG_ARM7VM_USEBASEPRI=y. * arch/arm/src/stm32/stm32_serial.c: Add support for USART single wire more (Contributed by the PX4 team). + * sched/: Implement support for retaining child task status after + the child task exists. This is behavior required by POSIX. + But in NuttX is only enabled with CONFIG_SCHED_HAVE_PARENT and + CONFIG_SCHED_CHILD_STATUS diff --git a/nuttx/Documentation/NuttxPortingGuide.html b/nuttx/Documentation/NuttxPortingGuide.html index fec7106b0..fd358f423 100644 --- a/nuttx/Documentation/NuttxPortingGuide.html +++ b/nuttx/Documentation/NuttxPortingGuide.html @@ -12,7 +12,7 @@ <h1><big><font color="#3c34ec"> <i>NuttX RTOS Porting Guide</i> </font></big></h1> - <p>Last Updated: January 13, 2013</p> + <p>Last Updated: January 23, 2013</p> </td> </tr> </table> @@ -4481,12 +4481,74 @@ build instrumentation is selected. Set to zero to disable. </li> <li> - <code>CONFIG_SCHED_HAVE_PARENT</code>: Remember the ID of the parent thread when a new child thread is created. + <code>CONFIG_SCHED_HAVE_PARENT</code>: Remember the ID of the parent thread when a new child task is created. This support enables some additional features (such as <code>SIGCHLD</code>) and modifies the behavior of other interfaces. For example, it makes <code>waitpid()</code> more standards complete by restricting the waited-for tasks to the children of the caller. Default: disabled. </li> <li> + <code>CONFIG_SCHED_CHILD_STATUS</code>: If this option is selected, then the exit status of the child task will be retained after the child task exits. + This option should be selected if you require knowledge of a child process' exit status. + Without this setting, <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code> may fail. + For example, if you do: + <p><ol> + <li> + Start child task + </li> + <li> + Wait for exit status (using <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code>). + </li> + </ol></p> + <p> + This can fail because the child task may run to completion before the wait begins. + There is a non-standard work-around in this case: + The above sequence will work if you disable pre-emption using <code>sched_lock()</code> prior to starting the child task, then re-enable pre-emption with <code>sched_unlock()</code> after the wait completes. + This works because the child task is not permitted to run until the wait is in place. + </p> + <p> + The standard solution would be to enable <code>CONFIG_SCHED_CHILD_STATUS</code>. + In this case the exit status of the child task is retained after the child exits and the wait will successful obtain the child task's exit status whether it is called before the child task exits or not. + </p> + <p> + <b>Warning</b>: + If you enable this feature, then your application must either (1) take responsibility for reaping the child status with <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code>, or (2) suppress retention of child status. + If you do not reap the child status, then you have a memory leak and your system will eventually fail. + </p> + Retention of child status can be suppressed on the parent using logic like: + </p> + <ul><pre> +struct sigaction sa; + +sa.sa_handler = SIG_IGN; +sa.sa_flags = SA_NOCLDWAIT; +int ret = sigaction(SIGCHLD, &sa, NULL); +</pre></ul> + </li> + <li> + <code>CONFIG_PREALLOC_CHILDSTATUS</code>: To prevent runaway child status allocations and to improve + allocation performance, child task exit status structures are pre-allocated when the system boots. + This setting determines the number of child status structures that will be pre-allocated. + If this setting is not defined or if it is defined to be zero then a value of 2*<code>MAX_TASKS</code> is used. + <p> + Note that there cannot be more that <code>CONFIG_MAX_TASKS</code> tasks in total. + However, the number of child status structures may need to be significantly larger because this number includes the maximum number of tasks that are running PLUS the number of tasks that have exit'ed without having their exit status reaped (via <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code>). + </p> + <p> + Obviously, if tasks spawn children indefinitely and never have the exit status reaped, then you may have a memory leak! + If you enable the <code>SCHED_CHILD_STATUS</code> feature, then your application must take responsibility for either (1) reaping the child status with <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code> or it must (2) suppress retention of child status. Otherwise, your system will eventually fail. + </p> + <p> + Retention of child status can be suppressed on the parent using logic like: + </p> + <ul><pre> +struct sigaction sa; + +sa.sa_handler = SIG_IGN; +sa.sa_flags = SA_NOCLDWAIT; +int ret = sigaction(SIGCHLD, &sa, NULL); +</pre></ul> + </li> + <li> <code>CONFIG_SYSTEM_TIME16</code>: The range of system time is, by default, 32-bits. However, if the MCU supports type <code>long long</code> and <code>CONFIG_SYSTEM_TIME16</code> is selected, diff --git a/nuttx/Documentation/NuttxUserGuide.html b/nuttx/Documentation/NuttxUserGuide.html index 3cfb63f11..10e5eb7ba 100644 --- a/nuttx/Documentation/NuttxUserGuide.html +++ b/nuttx/Documentation/NuttxUserGuide.html @@ -13,7 +13,7 @@ <h1><big><font color="#3c34ec"><i>NuttX Operating System<p>User's Manual</i></font></big></h1> <p><small>by</small></p> <p>Gregory Nutt<p> - <p>Last Updated: January 13, 2013</p> + <p>Last Updated: January 23, 2013</p> </td> </tr> </table> @@ -1767,20 +1767,114 @@ priority of the calling task is returned. </tr> </table> -<p>Scheduler locking interfaces</p> +<p> + <b>Task Control Interfaces</b>. +</p> <ul> - <li><a href="#schedlock">2.3.1 sched_lock</a></li> - <li><a href="#schedunlock">2.3.2 sched_unlock</a></li> - <li><a href="#schedlockcount">2.3.3 sched_lockcount</a></li> + <li> + <p> + <b>Scheduler locking interfaces</b>. + This <i>non-standard</i> interfaces are used to enable and disable pre-emption and to test is pre-emption is currently enabled. + </p> + <ul> + <li><a href="#schedlock">2.3.1 sched_lock</a></li> + <li><a href="#schedunlock">2.3.2 sched_unlock</a></li> + <li><a href="#schedlockcount">2.3.3 sched_lockcount</a></li> + </ul> + </li> + <li> + <p> + <b>Task synchronization interfaces</b>. + <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code> may be used to wait for termination of child tasks. + </p> + <ul> + <li><a href="#waitpid">2.3.4 waitpid</a></li> + <li><a href="#waitid">2.3.5 waitid</a></li> + <li><a href="#wait">2.3.6 wait</a></li> + </ul> + <p> + <code>atexit()</code> and <code>on_exit()</code> may be use to register callback functions that are executed when a task exits. + </p> + <ul> + <li><a href="#atexit">2.3.7 atexit</a></li> + <li><a href="#onexit">2.3.8 on_exit</a></li> + </ul> + </li> </ul> -<p>Task synchronization interfaces</p> + +<p> + <b>Parent and Child Tasks</b>. + The task synchronization interfaces historically depend upon parent and child relationships between tasks. + But default, NuttX does not use any parent/child knowledge. + However, there are three important configuration options that can change that. +</p> <ul> - <li><a href="#waitpid">2.3.4 waitpid</a></li> - <li><a href="#waitid">2.3.5 waitid</a></li> - <li><a href="#wait">2.3.6 wait</a></li> - <li><a href="#atexit">2.3.7 atexit</a></li> - <li><a href="#onexit">2.3.8 on_exit</a></li> + <li> + <p> + <b><code>CONFIG_SCHED_HAVE_PARENT</code></b>. + If this setting is defined, then it instructs NuttX to remember the task ID of the parent task when each new child task is created. + This support enables some additional features (such as <code>SIGCHLD</code>) and modifies the behavior of other interfaces. + For example, it makes <code>waitpid()</code> more standards complete by restricting the waited-for tasks to the children of the caller. + </p> + </li> + <li> + <p> + <b><code>CONFIG_SCHED_CHILD_STATUS</code></b> + If this option is selected, then the exit status of the child task will be retained after the child task exits. + This option should be selected if you require knowledge of a child process' exit status. + Without this setting, <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code> may fail. + For example, if you do: + </p> + <ol> + <li> + Start child task + </li> + <li> + Wait for exit status (using <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code>). + </li> + </ol> + <p> + This may fail because the child task may run to completion before the wait begins. + There is a non-standard work-around in this case: + The above sequence will work if you disable pre-emption using <code>sched_lock()</code> prior to starting the child task, then re-enable pre-emption with <code>sched_unlock()</code> after the wait completes. + This works because the child task is not permitted to run until the wait is in place. + </p> + <p> + The standard solution would be to enable <code>CONFIG_SCHED_CHILD_STATUS</code>. + In this case the exit status of the child task is retained after the child exits and the wait will successful obtain the child task's exit status whether it is called before the child task exits or not. + </p> + </li> + <li> + <p> + <b><code>CONFIG_PREALLOC_CHILDSTATUS</code></b>. + To prevent runaway child status allocations and to improve allocation performance, child task exit status structures are pre-allocated when the system boots. + This setting determines the number of child status structures that will be pre-allocated. + If this setting is not defined or if it is defined to be zero then a value of 2*<code>MAX_TASKS</code> is used. + </p> + <p> + Note that there cannot be more that <code>CONFIG_MAX_TASKS</code> tasks in total. + However, the number of child status structures may need to be significantly larger because this number includes the maximum number of tasks that are running PLUS the number of tasks that have exit'ed without having their exit status reaped (via <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code>). + </p> + <p> + Obviously, if tasks spawn children indefinitely and never have the exit status reaped, then you may have a memory leak! + (See <b>Warning</b> below) + </p> + </li> </ul> +<p> + <b>Warning</b>: + If you enable the <code>CONFIG_SCHED_CHILD_STATUS</code> feature, then your application must either (1) take responsibility for reaping the child status with <code>wait()</code>, <code>waitpid()</code> or <code>waitid()</code>, or (2) suppress retention of child status. + If you do not reap the child status, then you have a memory leak and your system will eventually fail. +</p> + Retention of child status can be suppressed on the parent using logic like: +</p> +<ul><pre> +struct sigaction sa; + +sa.sa_handler = SIG_IGN; +sa.sa_flags = SA_NOCLDWAIT; +int ret = sigaction(SIGCHLD, &sa, NULL); +</pre></ul> <H3><a name="schedlock">2.3.1 sched_lock</a></H3> @@ -4589,10 +4683,14 @@ sigaction(). interface of the same name. Differences from the POSIX implementation include: <ul> -<li>Special values of sa_handler in the struct sigaction act input -not handled (SIG_DFL, SIG_IGN). -<li>All sa_flags in struct sigaction of act input are ignored -(all treated like SA_SIGINFO). + <li> + There are no default actions so the special value <code>SIG_DFL</code> is treated like <code>SIG_IGN</code>. + </li> + <li> + All <code>sa_flags</code> in struct sigaction of act input are ignored (all treated like <code>SA_SIGINFO</code>). + The one exception is if <code>CONFIG_SCHED_CHILDSTATUS</code> is defined; + then <code>SA_NOCLDWAIT</code> is supported but only for <code>SIGCHLD</code>. + </li> </ul> <H3><a name="sigprocmask">2.8.7 sigprocmask</a></H3> diff --git a/nuttx/arch/arm/src/stm32/Kconfig b/nuttx/arch/arm/src/stm32/Kconfig index 85cdebd35..41724be2d 100644 --- a/nuttx/arch/arm/src/stm32/Kconfig +++ b/nuttx/arch/arm/src/stm32/Kconfig @@ -1810,7 +1810,7 @@ config STM32_TIM14_DAC2 endchoice -bool STM32_USART +config STM32_USART bool menu "U[S]ART Configuration" diff --git a/nuttx/configs/README.txt b/nuttx/configs/README.txt index babdf7330..1b78567a3 100644 --- a/nuttx/configs/README.txt +++ b/nuttx/configs/README.txt @@ -334,12 +334,79 @@ defconfig -- This is a configuration file similar to the Linux CONFIG_TASK_NAME_SIZE - Specifies that maximum size of a task name to save in the TCB. Useful if scheduler instrumentation is selected. Set to zero to disable. - CONFIG_SCHED_HAVE_PARENT - Remember the ID of the parent thread - when a new child thread is created. This support enables some + CONFIG_SCHED_HAVE_PARENT - Remember the ID of the parent task + when a new child task is created. This support enables some additional features (such as SIGCHLD) and modifies the behavior of other interfaces. For example, it makes waitpid() more standards complete by restricting the waited-for tasks to the children of the caller. Default: disabled. + CONFIG_SCHED_CHILD_STATUS + If this option is selected, then the exit status of the child task + will be retained after the child task exits. This option should be + selected if you require knowledge of a child process' exit status. + Without this setting, wait(), waitpid() or waitid() may fail. For + example, if you do: + + 1) Start child task + 2) Wait for exit status (using wait(), waitpid(), or waitid()). + + This can fail because the child task may run to completion before + the wait begins. There is a non-standard work-around in this case: + The above sequence will work if you disable pre-emption using + sched_lock() prior to starting the child task, then re-enable pre- + emption with sched_unlock() after the wait completes. This works + because the child task is not permitted to run until the wait is in + place. + + The standard solution would be to enable CONFIG_SCHED_CHILD_STATUS. In + this case the exit status of the child task is retained after the + child exits and the wait will successful obtain the child task's + exit status whether it is called before the child task exits or not. + + Warning: If you enable this feature, then your application must + either (1) take responsibility for reaping the child status with wait(), + waitpid(), or waitid(), or (2) suppress retention of child status. + If you do not reap the child status, then you have a memory leak and + your system will eventually fail. + + Retention of child status can be suppressed on the parent using logic like: + + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT; + int ret = sigaction(SIGCHLD, &sa, NULL); + + CONFIG_PREALLOC_CHILDSTATUS + To prevent runaway child status allocations and to improve + allocation performance, child task exit status structures are pre- + allocated when the system boots. This setting determines the number + of child status structures that will be pre-allocated. If this + setting is not defined or if it is defined to be zero then a value + of 2*MAX_TASKS is used. + + Note that there cannot be more that CONFIG_MAX_TASKS tasks in total. + However, the number of child status structures may need to be + significantly larger because this number includes the maximum number + of tasks that are running PLUS the number of tasks that have exit'ed + without having their exit status reaped (via wait(), waitid(), or + waitpid()). + + Obviously, if tasks spawn children indefinitely and never have the + exit status reaped, then you may have a memory leak! If you enable + the SCHED_CHILD_STATUS feature, then your application must take + responsibility for either (1) reaping the child status with wait(), + waitpid(), or waitid() or it must (2) suppress retention of child + status. Otherwise, your system will eventually fail. + + Retention of child status can be suppressed on the parent using logic like: + + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT; + int ret = sigaction(SIGCHLD, &sa, NULL); + CONFIG_START_YEAR, CONFIG_START_MONTH, CONFIG_START_DAY - Used to initialize the internal time logic. CONFIG_GREGORIAN_TIME - Enables Gregorian time conversions. diff --git a/nuttx/configs/sim/ostest/defconfig b/nuttx/configs/sim/ostest/defconfig index c8d5c501d..65f5330fc 100644 --- a/nuttx/configs/sim/ostest/defconfig +++ b/nuttx/configs/sim/ostest/defconfig @@ -83,9 +83,14 @@ CONFIG_BOARD_LOOPSPERMSEC=100 # CONFIG_SIM_WALLTIME is not set # +# External Memory Configuration +# + +# # Architecture Options # # CONFIG_ARCH_NOINTC is not set +# CONFIG_ARCH_VECNOTIRQ is not set # CONFIG_ARCH_DMA is not set # CONFIG_ARCH_IRQPRIO is not set # CONFIG_CUSTOM_STACK is not set @@ -93,6 +98,7 @@ CONFIG_BOARD_LOOPSPERMSEC=100 # CONFIG_ARCH_HAVE_VFORK is not set # CONFIG_ARCH_STACKDUMP is not set # CONFIG_ENDIAN_BIG is not set +# CONFIG_ARCH_HAVE_RAMFUNCS is not set # # Board Settings @@ -132,6 +138,7 @@ CONFIG_RR_INTERVAL=0 # CONFIG_SCHED_INSTRUMENTATION is not set CONFIG_TASK_NAME_SIZE=32 CONFIG_SCHED_HAVE_PARENT=y +# CONFIG_SCHED_CHILD_STATUS is not set # CONFIG_JULIAN_TIME is not set CONFIG_START_YEAR=2007 CONFIG_START_MONTH=2 @@ -242,8 +249,8 @@ CONFIG_SERIAL=y # # File system configuration # -# CONFIG_FS_FAT is not set # CONFIG_FS_RAMMAP is not set +# CONFIG_FS_FAT is not set # CONFIG_FS_NXFFS is not set # CONFIG_FS_ROMFS is not set @@ -271,6 +278,7 @@ CONFIG_MM_REGIONS=1 # CONFIG_BINFMT_EXEPATH is not set # CONFIG_NXFLAT is not set # CONFIG_ELF is not set +# CONFIG_BUILTIN is not set # CONFIG_PIC is not set # CONFIG_SYMTAB_ORDEREDBYNAME is not set @@ -318,7 +326,6 @@ CONFIG_LIB_SENDFILE_BUFSIZE=512 # # Built-In Applications # -# CONFIG_BUILTIN is not set # # Examples diff --git a/nuttx/include/nuttx/sched.h b/nuttx/include/nuttx/sched.h index 9ab96b7d2..1e75b5020 100644 --- a/nuttx/include/nuttx/sched.h +++ b/nuttx/include/nuttx/sched.h @@ -74,6 +74,7 @@ #define TCB_FLAG_NONCANCELABLE (1 << 2) /* Bit 2: Pthread is non-cancelable */ #define TCB_FLAG_CANCEL_PENDING (1 << 3) /* Bit 3: Pthread cancel is pending */ #define TCB_FLAG_ROUND_ROBIN (1 << 4) /* Bit 4: Round robin sched enabled */ +#define TCB_FLAG_NOCLDWAIT (1 << 5) /* Bit 5: Do not retain child exit status */ /* Values for struct child_status_s ch_flags */ @@ -183,7 +184,7 @@ struct child_status_s FAR struct child_status_s *flink; uint8_t ch_flags; /* Child status: See CHILD_FLAG_* definitions */ - pid_y ch_pid; /* Child task ID */ + pid_t ch_pid; /* Child task ID */ int ch_status; /* Child exit status */ }; #endif diff --git a/nuttx/include/signal.h b/nuttx/include/signal.h index 7c6b4cd55..a02170242 100644 --- a/nuttx/include/signal.h +++ b/nuttx/include/signal.h @@ -129,11 +129,13 @@ /* struct sigaction flag values */ -#define SA_NOCLDSTOP 1 /* Do not generate SIGCHILD when - * children stop (ignored) */ -#define SA_SIGINFO 2 /* Invoke the signal-catching function - * with 3 args instead of 1 - * (always assumed) */ +#define SA_NOCLDSTOP (1 << 0) /* Do not generate SIGCHILD when + * children stop (ignored) */ +#define SA_SIGINFO (1 << 1) /* Invoke the signal-catching function + * with 3 args instead of 1 + * (always assumed) */ +#define SA_NOCLDWAIT (1 << 2) /* If signo=SIGCHLD, exit status of child + * processes will be discarded */ /* These are the possible values of the signfo si_code field */ diff --git a/nuttx/include/sys/types.h b/nuttx/include/sys/types.h index 95feee72e..38f091e8a 100644 --- a/nuttx/include/sys/types.h +++ b/nuttx/include/sys/types.h @@ -158,13 +158,13 @@ typedef uint16_t ino_t; * negative PID values are used to represent invalid PIDs. */ -typedef int pid_t; +typedef int16_t pid_t; /* id_t is a general identifier that can be used to contain at least a pid_t, * uid_t, or gid_t. */ -typedef unsigned int id_t; +typedef int16_t id_t; /* Signed integral type of the result of subtracting two pointers */ diff --git a/nuttx/sched/Kconfig b/nuttx/sched/Kconfig index 11d74b583..fe9a88085 100644 --- a/nuttx/sched/Kconfig +++ b/nuttx/sched/Kconfig @@ -42,13 +42,88 @@ config SCHED_HAVE_PARENT bool "Support parent/child task relationships" default n ---help--- - Remember the ID of the parent thread when a new child thread is + Remember the ID of the parent task when a new child task is created. This support enables some additional features (such as SIGCHLD) and modifies the behavior of other interfaces. For example, it makes waitpid() more standards complete by restricting the waited-for tasks to the children of the caller. Default: disabled. +config SCHED_CHILD_STATUS + bool "Retain child exit status" + default n + depends on SCHED_HAVE_PARENT + ---help--- + If this option is selected, then the exit status of the child task + will be retained after the child task exits. This option should be + selected if you require knowledge of a child process' exit status. + Without this setting, wait(), waitpid() or waitid() may fail. For + example, if you do: + + 1) Start child task + 2) Wait for exit status (using wait(), waitpid(), or waitid()). + + This can fail because the child task may run to completion before + the wait begins. There is a non-standard work-around in this case: + The above sequence will work if you disable pre-emption using + sched_lock() prior to starting the child task, then re-enable pre- + emption with sched_unlock() after the wait completes. This works + because the child task is not permitted to run until the wait is in + place. + + The standard solution would be to enable SCHED_CHILD_STATUS. In + this case the exit status of the child task is retained after the + child exits and the wait will successful obtain the child task's + exit status whether it is called before the child task exits or not. + + Warning: If you enable this feature, then your application must + either (1) take responsibility for reaping the child status with wait(), + waitpid(), or waitid(), or (2) suppress retention of child status. + If you do not reap the child status, then you have a memory leak and + your system will eventually fail. + + Retention of child status can be suppressed on the parent using logic like: + + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT; + int ret = sigaction(SIGCHLD, &sa, NULL); + +config PREALLOC_CHILDSTATUS + int "Number of pre-allocated child status" + default 0 + depends on SCHED_CHILD_STATUS + ---help--- + To prevent runaway child status allocations and to improve + allocation performance, child task exit status structures are pre- + allocated when the system boots. This setting determines the number + of child status structures that will be pre-allocated. If this + setting is not defined or if it is defined to be zero then a value + of 2*MAX_TASKS is used. + + Note that there cannot be more that CONFIG_MAX_TASKS tasks in total. + However, the number of child status structures may need to be + significantly larger because this number includes the maximum number + of tasks that are running PLUS the number of tasks that have exit'ed + without having their exit status reaped (via wait(), waitid(), or + waitpid()). + + Obviously, if tasks spawn children indefinitely and never have the + exit status reaped, then you may have a memory leak! If you enable + the SCHED_CHILD_STATUS feature, then your application must take + responsibility for either (1) reaping the child status with wait(), + waitpid(), or waitid() or it must (2) suppress retention of child + status. Otherwise, your system will eventually fail. + + Retention of child status can be suppressed on the parent using logic like: + + struct sigaction sa; + + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT; + int ret = sigaction(SIGCHLD, &sa, NULL); + config JULIAN_TIME bool "Enables Julian time conversions" default n @@ -88,7 +163,7 @@ config PRIORITY_INHERITANCE Set to enable support for priority inheritance on mutexes and semaphores. config SEM_PREALLOCHOLDERS - int "Pre-allocated holders" + int "Number of pre-allocated holders" default 16 depends on PRIORITY_INHERITANCE ---help--- diff --git a/nuttx/sched/os_internal.h b/nuttx/sched/os_internal.h index b048f00a8..7d5095bad 100644 --- a/nuttx/sched/os_internal.h +++ b/nuttx/sched/os_internal.h @@ -264,17 +264,16 @@ extern const tasklist_t g_tasklisttable[NUM_TASK_STATES]; int os_bringup(void); void task_start(void); int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, - main_t main); + main_t main, uint8_t ttype); int task_argsetup(FAR _TCB *tcb, FAR const char *name, FAR const char *argv[]); void task_exithook(FAR _TCB *tcb, int status); int task_deletecurrent(void); #ifdef CONFIG_SCHED_HAVE_PARENT #ifdef CONFIG_SCHED_CHILD_STATUS -void weak_functiontask_initialize(void); +void weak_function task_initialize(void); FAR struct child_status_s *task_allocchild(void); void task_freechild(FAR struct child_status_s *status); -FAR struct child_status_s *task_addchild(FAR _TCB *tcb, pid_t pid, int status, - uint8_t flags); +void task_addchild(FAR _TCB *tcb, FAR struct child_status_s *child); FAR struct child_status_s *task_findchild(FAR _TCB *tcb, pid_t pid); FAR struct child_status_s *task_removechild(FAR _TCB *tcb, pid_t pid); void task_removechildren(FAR _TCB *tcb); diff --git a/nuttx/sched/os_start.c b/nuttx/sched/os_start.c index cb6a2c869..a6d4e83b9 100644 --- a/nuttx/sched/os_start.c +++ b/nuttx/sched/os_start.c @@ -286,7 +286,7 @@ void os_start(void) /* Initialize the processor-specific portion of the TCB */ - g_idletcb.flags = TCB_FLAG_TTYPE_KERNEL; + g_idletcb.flags = (TCB_FLAG_TTYPE_KERNEL | TCB_FLAG_NOCLDWAIT); up_initial_state(&g_idletcb); /* Initialize the semaphore facility(if in link). This has to be done diff --git a/nuttx/sched/pthread_create.c b/nuttx/sched/pthread_create.c index dc2db2916..f4d0d8fdf 100644 --- a/nuttx/sched/pthread_create.c +++ b/nuttx/sched/pthread_create.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/pthread_create.c * - * Copyright (C) 2007-2009, 2011 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 @@ -354,15 +354,10 @@ int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr, #endif } - /* Mark this task as a pthread (this setting will be needed in - * task_schedsetup() when up_initial_state() is called. - */ - - ptcb->flags |= TCB_FLAG_TTYPE_PTHREAD; - /* Initialize the task control block */ - ret = task_schedsetup(ptcb, priority, pthread_start, (main_t)start_routine); + ret = task_schedsetup(ptcb, priority, pthread_start, (main_t)start_routine, + TCB_FLAG_TTYPE_PTHREAD); if (ret != OK) { sched_releasetcb(ptcb); diff --git a/nuttx/sched/sched_waitid.c b/nuttx/sched/sched_waitid.c index a3f7221df..37ee26ce0 100644 --- a/nuttx/sched/sched_waitid.c +++ b/nuttx/sched/sched_waitid.c @@ -159,8 +159,29 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) * TCB is actually a child of this task. */ +#ifdef CONFIG_SCHED_CHILD_STATUS + if (rtcb->children == NULL) + { + /* There are no children */ + + err = ECHILD; + goto errout_with_errno; + } + else if (idtype == P_PID) + { + if (task_findchild(rtcb, (pid_t)id) == NULL) + { + /* This specific pid is not a child */ + + err = ECHILD; + goto errout_with_errno; + } + } +#else if (rtcb->nchildren == 0) { + /* There are no children */ + err = ECHILD; goto errout_with_errno; } @@ -175,11 +196,64 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) goto errout_with_errno; } } +#endif /* Loop until the child that we are waiting for dies */ for (;;) { +#ifdef CONFIG_SCHED_CHILD_STATUS + /* Check if the task has already died. Signals are not queued in + * NuttX. So a possibility is that the child has died and we + * missed the death of child signal (we got some other signal + * instead). + */ + + DEBUGASSERT(rtcb->children); + if (rtcb->children == NULL) + { + /* This should not happen. I am just wasting your FLASH. */ + + err = ECHILD; + goto errout_with_errno; + } + else if (idtype == P_PID) + { + FAR struct child_status_s *child; + + /* We are waiting for a specific PID. Get the current status + * of the child task. + */ + + child = task_findchild(rtcb, (pid_t)id); + DEBUGASSERT(child); + if (!child) + { + /* Yikes! The child status entry just disappeared! */ + + err = ECHILD; + goto errout_with_errno; + } + + /* Did the child exit? */ + + if ((child->ch_flags & CHILD_FLAG_EXITED) != 0) + { + /* The child has exited. Return the saved exit status */ + + info->si_signo = SIGCHLD; + info->si_code = CLD_EXITED; + info->si_value.sival_ptr = NULL; + info->si_pid = (pid_t)id; + info->si_status = child->ch_status; + + /* Discard the child entry and break out of the loop */ + + (void)task_removechild(rtcb, (pid_t)id); + task_freechild(child); + } + } +#else /* Check if the task has already died. Signals are not queued in * NuttX. So a possibility is that the child has died and we * missed the death of child signal (we got some other signal @@ -197,6 +271,7 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options) err = EINTR; goto errout_with_errno; } +#endif /* Wait for any death-of-child signal */ diff --git a/nuttx/sched/sched_waitpid.c b/nuttx/sched/sched_waitpid.c index dc715b2e9..fe3f7167d 100644 --- a/nuttx/sched/sched_waitpid.c +++ b/nuttx/sched/sched_waitpid.c @@ -274,6 +274,9 @@ errout: pid_t waitpid(pid_t pid, int *stat_loc, int options) { FAR _TCB *rtcb = (FAR _TCB *)g_readytorun.head; +#ifdef CONFIG_SCHED_CHILD_STATUS + FAR struct child_status_s *child; +#endif FAR struct siginfo info; sigset_t sigset; int err; @@ -300,12 +303,33 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) sched_lock(); - /* Verify that this task actually has children and that the the requeste + /* Verify that this task actually has children and that the the request * TCB is actually a child of this task. */ +#ifdef CONFIG_SCHED_CHILD_STATUS + if (rtcb->children == NULL) + { + /* There are no children */ + + err = ECHILD; + goto errout_with_errno; + } + else if (pid != (pid_t)-1) + { + /* This specific pid is not a child */ + + if (task_findchild(rtcb, pid) == NULL) + { + err = ECHILD; + goto errout_with_errno; + } + } +#else if (rtcb->nchildren == 0) { + /* There are no children */ + err = ECHILD; goto errout_with_errno; } @@ -320,6 +344,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) goto errout_with_errno; } } +#endif /* Loop until the child that we are waiting for dies */ @@ -337,7 +362,12 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) * chilren. */ +#ifdef CONFIG_SCHED_CHILD_STATUS + DEBUGASSERT(rtcb->children); + if (rtcb->children == NULL) +#else if (rtcb->nchildren == 0) +#endif { /* There were one or more children when we started so they * must have exit'ed. There are just no bread crumbs left @@ -351,6 +381,35 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) } else { +#ifdef CONFIG_SCHED_CHILD_STATUS + /* We are waiting for a specific PID. Get the current status + * of the child task. + */ + + child = task_findchild(rtcb, pid); + DEBUGASSERT(child); + if (!child) + { + /* Yikes! The child status entry just disappeared! */ + + err = ECHILD; + goto errout_with_errno; + } + + /* Did the child exit? */ + + if ((child->ch_flags & CHILD_FLAG_EXITED) != 0) + { + /* The child has exited. Return the saved exit status */ + + *stat_loc = child->ch_status; + + /* Discard the child entry and break out of the loop */ + + (void)task_removechild(rtcb, pid); + task_freechild(child); + } +#else /* We are waiting for a specific PID. We can use kill() with * signal number 0 to determine if that task is still alive. */ @@ -368,6 +427,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options) err = ECHILD; goto errout_with_errno; } +#endif } /* Wait for any death-of-child signal */ diff --git a/nuttx/sched/sig_action.c b/nuttx/sched/sig_action.c index fef5f1558..708667993 100644 --- a/nuttx/sched/sig_action.c +++ b/nuttx/sched/sig_action.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/sig_action.c * - * Copyright (C) 2007-2009 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -43,6 +43,7 @@ #include <signal.h> #include <queue.h> #include <sched.h> +#include <errno.h> #include "os_internal.h" #include "sig_internal.h" @@ -156,10 +157,11 @@ static FAR sigactq_t *sig_allocateaction(void) * Assumptions: * * POSIX Compatibility: - * - Special values of sa_handler in the struct sigaction - * act input not handled (SIG_DFL, SIG_IGN). - * - All sa_flags in struct sigaction of act input are - * ignored (all treated like SA_SIGINFO). + * - There are no default actions so the special value SIG_DFL is treated + * like SIG_IGN. + * - All sa_flags in struct sigaction of act input are ignored (all + * treated like SA_SIGINFO). The one exception is if CONFIG_SCHED_CHILDSTATUS + * is defined; then SA_NOCLDWAIT is supported but only for SIGCHLD * ****************************************************************************/ @@ -167,90 +169,129 @@ int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction * { FAR _TCB *rtcb = (FAR _TCB*)g_readytorun.head; FAR sigactq_t *sigact; - int ret = ERROR; /* Assume failure */ + int ret; /* Since sigactions can only be installed from the running thread of * execution, no special precautions should be necessary. */ - /* Verify the signal */ + /* Verify the signal number */ - if (GOOD_SIGNO(signo)) + if (!GOOD_SIGNO(signo)) { - ret = OK; /* Assume success */ + set_errno(EINVAL); + return ERROR; + } - /* Find the signal in the sigactionq */ + /* Find the signal in the sigactionq */ - sigact = sig_findaction(rtcb, signo); + sigact = sig_findaction(rtcb, signo); - /* Return the old sigaction value if so requested */ + /* Return the old sigaction value if so requested */ - if (oact) + if (oact) + { + if (sigact) { - if (sigact) - { - COPY_SIGACTION(oact, &sigact->act); - } - else - { - /* There isn't an old value */ - - oact->sa_u._sa_handler = NULL; - oact->sa_mask = NULL_SIGNAL_SET; - oact->sa_flags = 0; - } + COPY_SIGACTION(oact, &sigact->act); + } + else + { + /* There isn't an old value */ + + oact->sa_u._sa_handler = NULL; + oact->sa_mask = NULL_SIGNAL_SET; + oact->sa_flags = 0; } + } + + /* If the argument act is a null pointer, signal handling is unchanged; + * thus, the call can be used to enquire about the current handling of + * a given signal. + */ - /* If no sigaction was found, but one is needed, then - * allocate one. + if (!act) + { + return OK; + } + +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) + + /* Handle a special case. Retention of child status can be suppressed + * if signo == SIGCHLD and sa_flags == SA_NOCLDWAIT. + * + * POSIX.1 leaves it unspecified whether a SIGCHLD signal is generated + * when a child process terminates. In NuttX, a SIGCHLD signal is + * generated in this case; but in some other implementations, it may not + * be. + */ + + if (signo == SIGCHLD && (act->sa_flags & SA_NOCLDWAIT) != 0) + { + irqstate_t flags; + + /* We do require a critical section to muck with the TCB values that + * can be modified by the child thread. */ - if (!sigact && act && act->sa_u._sa_handler) - { - sigact = sig_allocateaction(); + flags = irqsave(); - /* An error has occurred if we could not allocate the sigaction */ + /* Mark that status should be not be retained */ - if (!sigact) - { - ret = ERROR; - } - else - { - /* Put the signal number in the queue entry */ + rtcb->flags |= TCB_FLAG_NOCLDWAIT; - sigact->signo = (uint8_t)signo; + /* Free all pending exit status */ - /* Add the new sigaction to sigactionq */ + task_removechildren(rtcb); + irqrestore(flags); + } +#endif - sq_addlast((FAR sq_entry_t*)sigact, &rtcb->sigactionq); - } - } + /* Handle the case where no sigaction is supplied (SIG_IGN) */ + + if (act->sa_u._sa_handler == SIG_IGN) + { + /* If there is a old sigaction, remove it from sigactionq */ + + sq_rem((FAR sq_entry_t*)sigact, &rtcb->sigactionq); + + /* And deallocate it */ + + sig_releaseaction(sigact); + } - /* Set the new sigaction if so requested */ + /* A sigaction has been supplied */ - if ((sigact) && (act)) + else + { + /* Check if a sigaction was found */ + + if (!sigact) { - /* Check if it is a request to install a new handler */ + /* No sigaction was found, but one is needed. Allocate one. */ - if (act->sa_u._sa_handler) - { - COPY_SIGACTION(&sigact->act, act); - } + sigact = sig_allocateaction(); - /* No.. It is a request to remove the old handler */ + /* An error has occurred if we could not allocate the sigaction */ - else - { - /* Remove the old sigaction from sigactionq */ + if (!sigact) + { + set_errno(ENOMEM); + return ERROR; + } + + /* Put the signal number in the queue entry */ - sq_rem((FAR sq_entry_t*)sigact, &rtcb->sigactionq); + sigact->signo = (uint8_t)signo; - /* And deallocate it */ + /* Add the new sigaction to sigactionq */ - sig_releaseaction(sigact); - } + sq_addlast((FAR sq_entry_t*)sigact, &rtcb->sigactionq); } + + /* Set the new sigaction */ + + COPY_SIGACTION(&sigact->act, act); } return ret; diff --git a/nuttx/sched/task_childstatus.c b/nuttx/sched/task_childstatus.c index ab5ace43a..0f6d36c29 100644 --- a/nuttx/sched/task_childstatus.c +++ b/nuttx/sched/task_childstatus.c @@ -39,24 +39,44 @@ #include <nuttx/config.h> +#include <sched.h> #include <errno.h> +#include <debug.h> #include "os_internal.h" #if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) /***************************************************************************** - * Private Types + * Pre-processor Definitions *****************************************************************************/ -/* Globals are maintained in a structure to minimize name collisions. Note - * that there cannot be more that CONFIG_MAX_TASKS tasks in total. So using - * CONFIG_MAX_TASKS should be sufficient (at least one task, the IDLE thread, - * will have no parent). +/* Note that there cannot be more that CONFIG_MAX_TASKS tasks in total. + * However, the number of child status structures may need to be significantly + * larger because this number includes the maximum number of tasks that are + * running PLUS the number of tasks that have exit'ed without having their + * exit status reaped (via wait(), waitid(), or waitpid()). + * + * Obviously, if tasks spawn children indefinitely and never have the exit + * status reaped, then you have a memory leak! */ +#if !defined(CONFIG_PREALLOC_CHILDSTATUS) || CONFIG_PREALLOC_CHILDSTATUS == 0 +# undef CONFIG_PREALLOC_CHILDSTATUS +# define CONFIG_PREALLOC_CHILDSTATUS (2*CONFIG_MAX_TASKS) +#endif + +#ifndef CONFIG_DEBUG +# undef CONFIG_DEBUG_CHILDSTATUS +#endif + +/***************************************************************************** + * Private Types + *****************************************************************************/ +/* Globals are maintained in a structure to minimize name collisions. */ + struct child_pool_s { - struct child_status_s alloc[CONFIG_MAX_TASKS]; + struct child_status_s alloc[CONFIG_PREALLOC_CHILDSTATUS]; FAR struct child_status_s *freelist; }; @@ -71,6 +91,40 @@ static struct child_pool_s g_child_pool; *****************************************************************************/ /***************************************************************************** + * Name: task_dumpchildren + * + * Description: + * Dump all of the children when the part TCB list is modified. + * + * Parameters: + * tcb - The parent TCB. + * + * Return Value: + * None. + * + * Assumptions: + * Called early in initialization. No special precautions are required. + * + *****************************************************************************/ + +#ifdef CONFIG_DEBUG_CHILDSTATUS +static void task_dumpchildren(FAR _TCB *tcb, FAR const char *msg) +{ + FAR struct child_status_s *child; + int i; + + dbg("Parent TCB=%p: %s\n", tcb, msg); + for (i = 0, child = tcb->children; child; i++, child = child->flink) + { + dbg(" %d. ch_flags=%02x ch_pid=%d ch_status=%d\n", + i, child->ch_flags, child->ch_pid, child->ch_status); + } +} +#else +# task_dumpchildren(t,m) +#endif + +/***************************************************************************** * Public Functions *****************************************************************************/ @@ -88,7 +142,7 @@ static struct child_pool_s g_child_pool; * None. * * Assumptions: - * Called early in initializatin. No special precautions are required. + * Called early in initialization. No special precautions are required. * *****************************************************************************/ @@ -102,11 +156,11 @@ void task_initialize(void) prev = &g_child_pool.alloc[0]; g_child_pool.freelist = prev; - for (i = 0; i < CONFIG_MAX_TASKS; i++) + for (i = 0; i < CONFIG_PREALLOC_CHILDSTATUS; i++) { - curr = &g_child_pool.alloc[i] + curr = &g_child_pool.alloc[i]; prev->flink = curr; - prev = curr; + prev = curr; } } @@ -140,7 +194,7 @@ FAR struct child_status_s *task_allocchild(void) if (ret) { g_child_pool.freelist = ret->flink; - ret->flink = NULL; + ret->flink = NULL; } return ret; @@ -170,7 +224,7 @@ void task_freechild(FAR struct child_status_s *child) if (child) { - child->flink = g_child_pool.freelist; + child->flink = g_child_pool.freelist; g_child_pool.freelist = child; } } @@ -179,18 +233,14 @@ void task_freechild(FAR struct child_status_s *child) * Name: task_addchild * * Description: - * Find a child status structure in the given TCB. + * Add a child status structure in the given TCB. * * Parameters: * tcb - The TCB of the parent task to containing the child status. - * pid - The ID of the child to create - * status - Child exit status (should be zero) - * flags - Child flags (see CHILD_FLAGS_* defininitions) + * child - The structure to be added * * Return Value: - * On success, a non-NULL pointer to a child status structure. NULL is - * returned if (1) there are no free status structures, or (2) an entry - * with this PID already exists. + * N * * Assumptions: * Called during task creation processing in a safe context. No special @@ -198,37 +248,14 @@ void task_freechild(FAR struct child_status_s *child) * *****************************************************************************/ -FAR struct child_status_s *task_addchild(FAR _TCB *tcb, pid_t pid, int status, - uint8_t flags) +void task_addchild(FAR _TCB *tcb, FAR struct child_status_s *child) { - FAR struct child_status_s *child; + /* Add the entry into the TCB list of children */ - /* Make sure that there is not already a structure for this PID */ + child->flink = tcb->children; + tcb->children = child; - child = task_findchild(tcb, pid); - if (child) - { - return NULL; - } - - /* Allocate a new status structure */ - - child = task_allocchild(void); - if (child) - { - /* Initialize the structure */ - - child->ch_flags = flags; - child->ch_pid = pid; - child->ch_status = status; - - /* Add the entry into the TCB list of children */ - - status->flink = tcb->children; - tcb->childen = status; - } - - return child; + task_dumpchildren(tcb, "task_addchild"); } /***************************************************************************** @@ -325,6 +352,7 @@ FAR struct child_status_s *task_removechild(FAR _TCB *tcb, pid_t pid) } curr->flink = NULL; + task_dumpchildren(tcb, "task_removechild"); } return curr; @@ -360,6 +388,9 @@ void task_removechildren(FAR _TCB *tcb) next = curr->flink; task_freechild(curr); } + + tcb->children = NULL; + task_dumpchildren(tcb, "task_removechildren"); } #endif /* CONFIG_SCHED_HAVE_PARENT && CONFIG_SCHED_CHILD_STATUS */ diff --git a/nuttx/sched/task_create.c b/nuttx/sched/task_create.c index 801706cbf..2ed929ab0 100644 --- a/nuttx/sched/task_create.c +++ b/nuttx/sched/task_create.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/task_create.c * - * Copyright (C) 2007-2010 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2010, 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -81,7 +81,7 @@ * * Input Parameters: * name - Name of the new task - * type - Type of the new task + * ttype - Type of the new task * priority - Priority of the new task * stack_size - size (in bytes) of the stack needed * entry - Entry point of a new task @@ -99,10 +99,10 @@ ****************************************************************************/ #ifndef CONFIG_CUSTOM_STACK -static int thread_create(const char *name, uint8_t type, int priority, +static int thread_create(const char *name, uint8_t ttype, int priority, int stack_size, main_t entry, const char **argv) #else -static int thread_create(const char *name, uint8_t type, int priority, +static int thread_create(const char *name, uint8_t ttype, int priority, main_t entry, const char **argv) #endif { @@ -142,15 +142,9 @@ static int thread_create(const char *name, uint8_t type, int priority, } #endif - /* Mark the type of this thread (this setting will be needed in - * task_schedsetup() when up_initial_state() is called. - */ - - tcb->flags |= type; - /* Initialize the task control block */ - ret = task_schedsetup(tcb, priority, task_start, entry); + ret = task_schedsetup(tcb, priority, task_start, entry, ttype); if (ret != OK) { goto errout_with_tcb; diff --git a/nuttx/sched/task_exithook.c b/nuttx/sched/task_exithook.c index 1106f2885..1813c12ed 100644 --- a/nuttx/sched/task_exithook.c +++ b/nuttx/sched/task_exithook.c @@ -1,7 +1,7 @@ /**************************************************************************** * sched/task_exithook.c * - * Copyright (C) 2011-2012 Gregory Nutt. All rights reserved. + * Copyright (C) 2011-2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt <gnutt@nuttx.org> * * Redistribution and use in source and binary forms, with or without @@ -202,51 +202,86 @@ static inline void task_sigchild(FAR _TCB *tcb, int status) FAR _TCB *ptcb; siginfo_t info; - /* Keep things stationary through the following */ + /* Only exiting tasks should generate SIGCHLD. pthreads use other + * mechansims. + */ - sched_lock(); + if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_TASK) + { + /* Keep things stationary through the following */ - /* Get the TCB of the receiving task */ + sched_lock(); - ptcb = sched_gettcb(tcb->parent); - if (!ptcb) - { - /* The parent no longer exists... bail */ + /* Get the TCB of the receiving task */ - sched_unlock(); - return; - } + ptcb = sched_gettcb(tcb->parent); + if (!ptcb) + { + /* The parent no longer exists... bail */ - /* Decrement the number of children from this parent */ + sched_unlock(); + return; + } - DEBUGASSERT(ptcb->nchildren > 0); - ptcb->nchildren--; +#ifdef CONFIG_SCHED_CHILD_STATUS + /* Check if the parent task has suppressed retention of child exit + * status information. Only 'tasks' report exit status, not pthreads. + * pthreads have a different mechanism. + */ - /* Set the parent to an impossible PID. We do this because under certain - * conditions, task_exithook() can be called multiple times. If this - * function is called again, sched_gettcb() will fail on the invalid - * parent PID above, nchildren will be decremented once and all will be - * well. - */ + if ((ptcb->flags & TCB_FLAG_NOCLDWAIT) == 0) + { + FAR struct child_status_s *child; - tcb->parent = INVALID_PROCESS_ID; + /* No.. Find the exit status entry for this task in the parent TCB */ - /* Create the siginfo structure. We don't actually know the cause. That - * is a bug. Let's just say that the child task just exit-ted for now. - */ + child = task_findchild(ptcb, getpid()); + DEBUGASSERT(child); + if (child) + { + /* Mark that the child has exit'ed */ - info.si_signo = SIGCHLD; - info.si_code = CLD_EXITED; - info.si_value.sival_ptr = NULL; - info.si_pid = tcb->pid; - info.si_status = status; + child->ch_flags |= CHILD_FLAG_EXITED; - /* Send the signal. We need to use this internal interface so that we can - * provide the correct si_code value with the signal. - */ + /* Save the exit status */ + + child->ch_status = status; + } + } +#else + /* Decrement the number of children from this parent */ - (void)sig_received(ptcb, &info); - sched_unlock(); + DEBUGASSERT(ptcb->nchildren > 0); + ptcb->nchildren--; +#endif + + /* Set the parent to an impossible PID. We do this because under + * certain conditions, task_exithook() can be called multiple times. + * If this function is called again, sched_gettcb() will fail on the + * invalid parent PID above, nchildren will be decremented once and + * all will be well. + */ + + tcb->parent = INVALID_PROCESS_ID; + + /* Create the siginfo structure. We don't actually know the cause. + * That is a bug. Let's just say that the child task just exit-ted + * for now. + */ + + info.si_signo = SIGCHLD; + info.si_code = CLD_EXITED; + info.si_value.sival_ptr = NULL; + info.si_pid = tcb->pid; + info.si_status = status; + + /* Send the signal. We need to use this internal interface so that we + * can provide the correct si_code value with the signal. + */ + + (void)sig_received(ptcb, &info); + sched_unlock(); + } } #else # define task_sigchild(tcb,status) @@ -344,6 +379,12 @@ void task_exithook(FAR _TCB *tcb, int status) (void)lib_flushall(tcb->streams); #endif + /* Discard any un-reaped child status (no zombies here!) */ + +#if defined(CONFIG_SCHED_HAVE_PARENT) && defined(CONFIG_SCHED_CHILD_STATUS) + task_removechildren(tcb); +#endif + /* Free all file-related resources now. This gets called again * just be be certain when the TCB is delallocated. However, we * really need to close files as soon as possible while we still diff --git a/nuttx/sched/task_init.c b/nuttx/sched/task_init.c index 31fc5ef70..0f0fdc68e 100644 --- a/nuttx/sched/task_init.c +++ b/nuttx/sched/task_init.c @@ -141,7 +141,8 @@ int task_init(FAR _TCB *tcb, const char *name, int priority, /* Initialize the task control block */ - ret = task_schedsetup(tcb, priority, task_start, entry); + ret = task_schedsetup(tcb, priority, task_start, entry, + TCB_FLAG_TTYPE_TASK); if (ret == OK) { /* Setup to pass parameters to the new task */ diff --git a/nuttx/sched/task_reparent.c b/nuttx/sched/task_reparent.c index 244825f80..28d371bf1 100644 --- a/nuttx/sched/task_reparent.c +++ b/nuttx/sched/task_reparent.c @@ -71,6 +71,9 @@ int task_reparent(pid_t ppid, pid_t chpid) { +#ifdef CONFIG_SCHED_CHILD_STATUS + FAR struct child_status_s *child; +#endif _TCB *ptcb; _TCB *chtcb; _TCB *otcb; @@ -127,12 +130,30 @@ int task_reparent(pid_t ppid, pid_t chpid) /* Then reparent the child */ + chtcb->parent = ppid; /* The task specified by ppid is the new parent */ + +#ifdef CONFIG_SCHED_CHILD_STATUS + /* Remove the child status entry from old parent TCB */ + + child = task_removechild(otcb, chpid); + if (child) + { + /* Add the child status entry to the new parent TCB */ + + task_addchild(ptcb, child); + ret = OK; + } + else + { + ret = -ENOENT; + } +#else DEBUGASSERT(otcb->nchildren > 0); - chtcb->parent = ppid; /* The task specified by ppid is the new parent */ otcb->nchildren--; /* The orignal parent now has one few children */ ptcb->nchildren++; /* The new parent has one additional child */ ret = OK; +#endif errout_with_ints: irqrestore(flags); diff --git a/nuttx/sched/task_setup.c b/nuttx/sched/task_setup.c index 92897f0ae..80aefded3 100644 --- a/nuttx/sched/task_setup.c +++ b/nuttx/sched/task_setup.c @@ -153,7 +153,8 @@ static int task_assignpid(FAR _TCB *tcb) * Save the task ID of the parent task in the child task's TCB. * * Parameters: - * tcb - The TCB of the new, child task. + * tcb - The TCB of the new, child task. + * ttype - Type of the new thread: task, pthread, or kernel thread * * Returned Value: * None @@ -165,13 +166,57 @@ static int task_assignpid(FAR _TCB *tcb) ****************************************************************************/ #ifdef CONFIG_SCHED_HAVE_PARENT -static inline void task_saveparent(FAR _TCB *tcb) +static inline void task_saveparent(FAR _TCB *tcb, uint8_t ttype) { FAR _TCB *rtcb = (FAR _TCB*)g_readytorun.head; - DEBUGASSERT(rtcb->nchildren < UINT16_MAX); + /* Save the parent task's ID in the child task's TCB. I am not sure if + * this makes sense for the case of pthreads or not, but I don't think it + * is harmful in any event. + */ + tcb->parent = rtcb->pid; - rtcb->nchildren++; + + /* Exit status only needs to be retained for the case of tasks, however */ + + if (ttype == TCB_FLAG_TTYPE_TASK) + { +#ifdef CONFIG_SCHED_CHILD_STATUS + FAR struct child_status_s *child; + + /* Make sure that there is not already a structure for this PID in the + * parent TCB. There should not be. + */ + + child = task_findchild(rtcb, tcb->pid); + DEBUGASSERT(!child); + if (!child) + { + /* Allocate a new status structure */ + + child = task_allocchild(); + } + + /* Did we successfully find/allocate the child status structure? */ + + DEBUGASSERT(child); + if (child) + { + /* Yes.. Initialize the structure */ + + child->ch_flags = ttype; + child->ch_pid = tcb->pid; + child->ch_status = 0; + + /* Add the entry into the TCB list of children */ + + task_addchild(rtcb, child); + } +#else + DEBUGASSERT(rtcb->nchildren < UINT16_MAX); + rtcb->nchildren++; +#endif + } } #else # define task_saveparent(tcb) @@ -235,7 +280,7 @@ static inline void task_dupdspace(FAR _TCB *tcb) * priority - Priority of the new task * entry - Entry point of a new task * main - Application start point of the new task - * type - Type of the new thread: task, pthread, or kernel thread + * ttype - Type of the new thread: task, pthread, or kernel thread * * Return Value: * OK on success; ERROR on failure. @@ -245,7 +290,8 @@ static inline void task_dupdspace(FAR _TCB *tcb) * ****************************************************************************/ -int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, main_t main) +int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, main_t main, + uint8_t ttype) { int ret; @@ -264,9 +310,17 @@ int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, main_t main) tcb->start = start; tcb->entry.main = main; + /* Save the thrad type. This setting will be needed in + * up_initial_state() is called. + */ + + ttype &= TCB_FLAG_TTYPE_MASK; + tcb->flags &= ~TCB_FLAG_TTYPE_MASK; + tcb->flags |= ttype; + /* Save the task ID of the parent task in the TCB */ - task_saveparent(tcb); + task_saveparent(tcb, ttype); /* exec(), pthread_create(), task_create(), and vfork() all * inherit the signal mask of the parent thread. diff --git a/nuttx/sched/task_vfork.c b/nuttx/sched/task_vfork.c index 46b2d8e9f..fece4c596 100644 --- a/nuttx/sched/task_vfork.c +++ b/nuttx/sched/task_vfork.c @@ -136,12 +136,6 @@ FAR _TCB *task_vforksetup(start_t retaddr) (void)env_dup(child); - /* Mark the type of this thread (this setting will be needed in - * task_schedsetup() when up_initial_state() is called. - */ - - child->flags |= TCB_FLAG_TTYPE_TASK; - /* Get the priority of the parent task */ #ifdef CONFIG_PRIORITY_INHERITANCE @@ -153,7 +147,8 @@ FAR _TCB *task_vforksetup(start_t retaddr) /* Initialize the task control block. This calls up_initial_state() */ svdbg("Child priority=%d start=%p\n", priority, retaddr); - ret = task_schedsetup(child, priority, retaddr, parent->entry.main); + ret = task_schedsetup(child, priority, retaddr, parent->entry.main, + TCB_FLAG_TTYPE_TASK); if (ret != OK) { goto errout_with_tcb; |