/**************************************************************************** * sched/group/group_signal.c * * Copyright (C) 2013, 2016 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include "sched/sched.h" #include "group/group.h" #include "signal/signal.h" #if defined(HAVE_TASK_GROUP) && !defined(CONFIG_DISABLE_SIGNALS) /**************************************************************************** * 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. * * Parameters: * pid - The ID of the group member that may be signalled. * arg - A pointer to a struct group_signal_s instance. * * Return 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 = sched_gettcb(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). */ 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 a action associated with the task group? */ sigact = sig_findaction(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 = 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 */ } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: group_signal * * Description: * Send a signal to every member of the group. * * Parameters: * group - The task group that needs to be signalled. * * Return 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). */ 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 } #endif /* HAVE_TASK_GROUP && !CONFIG_DISABLE_SIGNALS */