When using pthread_kill, the signal should be delivered to the specified thread. Current implementation, however, may add the signal to the groups pending list, if the signal is masked at the time of dispatch. From the group's pending list it can be delivered to any thread of the group, which is wrong. Fix this by adding a new field "FAR struct tcb_s *tcb" to "struct sigpendq", marking if the signal needs to be delivered to a specific thread. Use NULL for the value if delivery to any thread in the group is ok. Signed-off-by: Jukka Laitinen <jukka.laitinen@tii.ae>
278 lines
7.9 KiB
C
278 lines
7.9 KiB
C
/****************************************************************************
|
|
* 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
|
|
}
|