diff --git a/sched/signal/CMakeLists.txt b/sched/signal/CMakeLists.txt index 9db325fec5e..31c32c44a10 100644 --- a/sched/signal/CMakeLists.txt +++ b/sched/signal/CMakeLists.txt @@ -33,6 +33,7 @@ if(NOT CONFIG_DISABLE_ALL_SIGNALS) sig_notification.c sig_dispatch.c sig_pause.c + sig_clockwait.c sig_nanosleep.c sig_usleep.c sig_sleep.c diff --git a/sched/signal/Make.defs b/sched/signal/Make.defs index b70ac2a8246..7117c527655 100644 --- a/sched/signal/Make.defs +++ b/sched/signal/Make.defs @@ -23,7 +23,7 @@ ifneq ($(CONFIG_DISABLE_ALL_SIGNALS),y) CSRCS += sig_dispatch.c sig_kill.c sig_lowest.c sig_nanosleep.c CSRCS += sig_notification.c sig_pause.c sig_ppoll.c sig_procmask.c CSRCS += sig_pselect.c sig_queue.c sig_sleep.c sig_suspend.c sig_tgkill.c -CSRCS += sig_timedwait.c sig_usleep.c sig_waitinfo.c +CSRCS += sig_timedwait.c sig_usleep.c sig_waitinfo.c sig_clockwait.c endif ifeq ($(CONFIG_ENABLE_ALL_SIGNALS),y) diff --git a/sched/signal/sig_clockwait.c b/sched/signal/sig_clockwait.c new file mode 100644 index 00000000000..92a6859d7db --- /dev/null +++ b/sched/signal/sig_clockwait.c @@ -0,0 +1,291 @@ +/**************************************************************************** + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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(); + + 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 + } + + /* 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; +} diff --git a/sched/signal/sig_timedwait.c b/sched/signal/sig_timedwait.c index d3f63506694..e6024702be3 100644 --- a/sched/signal/sig_timedwait.c +++ b/sched/signal/sig_timedwait.c @@ -55,241 +55,10 @@ * 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(); - - 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 - } - - /* 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; -} - /**************************************************************************** * Name: nxsig_timedwait *