mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-16 17:03:34 +08:00
Add logic to retain child task exit status if so configured
git-svn-id: http://svn.code.sf.net/p/nuttx/code/trunk@5553 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
+77
-2
@@ -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---
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
+102
-61
@@ -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 */
|
||||
|
||||
/* If no sigaction was found, but one is needed, then
|
||||
* allocate one.
|
||||
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 (!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)
|
||||
flags = irqsave();
|
||||
|
||||
/* Mark that status should be not be retained */
|
||||
|
||||
rtcb->flags |= TCB_FLAG_NOCLDWAIT;
|
||||
|
||||
/* Free all pending exit status */
|
||||
|
||||
task_removechildren(rtcb);
|
||||
irqrestore(flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* A sigaction has been supplied */
|
||||
|
||||
else
|
||||
{
|
||||
/* Check if a sigaction was found */
|
||||
|
||||
if (!sigact)
|
||||
{
|
||||
/* No sigaction was found, but one is needed. Allocate one. */
|
||||
|
||||
sigact = sig_allocateaction();
|
||||
|
||||
/* An error has occurred if we could not allocate the sigaction */
|
||||
|
||||
if (!sigact)
|
||||
{
|
||||
ret = ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Put the signal number in the queue entry */
|
||||
{
|
||||
set_errno(ENOMEM);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
sigact->signo = (uint8_t)signo;
|
||||
/* Put the signal number in the queue entry */
|
||||
|
||||
/* Add the new sigaction to sigactionq */
|
||||
sigact->signo = (uint8_t)signo;
|
||||
|
||||
sq_addlast((FAR sq_entry_t*)sigact, &rtcb->sigactionq);
|
||||
}
|
||||
/* Add the new sigaction to sigactionq */
|
||||
|
||||
sq_addlast((FAR sq_entry_t*)sigact, &rtcb->sigactionq);
|
||||
}
|
||||
|
||||
/* Set the new sigaction if so requested */
|
||||
/* Set the new sigaction */
|
||||
|
||||
if ((sigact) && (act))
|
||||
{
|
||||
/* Check if it is a request to install a new handler */
|
||||
|
||||
if (act->sa_u._sa_handler)
|
||||
{
|
||||
COPY_SIGACTION(&sigact->act, act);
|
||||
}
|
||||
|
||||
/* No.. It is a request to remove the old handler */
|
||||
|
||||
else
|
||||
{
|
||||
/* Remove the old sigaction from sigactionq */
|
||||
|
||||
sq_rem((FAR sq_entry_t*)sigact, &rtcb->sigactionq);
|
||||
|
||||
/* And deallocate it */
|
||||
|
||||
sig_releaseaction(sigact);
|
||||
}
|
||||
}
|
||||
COPY_SIGACTION(&sigact->act, act);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -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)
|
||||
|
||||
/*****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
*****************************************************************************/
|
||||
/* 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. 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).
|
||||
*/
|
||||
/* 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;
|
||||
};
|
||||
|
||||
@@ -70,6 +90,40 @@ static struct child_pool_s g_child_pool;
|
||||
* Private Functions
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************
|
||||
* 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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
+82
-41
@@ -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();
|
||||
|
||||
/* Get the TCB of the receiving task */
|
||||
|
||||
ptcb = sched_gettcb(tcb->parent);
|
||||
if (!ptcb)
|
||||
if ((tcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_TASK)
|
||||
{
|
||||
/* The parent no longer exists... bail */
|
||||
/* Keep things stationary through the following */
|
||||
|
||||
sched_lock();
|
||||
|
||||
/* Get the TCB of the receiving task */
|
||||
|
||||
ptcb = sched_gettcb(tcb->parent);
|
||||
if (!ptcb)
|
||||
{
|
||||
/* The parent no longer exists... bail */
|
||||
|
||||
sched_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
#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.
|
||||
*/
|
||||
|
||||
if ((ptcb->flags & TCB_FLAG_NOCLDWAIT) == 0)
|
||||
{
|
||||
FAR struct child_status_s *child;
|
||||
|
||||
/* No.. Find the exit status entry for this task in the parent TCB */
|
||||
|
||||
child = task_findchild(ptcb, getpid());
|
||||
DEBUGASSERT(child);
|
||||
if (child)
|
||||
{
|
||||
/* Mark that the child has exit'ed */
|
||||
|
||||
child->ch_flags |= CHILD_FLAG_EXITED;
|
||||
|
||||
/* Save the exit status */
|
||||
|
||||
child->ch_status = status;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Decrement the number of children from this parent */
|
||||
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Decrement the number of children from this parent */
|
||||
|
||||
DEBUGASSERT(ptcb->nchildren > 0);
|
||||
ptcb->nchildren--;
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user