int sig_dispatch(pid_t pid, FAR siginfo_t *info) { #ifdef HAVE_GROUP_MEMBERS FAR struct tcb_s *stcb; FAR struct task_group_s *group; /* Get the TCB associated with the pid */ stcb = sched_gettcb(pid); if (stcb) { /* The task/thread associated with this PID is still active. Get its * task group. */ group = stcb->group; } else { /* The task/thread associated with this PID has exited. In the normal * usage model, the PID should correspond to the PID of the task that * created the task group. Try looking it up. */ group = group_findbypid(pid); } /* Did we locate the group? */ if (group) { /* Yes.. call group_signal() to send the signal to the correct group * member. */ return group_signal(group, info); } else { return -ESRCH; } #else FAR struct tcb_s *stcb; /* Get the TCB associated with the pid */ stcb = sched_gettcb(pid); if (!stcb) { return -ESRCH; } return sig_tcbdispatch(stcb, info); #endif }
static void pthread_condtimedout(int argc, uint32_t pid, uint32_t signo) { #ifdef HAVE_GROUP_MEMBERS FAR struct tcb_s *tcb; siginfo_t info; /* The logic below if equivalent to sigqueue(), but uses sig_tcbdispatch() * instead of sig_dispatch(). This avoids the group signal deliver logic * and assures, instead, that the signal is delivered specifically to this * thread that is known to be waiting on the signal. */ /* Get the waiting TCB. sched_gettcb() might return NULL if the task has * exited for some reason. */ tcb = sched_gettcb((pid_t)pid); if (tcb) { /* Create the siginfo structure */ info.si_signo = signo; info.si_code = SI_QUEUE; info.si_errno = ETIMEDOUT; info.si_value.sival_ptr = NULL; #ifdef CONFIG_SCHED_HAVE_PARENT info.si_pid = (pid_t)pid; info.si_status = OK; #endif /* Process the receipt of the signal. The scheduler is not locked as * is normally the case when this function is called because we are in * a watchdog timer interrupt handler. */ (void)sig_tcbdispatch(tcb, &info); } #else /* HAVE_GROUP_MEMBERS */ /* Things are a little easier if there are not group members. We can just * use sigqueue(). */ #ifdef CONFIG_CAN_PASS_STRUCTS union sigval value; /* Send the specified signal to the specified task. */ value.sival_ptr = NULL; (void)sigqueue((int)pid, (int)signo, value); #else (void)sigqueue((int)pid, (int)signo, NULL); #endif #endif /* HAVE_GROUP_MEMBERS */ }
static inline void task_sigchild(FAR struct tcb_s *ptcb, FAR struct tcb_s *ctcb, int status) { siginfo_t info; /* If task groups are not supported then we will report SIGCHLD when the * task exits. Unfortunately, there could still be threads in the group * that are still running. */ #ifndef CONFIG_DISABLE_PTHREAD if ((ctcb->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD) #endif { #ifdef CONFIG_SCHED_CHILD_STATUS /* Save the exit status now of the main thread */ task_exitstatus(ptcb->group, status); #else /* CONFIG_SCHED_CHILD_STATUS */ /* Decrement the number of children from this parent */ DEBUGASSERT(ptcb->nchildren > 0); ptcb->nchildren--; #endif /* CONFIG_SCHED_CHILD_STATUS */ /* 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 = ctcb->group->tg_task; #else info.si_pid = ctcb->pid; #endif 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_tcbdispatch(ptcb, &info); } }
int pthread_kill(pthread_t thread, int signo) { #ifdef HAVE_GROUP_MEMBERS /* If group members are support then pthread_kill() differs from kill(). * kill(), in this case, must following the POSIX rules for delivery of * signals in the group environment. Otherwise, kill(), like * pthread_kill() will just deliver the signal to the thread ID it is * requested to use. */ #ifdef CONFIG_SCHED_HAVE_PARENT FAR struct tcb_s *rtcb = (FAR struct tcb_s *)g_readytorun.head; #endif FAR struct tcb_s *stcb; siginfo_t info; int ret; /* Make sure that the signal is valid */ if (!GOOD_SIGNO(signo)) { ret = -EINVAL; goto errout; } /* Keep things stationary through the following */ sched_lock(); /* Create the siginfo structure */ info.si_signo = signo; info.si_code = SI_USER; info.si_value.sival_ptr = NULL; #ifdef CONFIG_SCHED_HAVE_PARENT info.si_pid = rtcb->pid; info.si_status = OK; #endif /* Get the TCB associated with the thread */ stcb = sched_gettcb((pid_t)thread); if (!stcb) { ret = -ESRCH; goto errout_with_lock; } /* Dispatch the signal to thread, bypassing normal task group thread * dispatch rules. */ ret = sig_tcbdispatch(stcb, &info); sched_unlock(); if (ret < 0) { goto errout; } return OK; errout_with_lock: sched_unlock(); errout: return -ret; #else /* If group members are not supported then pthread_kill is basically the * same as kill(). */ int ret; set_errno(EINVAL); ret = kill((pid_t)thread, signo); if (ret != OK) { ret = get_errno(); } return ret; #endif }
static int group_signal_handler(pid_t pid, FAR void *arg) { FAR struct group_signal_s *info = (FAR struct group_signal_s *)arg; FAR struct tcb_s *tcb; FAR sigactq_t *sigact; int ret; DEBUGASSERT(info); /* Get the TCB associated with the group member */ tcb = sched_gettcb(pid); DEBUGASSERT(tcb); if (tcb) { /* Set this one as the default if we have not already set the default. */ if (!info->dtcb) { info->dtcb = tcb; } /* Is the thread waiting for this signal (in this case, the signal is * probably blocked). */ if (sigismember(&tcb->sigwaitmask, info->siginfo->si_signo) && !info->atcb) { /* Yes.. This means that the task is suspended, waiting for this * signal to occur. Stop looking and use this TCB. The * requirement is this: If a task group receives a signal and * more than one thread is waiting on that signal, then one and * only one indeterminate thread out of that waiting group will * receive the signal. */ ret = sig_tcbdispatch(tcb, info->siginfo); if (ret < 0) { return ret; } /* Limit to one thread */ info->atcb = tcb; if (info->ptcb != NULL); { return 1; /* Terminate the search */ } } /* Is this signal unblocked on this thread? */ if (!sigismember(&tcb->sigprocmask, info->siginfo->si_signo) && !info->ptcb && tcb != info->atcb) { /* Yes.. remember this TCB if we have not encountered any * other threads that have the signal unblocked. */ if (!info->utcb) { info->utcb = tcb; } /* Is there also an action associated with the task? */ sigact = sig_findaction(tcb, info->siginfo->si_signo); if (sigact) { /* Yes.. then use this thread. The requirement is this: * If a task group receives a signal then one and only one * indeterminate thread in the task group which is not * blocking the signal will receive the signal. */ ret = sig_tcbdispatch(tcb, info->siginfo); if (ret < 0) { return ret; } /* Limit to one thread */ info->ptcb = tcb; if (info->atcb != NULL) { return 1; /* Terminate the search */ } } } } return 0; /* Keep searching */ }
int group_signal(FAR struct task_group_s *group, FAR siginfo_t *siginfo) { #ifdef HAVE_GROUP_MEMBERS struct group_signal_s info; FAR struct tcb_s *tcb; int ret; DEBUGASSERT(group && siginfo); info.siginfo = siginfo; info.dtcb = NULL; /* Default, valid TCB */ info.utcb = NULL; /* TCB with this signal unblocked */ info.atcb = NULL; /* This TCB was awakened */ info.ptcb = NULL; /* This TCB received the signal */ /* Make sure that pre-emption is disabled to that we signal all of the * members of the group before any of them actually run. (This does * nothing if were were called from an interrupt handler). */ sched_lock(); /* Now visit each member of the group and perform signal handling checks. */ ret = group_foreachchild(group, group_signal_handler, &info); if (ret < 0) { goto errout; } /* We need to dispatch the signal in any event (if nothing else so that it * can be added to the pending signal list). If we found a thread with the * signal unblocked, then use that thread. */ if (info.atcb == NULL && info.ptcb == NULL) { if (info.utcb) { tcb = info.utcb; } /* Otherwise use the default TCB. There should always be a default * TCB. It will have the signal blocked, but can be used to get the * signal to a pending state. */ else /* if (info.dtcb) */ { DEBUGASSERT(info.dtcb); tcb = info.dtcb; } /* Now deliver the signal to the selected group member */ ret = sig_tcbdispatch(tcb, siginfo); } errout: sched_unlock(); return ret; #else return -ENOSYS; #endif }