/**************************************************************************** * sched/group/group_signal.c * * SPDX-License-Identifier: Apache-2.0 * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include "sched/sched.h" #include "group/group.h" #include "signal/signal.h" /**************************************************************************** * Private Types ****************************************************************************/ #ifdef HAVE_GROUP_MEMBERS struct group_signal_s { FAR siginfo_t *siginfo; /* Signal to be dispatched */ FAR struct tcb_s *dtcb; /* Default, valid TCB */ FAR struct tcb_s *utcb; /* TCB with this signal unblocked */ FAR struct tcb_s *atcb; /* This TCB was awakened */ FAR struct tcb_s *ptcb; /* This TCB received the signal */ }; #endif /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: group_signal_handler * * Description: * Callback from group_foreachchild that handles one member of the group. * * Input Parameters: * pid - The ID of the group member that may be signalled. * arg - A pointer to a struct group_signal_s instance. * * Returned Value: * 0 (OK) on success; a negated errno value on failure. * ****************************************************************************/ #ifdef HAVE_GROUP_MEMBERS 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; /* Get the TCB associated with the group member */ tcb = nxsched_get_tcb(pid); DEBUGASSERT(tcb != NULL && tcb->group != NULL && info != NULL); 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). */ ret = nxsig_ismember(&tcb->sigwaitmask, info->siginfo->si_signo); if (ret == 1 && (!info->atcb || info->siginfo->si_signo == SIGCHLD)) { /* 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 = nxsig_tcbdispatch(tcb, info->siginfo); if (ret < 0) { return ret; } /* Limit to one thread */ info->atcb = tcb; if (info->ptcb != NULL && info->siginfo->si_signo != SIGCHLD) { return 1; /* Terminate the search */ } } /* Is this signal unblocked on this thread? */ if (!nxsig_ismember(&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 a action associated with the task group? */ sigact = nxsig_find_action(tcb->group, 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 = nxsig_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 */ } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: group_signal * * Description: * Send a signal to every member of the group. * * Input Parameters: * group - The task group that needs to be signalled. * * Returned Value: * 0 (OK) on success; a negated errno value on failure. * * Assumptions: * Called during task termination in a safe context. No special precautions * are required here. Because signals can be sent from interrupt handlers, * this function may be called indirectly in the context of an interrupt * handler. * ****************************************************************************/ 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). */ #ifdef CONFIG_SMP irqstate_t flags = enter_critical_section(); #else sched_lock(); #endif /* 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) { tcb = info.dtcb; } else { ret = -ECHILD; goto errout; } /* Now deliver the signal to the selected group member */ ret = nxsig_tcbdispatch(tcb, siginfo); } errout: #ifdef CONFIG_SMP leave_critical_section(flags); #else sched_unlock(); #endif return ret; #else UNUSED(group); UNUSED(siginfo); return -ENOSYS; #endif }