aboutsummaryrefslogtreecommitdiff
path: root/nuttx/sched
diff options
context:
space:
mode:
authorpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2013-01-24 23:18:32 +0000
committerpatacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3>2013-01-24 23:18:32 +0000
commita2ec48846f786e72a976038c9467b25a61ad5a9f (patch)
treec4f4fcda6c667b7928c3b2aaa89a81027e298b47 /nuttx/sched
parent25e9b8d0846b68a18014c63146234193bfe539e8 (diff)
downloadpx4-firmware-a2ec48846f786e72a976038c9467b25a61ad5a9f.tar.gz
px4-firmware-a2ec48846f786e72a976038c9467b25a61ad5a9f.tar.bz2
px4-firmware-a2ec48846f786e72a976038c9467b25a61ad5a9f.zip
Fix some missing logic and inconsistencies in child status logic; Fix a bug introduced into sigaction()
git-svn-id: http://svn.code.sf.net/p/nuttx/code/trunk@5560 42af7a65-404d-4744-a932-0658087f49c3
Diffstat (limited to 'nuttx/sched')
-rw-r--r--nuttx/sched/Kconfig7
-rw-r--r--nuttx/sched/os_internal.h1
-rw-r--r--nuttx/sched/sched_waitid.c136
-rw-r--r--nuttx/sched/sched_waitpid.c132
-rw-r--r--nuttx/sched/sig_action.c22
-rw-r--r--nuttx/sched/task_childstatus.c36
-rw-r--r--nuttx/sched/task_reparent.c24
-rw-r--r--nuttx/sched/task_setup.c23
8 files changed, 279 insertions, 102 deletions
diff --git a/nuttx/sched/Kconfig b/nuttx/sched/Kconfig
index 7ea301600..7745c2c25 100644
--- a/nuttx/sched/Kconfig
+++ b/nuttx/sched/Kconfig
@@ -124,6 +124,13 @@ config PREALLOC_CHILDSTATUS
sa.sa_flags = SA_NOCLDWAIT;
int ret = sigaction(SIGCHLD, &sa, NULL);
+config DEBUG_CHILDSTATUS
+ bool "Enable Child Status Debug Output"
+ default n
+ depends on SCHED_CHILD_STATUS && DEBUG
+ ---help---
+ Very detailed... I am sure that you do not want this.
+
config JULIAN_TIME
bool "Enables Julian time conversions"
default n
diff --git a/nuttx/sched/os_internal.h b/nuttx/sched/os_internal.h
index 7d5095bad..dc87cb9a4 100644
--- a/nuttx/sched/os_internal.h
+++ b/nuttx/sched/os_internal.h
@@ -274,6 +274,7 @@ void weak_function task_initialize(void);
FAR struct child_status_s *task_allocchild(void);
void task_freechild(FAR struct child_status_s *status);
void task_addchild(FAR _TCB *tcb, FAR struct child_status_s *child);
+FAR struct child_status_s *task_exitchild(FAR _TCB *tcb);
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/sched_waitid.c b/nuttx/sched/sched_waitid.c
index 37ee26ce0..e73bc223c 100644
--- a/nuttx/sched/sched_waitid.c
+++ b/nuttx/sched/sched_waitid.c
@@ -54,6 +54,36 @@
*****************************************************************************/
/*****************************************************************************
+ * Name: exitted_child
+ *
+ * Description:
+ * Handle the case where a child exitted properlay was we (apparently) lost
+ * the detch of child signal.
+ *
+ *****************************************************************************/
+
+#ifdef CONFIG_SCHED_CHILD_STATUS
+static void exitted_child(FAR _TCB *rtcb, FAR struct child_status_s *child,
+ FAR siginfo_t *info)
+{
+ /* The child has exited. Return the saved exit status (and some fudged
+ * information.
+ */
+
+ info->si_signo = SIGCHLD;
+ info->si_code = CLD_EXITED;
+ info->si_value.sival_ptr = NULL;
+ info->si_pid = child->ch_pid;
+ info->si_status = child->ch_status;
+
+ /* Discard the child entry */
+
+ (void)task_removechild(rtcb, child->ch_pid);
+ task_freechild(child);
+}
+#endif
+
+/*****************************************************************************
* Public Functions
*****************************************************************************/
@@ -120,9 +150,14 @@
*
*****************************************************************************/
-int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options)
+int waitid(idtype_t idtype, id_t id, FAR siginfo_t *info, int options)
{
FAR _TCB *rtcb = (FAR _TCB *)g_readytorun.head;
+ FAR _TCB *ctcb;
+#ifdef CONFIG_SCHED_CHILD_STATUS
+ FAR struct child_status_s *child;
+ bool retains;
+#endif
sigset_t sigset;
int err;
int ret;
@@ -160,7 +195,11 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options)
*/
#ifdef CONFIG_SCHED_CHILD_STATUS
- if (rtcb->children == NULL)
+ /* Does this task retain child status? */
+
+ retains = ((rtcb->flags && TCB_FLAG_NOCLDWAIT) == 0);
+
+ if (rtcb->children == NULL && retains)
{
/* There are no children */
@@ -169,13 +208,29 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options)
}
else if (idtype == P_PID)
{
- if (task_findchild(rtcb, (pid_t)id) == NULL)
- {
- /* This specific pid is not a child */
+ /* Get the TCB corresponding to this PID and make sure it is our child. */
+ ctcb = sched_gettcb((pid_t)id);
+ if (!ctcb || ctcb->parent != rtcb->pid)
+ {
err = ECHILD;
goto errout_with_errno;
}
+
+ /* Does this task retain child status? */
+
+ if (retains)
+ {
+ /* Check if this specific pid has allocated child status? */
+
+ 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)
@@ -189,7 +244,7 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options)
{
/* Get the TCB corresponding to this PID and make sure it is our child. */
- FAR _TCB *ctcb = sched_gettcb((pid_t)id);
+ ctcb = sched_gettcb((pid_t)id);
if (!ctcb || ctcb->parent != rtcb->pid)
{
err = ECHILD;
@@ -209,48 +264,61 @@ int waitid(idtype_t idtype, id_t id, siginfo_t *info, int options)
* instead).
*/
- DEBUGASSERT(rtcb->children);
- if (rtcb->children == NULL)
+ DEBUGASSERT(!retains || rtcb->children);
+ if (idtype == P_ALL)
{
- /* This should not happen. I am just wasting your FLASH. */
+ /* We are waiting for any child to exit */
- err = ECHILD;
- goto errout_with_errno;
+ if (retains && (child = task_exitchild(rtcb)) != NULL)
+ {
+ /* A child has exitted. Apparently we missed the signal.
+ * Return the exit status and break out of the loop.
+ */
+
+ exitted_child(rtcb, child, info);
+ break;
+ }
}
- 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.
- */
+ /* We are waiting for a specific PID. Does this task retain child status? */
+
+ else if (retains)
+ {
+ /* Yes ... 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 */
+ /* The child has exited. Return the exit status and break out
+ * of the loop.
+ */
- 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;
+ exitted_child(rtcb, child, info);
+ break;
+ }
+ }
+ else
+ {
+ /* We can use kill() with signal number 0 to determine if that
+ * task is still alive.
+ */
- /* Discard the child entry and break out of the loop */
+ ret = kill((pid_t)id, 0);
+ if (ret < 0)
+ {
+ /* It is no longer running. We know that the child task
+ * was running okay when we started, so we must have lost
+ * the signal. In this case, we know that the task exit'ed,
+ * but we do not know its exit status. It would be better
+ * to reported ECHILD than bogus status.
+ */
- (void)task_removechild(rtcb, (pid_t)id);
- task_freechild(child);
+ err = ECHILD;
+ goto errout_with_errno;
}
}
#else
diff --git a/nuttx/sched/sched_waitpid.c b/nuttx/sched/sched_waitpid.c
index fe3f7167d..ecdc60a2c 100644
--- a/nuttx/sched/sched_waitpid.c
+++ b/nuttx/sched/sched_waitpid.c
@@ -185,7 +185,7 @@
#ifndef CONFIG_SCHED_HAVE_PARENT
pid_t waitpid(pid_t pid, int *stat_loc, int options)
{
- _TCB *tcb;
+ _TCB *ctcb;
bool mystat;
int err;
int ret;
@@ -208,8 +208,8 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
/* Get the TCB corresponding to this PID */
- tcb = sched_gettcb(pid);
- if (!tcb)
+ ctcb = sched_gettcb(pid);
+ if (!ctcb)
{
err = ECHILD;
goto errout_with_errno;
@@ -221,15 +221,15 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
* others?
*/
- if (stat_loc != NULL && tcb->stat_loc == NULL)
+ if (stat_loc != NULL && ctcb->stat_loc == NULL)
{
- tcb->stat_loc = stat_loc;
- mystat = true;
+ ctcb->stat_loc = stat_loc;
+ mystat = true;
}
/* Then wait for the task to exit */
- ret = sem_wait(&tcb->exitsem);
+ ret = sem_wait(&ctcb->exitsem);
if (ret < 0)
{
/* Unlock pre-emption and return the ERROR (sem_wait has already set
@@ -239,7 +239,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
if (mystat)
{
- tcb->stat_loc = NULL;
+ ctcb->stat_loc = NULL;
}
goto errout;
@@ -274,8 +274,10 @@ errout:
pid_t waitpid(pid_t pid, int *stat_loc, int options)
{
FAR _TCB *rtcb = (FAR _TCB *)g_readytorun.head;
+ FAR _TCB *ctcb;
#ifdef CONFIG_SCHED_CHILD_STATUS
FAR struct child_status_s *child;
+ bool retains;
#endif
FAR struct siginfo info;
sigset_t sigset;
@@ -303,27 +305,43 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
sched_lock();
- /* Verify that this task actually has children and that the the request
- * TCB is actually a child of this task.
+ /* Verify that this task actually has children and that the requested PID
+ * is actually a child of this task.
*/
#ifdef CONFIG_SCHED_CHILD_STATUS
- if (rtcb->children == NULL)
- {
- /* There are no children */
+ /* Does this task retain child status? */
+ retains = ((rtcb->flags && TCB_FLAG_NOCLDWAIT) == 0);
+
+ if (rtcb->children == NULL && retains)
+ {
err = ECHILD;
goto errout_with_errno;
}
else if (pid != (pid_t)-1)
{
- /* This specific pid is not a child */
+ /* Get the TCB corresponding to this PID and make sure it is our child. */
- if (task_findchild(rtcb, pid) == NULL)
+ ctcb = sched_gettcb(pid);
+ if (!ctcb || ctcb->parent != rtcb->pid)
{
err = ECHILD;
goto errout_with_errno;
}
+
+ /* Does this task retain child status? */
+
+ if (retains)
+ {
+ /* Check if this specific pid has allocated child status? */
+
+ if (task_findchild(rtcb, pid) == NULL)
+ {
+ err = ECHILD;
+ goto errout_with_errno;
+ }
+ }
}
#else
if (rtcb->nchildren == 0)
@@ -337,7 +355,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
{
/* Get the TCB corresponding to this PID and make sure it is our child. */
- FAR _TCB *ctcb = sched_gettcb(pid);
+ ctcb = sched_gettcb(pid);
if (!ctcb || ctcb->parent != rtcb->pid)
{
err = ECHILD;
@@ -350,6 +368,7 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
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
@@ -362,39 +381,33 @@ 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
+ DEBUGASSERT(!retains || rtcb->children);
+ if (retains && (child = task_exitchild(rtcb)) != NULL)
{
- /* There were one or more children when we started so they
- * must have exit'ed. There are just no bread crumbs left
- * behind to tell us the PID(s) of the existed children.
- * Reporting ECHLD is about all we can do in this case.
+ /* A child has exitted. Apparently we missed the signal.
+ * Return the saved exit status.
*/
- err = ECHILD;
- goto errout_with_errno;
+ /* 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, child->ch_pid);
+ task_freechild(child);
+ break;
}
}
- else
+
+ /* We are waiting for a specific PID. Does this task retain child status? */
+
+ else if (retains)
{
-#ifdef CONFIG_SCHED_CHILD_STATUS
- /* We are waiting for a specific PID. Get the current status
- * of the child task.
- */
+ /* 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? */
@@ -408,27 +421,48 @@ pid_t waitpid(pid_t pid, int *stat_loc, int options)
(void)task_removechild(rtcb, pid);
task_freechild(child);
+ break;
}
-#else
- /* We are waiting for a specific PID. We can use kill() with
- * signal number 0 to determine if that task is still alive.
+ }
+ else
+ {
+ /* We can use kill() with signal number 0 to determine if that
+ * task is still alive.
*/
ret = kill(pid, 0);
if (ret < 0)
{
- /* It is no longer running. We know that the child task was
- * running okay when we started, so we must have lost the
- * signal. In this case, we know that the task exit'ed, but
- * we do not know its exit status. It would be better to
- * reported ECHILD that bogus status.
+ /* It is no longer running. We know that the child task
+ * was running okay when we started, so we must have lost
+ * the signal. In this case, we know that the task exit'ed,
+ * but we do not know its exit status. It would be better
+ * to reported ECHILD than bogus status.
*/
err = ECHILD;
goto errout_with_errno;
}
-#endif
}
+#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
+ * instead).
+ */
+
+ if (rtcb->nchildren == 0 ||
+ (pid != (pid_t)-1 && (ret = kill((pid_t)id, 0)) < 0))
+ {
+ /* We know that the child task was running okay we stared,
+ * so we must have lost the signal. What can we do?
+ * Let's claim we were interrupted by a signal.
+ */
+
+ err = EINTR;
+ 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 708667993..7d84b6291 100644
--- a/nuttx/sched/sig_action.c
+++ b/nuttx/sched/sig_action.c
@@ -169,7 +169,6 @@ 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;
/* Since sigactions can only be installed from the running thread of
* execution, no special precautions should be necessary.
@@ -251,24 +250,31 @@ int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction *
if (act->sa_u._sa_handler == SIG_IGN)
{
- /* If there is a old sigaction, remove it from sigactionq */
+ /* Do we still have a sigaction container from the previous setting? */
- sq_rem((FAR sq_entry_t*)sigact, &rtcb->sigactionq);
+ if (sigact)
+ {
+ /* Yes.. Remove it from sigactionq */
+
+ sq_rem((FAR sq_entry_t*)sigact, &rtcb->sigactionq);
- /* And deallocate it */
+ /* And deallocate it */
- sig_releaseaction(sigact);
+ sig_releaseaction(sigact);
+ }
}
/* A sigaction has been supplied */
else
{
- /* Check if a sigaction was found */
+ /* Do we still have a sigaction container from the previous setting?
+ * If so, then re-use for the new signal action.
+ */
if (!sigact)
{
- /* No sigaction was found, but one is needed. Allocate one. */
+ /* No.. Then we need to allocate one for the new action. */
sigact = sig_allocateaction();
@@ -294,7 +300,7 @@ int sigaction(int signo, FAR const struct sigaction *act, FAR struct sigaction *
COPY_SIGACTION(&sigact->act, act);
}
- return ret;
+ return OK;
}
/****************************************************************************
diff --git a/nuttx/sched/task_childstatus.c b/nuttx/sched/task_childstatus.c
index 0f6d36c29..09aa48135 100644
--- a/nuttx/sched/task_childstatus.c
+++ b/nuttx/sched/task_childstatus.c
@@ -298,6 +298,42 @@ FAR struct child_status_s *task_findchild(FAR _TCB *tcb, pid_t pid)
}
/*****************************************************************************
+ * Name: task_exitchild
+ *
+ * Description:
+ * Search for any child that has exitted.
+ *
+ * Parameters:
+ * tcb - The TCB of the parent task to containing the child status.
+ *
+ * Return Value:
+ * On success, a non-NULL pointer to a child status structure for the
+ * exited child. NULL is returned if not child has exited.
+ *
+ * Assumptions:
+ * Called during SIGCHLD processing in a safe context. No special precautions
+ * are required here.
+ *
+ *****************************************************************************/
+
+FAR struct child_status_s *task_exitchild(FAR _TCB *tcb)
+{
+ FAR struct child_status_s *child;
+
+ /* Find the status structure with the matching PID */
+
+ for (child = tcb->children; child; child = child->flink)
+ {
+ if ((child->ch_flags & CHILD_FLAG_EXITED) != 0)
+ {
+ return child;
+ }
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************
* Name: task_removechild
*
* Description:
diff --git a/nuttx/sched/task_reparent.c b/nuttx/sched/task_reparent.c
index 28d371bf1..3a7ece37d 100644
--- a/nuttx/sched/task_reparent.c
+++ b/nuttx/sched/task_reparent.c
@@ -138,14 +138,32 @@ int task_reparent(pid_t ppid, pid_t chpid)
child = task_removechild(otcb, chpid);
if (child)
{
- /* Add the child status entry to the new parent TCB */
+ /* Has the new parent supressed child exit status? */
+
+ if ((ptcb->flags && TCB_FLAG_NOCLDWAIT) == 0)
+ {
+ /* No.. Add the child status entry to the new parent TCB */
+
+ task_addchild(ptcb, child);
+ }
+ else
+ {
+ /* Yes.. Discard the child status entry */
+
+ task_freechild(child);
+ }
+
+ /* Either case is a success */
- task_addchild(ptcb, child);
ret = OK;
}
else
{
- ret = -ENOENT;
+ /* This would not be an error if the original parent has
+ * suppressed child exit status.
+ */
+
+ ret = ((otcb->flags && TCB_FLAG_NOCLDWAIT) == 0) ? -ENOENT : OK;
}
#else
DEBUGASSERT(otcb->nchildren > 0);
diff --git a/nuttx/sched/task_setup.c b/nuttx/sched/task_setup.c
index 8f54b2e93..66c948cfd 100644
--- a/nuttx/sched/task_setup.c
+++ b/nuttx/sched/task_setup.c
@@ -150,7 +150,8 @@ static int task_assignpid(FAR _TCB *tcb)
* Name: task_saveparent
*
* Description:
- * Save the task ID of the parent task in the child task's TCB.
+ * Save the task ID of the parent task in the child task's TCB and allocate
+ * a child status structure to catch the child task's exit status.
*
* Parameters:
* tcb - The TCB of the new, child task.
@@ -177,11 +178,15 @@ static inline void task_saveparent(FAR _TCB *tcb, uint8_t ttype)
tcb->parent = rtcb->pid;
- /* Exit status only needs to be retained for the case of tasks, however */
+#ifdef CONFIG_SCHED_CHILD_STATUS
+ /* Exit status only needs to be retained for the case of tasks, however.
+ * Tasks can also suppress retention of their child status by applying
+ * the SA_NOCLDWAIT flag with sigaction()/
+ */
- if (ttype == TCB_FLAG_TTYPE_TASK)
+ if (ttype == TCB_FLAG_TTYPE_TASK &&
+ (rtcb->flags && TCB_FLAG_NOCLDWAIT) == 0)
{
-#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
@@ -212,11 +217,11 @@ static inline void task_saveparent(FAR _TCB *tcb, uint8_t ttype)
task_addchild(rtcb, child);
}
+ }
#else
- DEBUGASSERT(rtcb->nchildren < UINT16_MAX);
- rtcb->nchildren++;
+ DEBUGASSERT(rtcb->nchildren < UINT16_MAX);
+ rtcb->nchildren++;
#endif
- }
}
#else
# define task_saveparent(tcb,ttype)
@@ -318,7 +323,9 @@ int task_schedsetup(FAR _TCB *tcb, int priority, start_t start, main_t main,
tcb->flags &= ~TCB_FLAG_TTYPE_MASK;
tcb->flags |= ttype;
- /* Save the task ID of the parent task in the TCB */
+ /* Save the task ID of the parent task in the TCB and allocate
+ * a child status structure.
+ */
task_saveparent(tcb, ttype);