static inline void task_exitstatus(FAR struct task_group_s *group, int status) { FAR struct child_status_s *child; /* Check if the parent task group has suppressed retention of * child exit status information. */ if ((group->tg_flags & GROUP_FLAG_NOCLDWAIT) == 0) { /* No.. Find the exit status entry for this task in the parent TCB */ child = group_findchild(group, getpid()); DEBUGASSERT(child); if (child) { #ifndef HAVE_GROUP_MEMBERS /* No group members? Save the exit status */ child->ch_status = status; #endif /* Save the exit status.. For the case of HAVE_GROUP_MEMBERS, * the child status will be as exited until the last member * of the task group exits. */ child->ch_status = status; } } }
static inline void task_groupexit(FAR struct task_group_s *group) { FAR struct child_status_s *child; /* Check if the parent task group has suppressed retention of child exit * status information. */ if ((group->tg_flags & GROUP_FLAG_NOCLDWAIT) == 0) { /* No.. Find the exit status entry for this task in the parent TCB */ child = group_findchild(group, getpid()); DEBUGASSERT(child); if (child) { /* Mark that all members of the child task group has exited */ child->ch_flags |= CHILD_FLAG_EXITED; } } }
static inline void task_saveparent(FAR struct tcb_s *tcb, uint8_t ttype) { FAR struct tcb_s *rtcb = (FAR struct tcb_s*)g_readytorun.head; #if defined(HAVE_GROUP_MEMBERS) || defined(CONFIG_SCHED_CHILD_STATUS) DEBUGASSERT(tcb && tcb->group && rtcb->group); #else #endif #ifdef HAVE_GROUP_MEMBERS /* Save the ID of the parent tasks' task group in the child's task group. * Do nothing for pthreads. The parent and the child are both members of * the same task group. */ #ifndef CONFIG_DISABLE_PTHREAD if ((tcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD) #endif { /* This is a new task in a new task group, we have to copy the ID from * the parent's task group structure to child's task group. */ tcb->group->tg_pgid = rtcb->group->tg_gid; } #else DEBUGASSERT(tcb); /* 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->ppid = rtcb->pid; #endif #ifdef CONFIG_SCHED_CHILD_STATUS /* Tasks can also suppress retention of their child status by applying * the SA_NOCLDWAIT flag with sigaction(). */ if ((rtcb->group->tg_flags && GROUP_FLAG_NOCLDWAIT) == 0) { 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 = group_findchild(rtcb->group, tcb->pid); DEBUGASSERT(!child); if (!child) { /* Allocate a new status structure */ child = group_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 */ group_addchild(rtcb->group, child); } } #else DEBUGASSERT(rtcb->nchildren < UINT16_MAX); rtcb->nchildren++; #endif }
pid_t waitpid(pid_t pid, int *stat_loc, int options) { FAR struct tcb_s *rtcb = this_task(); FAR struct tcb_s *ctcb; #ifdef CONFIG_SCHED_CHILD_STATUS FAR struct child_status_s *child; bool retains; #endif FAR struct siginfo info; sigset_t set; int errcode; int ret; DEBUGASSERT(stat_loc); /* waitpid() is a cancellation point */ (void)enter_cancellation_point(); /* None of the options are supported */ #ifdef CONFIG_DEBUG_FEATURES if (options != 0) { set_errno(ENOSYS); leave_cancellation_point(); return ERROR; } #endif /* Create a signal set that contains only SIGCHLD */ (void)sigemptyset(&set); (void)sigaddset(&set, SIGCHLD); /* Disable pre-emption so that nothing changes while the loop executes */ sched_lock(); /* Verify that this task actually has children and that the requested PID * is actually a child of this task. */ #ifdef CONFIG_SCHED_CHILD_STATUS /* Does this task retain child status? */ retains = ((rtcb->group->tg_flags && GROUP_FLAG_NOCLDWAIT) == 0); if (rtcb->group->tg_children == NULL && retains) { errcode = ECHILD; goto errout_with_errno; } else if (pid != (pid_t)-1) { /* Get the TCB corresponding to this PID and make sure that the * thread it is our child. */ ctcb = sched_gettcb(pid); #ifdef HAVE_GROUP_MEMBERS if (ctcb == NULL || ctcb->group->tg_pgid != rtcb->group->tg_gid) #else if (ctcb == NULL || ctcb->group->tg_ppid != rtcb->pid) #endif { errcode = ECHILD; goto errout_with_errno; } /* Does this task retain child status? */ if (retains) { /* Check if this specific pid has allocated child status? */ if (group_findchild(rtcb->group, pid) == NULL) { errcode = ECHILD; goto errout_with_errno; } } } #else /* CONFIG_SCHED_CHILD_STATUS */ if (rtcb->group->tg_nchildren == 0) { /* There are no children */ errcode = ECHILD; goto errout_with_errno; } else if (pid != (pid_t)-1) { /* Get the TCB corresponding to this PID and make sure that the * thread it is our child. */ ctcb = sched_gettcb(pid); #ifdef HAVE_GROUP_MEMBERS if (ctcb == NULL || ctcb->group->tg_pgid != rtcb->group->tg_gid) #else if (ctcb == NULL || ctcb->group->tg_ppid != rtcb->pid) #endif { errcode = ECHILD; goto errout_with_errno; } } #endif /* CONFIG_SCHED_CHILD_STATUS */ /* 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). */ if (pid == (pid_t)-1) { /* We are waiting for any child, check if there are still * chilren. */ DEBUGASSERT(!retains || rtcb->group->tg_children); if (retains && (child = group_exitchild(rtcb->group)) != NULL) { /* A child has exited. Apparently we missed the signal. * Return the saved exit status. */ /* The child has exited. Return the saved exit status */ *stat_loc = child->ch_status << 8; /* Discard the child entry and break out of the loop */ (void)group_removechild(rtcb->group, child->ch_pid); group_freechild(child); break; } } /* We are waiting for a specific PID. Does this task retain child status? */ else if (retains) { /* Get the current status of the child task. */ child = group_findchild(rtcb->group, pid); DEBUGASSERT(child); /* 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 << 8; /* Discard the child entry and break out of the loop */ (void)group_removechild(rtcb->group, pid); group_freechild(child); break; } } 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 than bogus status. */ errcode = ECHILD; goto errout_with_errno; } } #else /* 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). */ if (rtcb->group->tg_nchildren == 0 || (pid != (pid_t)-1 && (ret = kill(pid, 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 return ECHILD.. that is at least informative. */ errcode = ECHILD; goto errout_with_errno; } #endif /* CONFIG_SCHED_CHILD_STATUS */ /* Wait for any death-of-child signal */ ret = sigwaitinfo(&set, &info); if (ret < 0) { goto errout_with_lock; } /* Was this the death of the thread we were waiting for? In the of * pid == (pid_t)-1, we are waiting for any child thread. */ if (info.si_signo == SIGCHLD && (pid == (pid_t)-1 || info.si_pid == pid)) { /* Yes... return the status and PID (in the event it was -1) */ *stat_loc = info.si_status << 8; pid = info.si_pid; break; } } leave_cancellation_point(); sched_unlock(); return (int)pid; errout_with_errno: set_errno(errcode); errout_with_lock: leave_cancellation_point(); sched_unlock(); return ERROR; }