| /**************************************************************************** |
| * 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 <nuttx/config.h> |
| |
| #include <sched.h> |
| #include <assert.h> |
| #include <signal.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <nuttx/signal.h> |
| |
| #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); |
| |
| /* 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, true); |
| 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, true); |
| 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, true); |
| } |
| |
| errout: |
| #ifdef CONFIG_SMP |
| leave_critical_section(flags); |
| #else |
| sched_unlock(); |
| #endif |
| return ret; |
| |
| #else |
| |
| UNUSED(group); |
| UNUSED(siginfo); |
| return -ENOSYS; |
| |
| #endif |
| } |