static void group_assigngid(FAR struct task_group_s *group) { irqstate_t flags; gid_t gid; /* Pre-emption should already be enabled, but lets be paranoid careful */ sched_lock(); /* Loop until we create a unique ID */ for (;;) { /* Increment the ID counter. This is global data so be extra paranoid. */ flags = irqsave(); gid = ++g_gidcounter; /* Check for overflow */ if (gid <= 0) { g_gidcounter = 1; irqrestore(flags); } else { /* Does a task group with this ID already exist? */ irqrestore(flags); if (group_findbygid(gid) == NULL) { /* Now assign this ID to the group and return */ group->tg_gid = gid; sched_unlock(); return; } } } }
int task_reparent(pid_t ppid, pid_t chpid) { #ifdef CONFIG_SCHED_CHILD_STATUS FAR struct child_status_s *child; #endif FAR struct task_group_s *chgrp; FAR struct task_group_s *ogrp; FAR struct task_group_s *pgrp; struct tcb_s *tcb; gid_t ogid; gid_t pgid; irqstate_t flags; int ret; /* Disable interrupts so that nothing can change in the relatinoship of * the three task: Child, current parent, and new parent. */ flags = irqsave(); /* Get the child tasks task group */ tcb = sched_gettcb(chpid); if (!tcb) { ret = -ECHILD; goto errout_with_ints; } DEBUGASSERT(tcb->group); chgrp = tcb->group; /* Get the GID of the old parent task's task group (ogid) */ ogid = chgrp->tg_pgid; /* Get the old parent task's task group (ogrp) */ ogrp = group_findbygid(ogid); if (!ogrp) { ret = -ESRCH; goto errout_with_ints; } /* If new parent task's PID (ppid) is zero, then new parent is the * grandparent will be the new parent, i.e., the parent of the current * parent task. */ if (ppid == 0) { /* Get the grandparent task's task group (pgrp) */ pgid = ogrp->tg_pgid; pgrp = group_findbygid(pgid); } else { /* Get the new parent task's task group (pgrp) */ tcb = sched_gettcb(ppid); if (!tcb) { ret = -ESRCH; goto errout_with_ints; } pgrp = tcb->group; pgid = pgrp->tg_gid; } if (!pgrp) { ret = -ESRCH; goto errout_with_ints; } /* Then reparent the child. Notice that we don't actually change the * parent of the task. Rather, we change the parent task group for * all members of the child's task group. */ chgrp->tg_pgid = pgid; #ifdef CONFIG_SCHED_CHILD_STATUS /* Remove the child status entry from old parent task group */ child = group_removechild(ogrp, chpid); if (child) { /* Has the new parent's task group supressed child exit status? */ if ((pgrp->tg_flags && GROUP_FLAG_NOCLDWAIT) == 0) { /* No.. Add the child status entry to the new parent's task group */ group_addchild(pgrp, child); } else { /* Yes.. Discard the child status entry */ group_freechild(child); } /* Either case is a success */ ret = OK; } else { /* This would not be an error if the original parent's task group has * suppressed child exit status. */ ret = ((ogrp->tg_flags && GROUP_FLAG_NOCLDWAIT) == 0) ? -ENOENT : OK; } #else /* CONFIG_SCHED_CHILD_STATUS */ DEBUGASSERT(otcb->nchildren > 0); otcb->nchildren--; /* The orignal parent now has one few children */ ptcb->nchildren++; /* The new parent has one additional child */ ret = OK; #endif /* CONFIG_SCHED_CHILD_STATUS */ errout_with_ints: irqrestore(flags); return ret; }
int group_addrenv(FAR struct tcb_s *tcb) { FAR struct task_group_s *group; FAR struct task_group_s *oldgroup; irqstate_t flags; gid_t gid; int ret; /* NULL for the tcb means to use the TCB of the task at the head of the * ready to run list. */ if (!tcb) { tcb = (FAR struct tcb_s *)g_readytorun.head; } DEBUGASSERT(tcb && tcb->group); group = tcb->group; /* Does the group have an address environment? */ if ((group->tg_flags & GROUP_FLAG_ADDRENV) == 0) { /* No... just return perhaps leaving a different address environment * intact. */ return OK; } /* Get the ID of the group that needs the address environment */ gid = group->tg_gid; DEBUGASSERT(gid != 0); /* Are we going to change address environments? */ flags = irqsave(); if (gid != g_gid_current) { /* Yes.. Is there a current address environment in place? */ if (g_gid_current != 0) { /* Find the old group with this ID. */ oldgroup = group_findbygid(g_gid_current); DEBUGASSERT(oldgroup && (oldgroup->tg_flags & GROUP_FLAG_ADDRENV) != 0); if (oldgroup) { /* We need to flush the D-Cache and Invalidate the I-Cache for * the group whose environment is disappearing. */ up_addrenv_coherent(&oldgroup->addrenv); } } /* Instantiate the new address environment (removing the old * environment in the process). For the case of kernel threads, * the old mappings will be removed and no new mappings will be * instantiated. */ ret = up_addrenv_select(&group->addrenv, NULL); if (ret < 0) { bdbg("ERROR: up_addrenv_select failed: %d\n", ret); } /* Save the new, current group */ g_gid_current = gid; } irqrestore(flags); return OK; }
static inline void task_sigchild(gid_t pgid, FAR struct tcb_s *ctcb, int status) { FAR struct task_group_s *chgrp = ctcb->group; FAR struct task_group_s *pgrp; siginfo_t info; DEBUGASSERT(chgrp); /* Get the parent task group. It is possible that all of the members of * the parent task group have exited. This would not be an error. In * this case, the child task group has been orphaned. */ pgrp = group_findbygid(pgid); if (!pgrp) { /* Set the task group ID to an invalid group ID. The dead parent * task group ID could get reused some time in the future. */ chgrp->tg_pgid = INVALID_GROUP_ID; return; } /* Save the exit status now if this is the main thread of the task group * that is exiting. Only the exiting main task of a task group carries * interpretable exit Check if this is the main task that is exiting. */ #ifndef CONFIG_DISABLE_PTHREAD if ((ctcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD) #endif { task_exitstatus(pgrp, status); } /* But only the final exiting thread in a task group, whatever it is, * should generate SIGCHLD. */ if (chgrp->tg_nmembers == 1) { /* Mark that all of the threads in the task group have exited */ task_groupexit(pgrp); /* Create the siginfo structure. We don't actually know the cause. * That is a bug. Let's just say that the child task just exited * for now. */ info.si_signo = SIGCHLD; info.si_code = CLD_EXITED; info.si_errno = OK; info.si_value.sival_ptr = NULL; #ifndef CONFIG_DISABLE_PTHREAD info.si_pid = chgrp->tg_task; #else info.si_pid = ctcb->pid; #endif info.si_status = status; /* Send the signal to one thread in the group */ (void)group_signal(pgrp, &info); } }