nuttx/sched/signal/sig_clockwait.c
Piyush Patle 0dccc8ba21 include/debug.h: Move to include/nuttx/debug.h
debug.h is a NuttX-specific, non-POSIX header. Placing it in the
top-level include/ directory creates naming conflicts with external
projects that define their own debug.h.
This commit moves the canonical header to include/nuttx/debug.h,
following the NuttX convention for non-POSIX/non-standard headers,
and updates all in-tree references.

A backward-compatibility shim is left at include/debug.h that
emits a deprecation #warning and re-includes <nuttx/debug.h>,
allowing out-of-tree code to continue building while migrating.

Signed-off-by: Piyush Patle <piyushpatle228@gmail.com>
2026-04-07 07:50:06 -03:00

297 lines
9 KiB
C

/****************************************************************************
* sched/signal/sig_clockwait.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 <nuttx/compiler.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <assert.h>
#include <nuttx/debug.h>
#include <sched.h>
#include <errno.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/wdog.h>
#include <nuttx/signal.h>
#include <nuttx/cancelpt.h>
#include <nuttx/queue.h>
#include "sched/sched.h"
#include "signal/signal.h"
#include "clock/clock.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: nxsig_timeout
*
* Description:
* A timeout elapsed while waiting for signals to be queued.
*
* Assumptions:
* This function executes in the context of the timer interrupt handler.
* Local interrupts are assumed to be disabled on entry.
*
****************************************************************************/
static void nxsig_timeout(wdparm_t arg)
{
FAR struct tcb_s *wtcb = (FAR struct tcb_s *)(uintptr_t)arg;
irqstate_t flags;
/* We must be in a critical section in order to call up_switch_context()
* below.
*/
flags = enter_critical_section();
/* There may be a race condition -- make sure the task is
* still waiting for a signal
*/
if (wtcb->task_state == TSTATE_WAIT_SIG)
{
nxsig_wait_irq(wtcb, SIG_WAIT_TIMEOUT, SI_TIMER, ETIMEDOUT);
}
leave_critical_section(flags);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: nxsig_wait_irq
*
* Description:
* An error event has occurred and the signal wait must be terminated with
* an error.
*
****************************************************************************/
void nxsig_wait_irq(FAR struct tcb_s *wtcb, uint8_t signo,
uint8_t code, int errcode)
{
FAR struct tcb_s *rtcb = this_task();
#ifdef CONFIG_DISABLE_ALL_SIGNALS
UNUSED(signo);
UNUSED(code);
UNUSED(errcode);
#else
if (wtcb->sigunbinfo != NULL)
{
wtcb->sigunbinfo->si_signo = signo;
wtcb->sigunbinfo->si_code = code;
wtcb->sigunbinfo->si_errno = errcode;
wtcb->sigunbinfo->si_value.sival_int = 0;
# ifdef CONFIG_SCHED_HAVE_PARENT
wtcb->sigunbinfo->si_pid = 0; /* Not applicable */
wtcb->sigunbinfo->si_status = OK;
# endif
}
#endif
/* Remove the task from waiting list */
dq_rem((FAR dq_entry_t *)wtcb, list_waitingforsignal());
/* Add the task to ready-to-run task list, and
* perform the context switch if one is needed
*/
if (nxsched_add_readytorun(wtcb))
{
up_switch_context(this_task(), rtcb);
}
}
/****************************************************************************
* Name: nxsig_clockwait
*
* Description:
* This function selects the pending signal set specified by the argument
* set. If multiple signals are pending in set, it will remove and return
* the lowest numbered one. If no signals in set are pending at the time
* of the call, the calling process will be suspended until one of the
* signals in set becomes pending, OR until the process is interrupted by
* an unblocked signal, OR until the time interval specified by timeout
* (if any), has expired. If timeout is NULL, then the timeout interval
* is forever.
*
* If the info argument is non-NULL, the selected signal number is stored
* in the si_signo member and the cause of the signal is stored in the
* si_code member. The content of si_value is only meaningful if the
* signal was generated by sigqueue() (or nxsig_queue).
*
* This is an internal OS interface. It is functionally equivalent to
* sigtimedwait() except that:
*
* - It is not a cancellation point, and
* - It does not modify the errno value.
*
* Input Parameters:
* clockid - The ID of the clock to be used to measure the timeout.
* flags - Open flags. TIMER_ABSTIME is the only supported flag.
* rqtp - The amount of time to be suspended from execution.
* rmtp - If the rmtp argument is non-NULL, the timespec structure
* referenced by it is updated to contain the amount of time
* remaining in the interval (the requested time minus the time
* actually slept)
*
* Returned Value:
* This is an internal OS interface and should not be used by applications.
* A negated errno value is returned on failure.
*
* EAGAIN - wait time is zero.
* EINTR - The wait was interrupted by an unblocked, caught signal.
*
* Notes:
* This function should be called with critical section set.
*
****************************************************************************/
int nxsig_clockwait(int clockid, int flags,
FAR const struct timespec *rqtp,
FAR struct timespec *rmtp)
{
FAR struct tcb_s *rtcb;
irqstate_t iflags;
clock_t expect = 0u;
clock_t stop = 0u;
if (rqtp && (rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1000000000))
{
return -EINVAL;
}
/* If rqtp is zero, yield CPU and return
* Notice: The behavior of sleep(0) is not defined in POSIX, so there are
* different implementations:
* 1. In Linux, nanosleep(0) will call schedule() to yield CPU:
* https://elixir.bootlin.com/linux/latest/source/kernel/time/
* hrtimer.c#L2038
* 2. In BSD, nanosleep(0) will return immediately:
* https://github.com/freebsd/freebsd-src/blob/
* 475fa89800086718bd9249fd4dc3f862549f1f78/crypto/openssh/
* openbsd-compat/bsd-misc.c#L243
*/
if (rqtp && rqtp->tv_sec == 0 && rqtp->tv_nsec == 0)
{
sched_yield();
return -EAGAIN;
}
#ifdef CONFIG_CANCELLATION_POINTS
/* nxsig_clockwait() is not a cancellation point, but it may be called
* from a cancellation point. So if a cancellation is pending, we
* must exit immediately without waiting.
*/
if (check_cancellation_point())
{
/* If there is a pending cancellation, then do not perform
* the wait. Exit now with ECANCELED.
*/
return -ECANCELED;
}
#endif
iflags = enter_critical_section();
rtcb = this_task();
if (rqtp)
{
/* Start the watchdog timer */
if ((flags & TIMER_ABSTIME) == 0)
{
expect = clock_delay2abstick(clock_time2ticks(rqtp));
}
else if (clockid == CLOCK_REALTIME)
{
#ifdef CONFIG_CLOCK_TIMEKEEPING
clock_t delay;
clock_abstime2ticks(CLOCK_REALTIME, rqtp, &delay);
expect = clock_delay2abstick(delay);
#else
clock_realtime2absticks(rqtp, &expect);
#endif
}
else
{
expect = clock_time2ticks(rqtp);
}
wd_start_abstick(&rtcb->waitdog, expect,
nxsig_timeout, (uintptr_t)rtcb);
}
/* Remove the tcb task from the ready-to-run list. */
nxsched_remove_self(rtcb);
/* Add the task to the specified blocked task list */
rtcb->task_state = TSTATE_WAIT_SIG;
dq_addlast((FAR dq_entry_t *)rtcb, list_waitingforsignal());
/* Now, perform the context switch if one is needed */
up_switch_context(this_task(), rtcb);
/* We no longer need the watchdog */
if (rqtp)
{
stop = clock_systime_ticks();
}
leave_critical_section(iflags);
if (rqtp && rmtp && expect)
{
clock_ticks2time(rmtp,
clock_compare(stop, expect) ? expect - stop : 0);
}
return 0;
}