Пример #1
0
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;
        }
    }
}
Пример #2
0
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;
        }
    }
}
Пример #3
0
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
}
Пример #4
0
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;
}