blob: 16f898b7c83d0964864edc67a9159c928a906ad6 [file] [log] [blame]
/****************************************************************************
* sched/sched/sched_sleep.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/irq.h>
#include <nuttx/clock.h>
#include "sched/sched.h"
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: nxsched_timeout
*
* Description:
* A timeout elapsed while waiting for timeout.
*
* Assumptions:
* This function executes in the context of the timer interrupt handler.
* Local interrupts are assumed to be disabled on entry.
*
* Input Parameters:
* arg - Parameter to pass to wdentry.
*
****************************************************************************/
static void nxsched_timeout(wdparm_t arg)
{
nxsched_wakeup((FAR struct tcb_s *)(uintptr_t)arg);
}
/****************************************************************************
* Name: nxsched_ticksleep
*
* Description:
* The nxsched_ticksleep() function will cause the calling thread to be
* suspended from execution for the specified number of system ticks.
*
* It can only be resumed through scheduler operations.
*
* Input Parameters:
* ticks - The number of system ticks to sleep.
*
* Returned Value:
* None
*
****************************************************************************/
void nxsched_ticksleep(unsigned int ticks)
{
FAR struct tcb_s *rtcb;
irqstate_t flags;
if (ticks == 0)
{
sched_yield();
return;
}
flags = enter_critical_section();
rtcb = this_task();
wd_start(&rtcb->waitdog, ticks, nxsched_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_SLEEPING;
dq_addlast((FAR dq_entry_t *)rtcb, list_waitingforsignal());
/* Now, perform the context switch if one is needed */
up_switch_context(this_task(), rtcb);
wd_cancel(&rtcb->waitdog);
leave_critical_section(flags);
}
/****************************************************************************
* Name: nxsched_wakeup
*
* Description:
* The nxsched_wakeup() function is used to wake up a task that is
* currently in the sleeping state before its timeout expires.
*
* This function can be used by internal scheduler logic or by
* system-level components that need to resume a sleeping task early.
*
* Input Parameters:
* tcb - Pointer to the TCB of the task to be awakened.
*
* Returned Value:
* None
*
****************************************************************************/
void nxsched_wakeup(FAR struct tcb_s *tcb)
{
irqstate_t flags;
DEBUGASSERT(tcb != NULL);
flags = enter_critical_section();
if (tcb->task_state == TSTATE_SLEEPING)
{
FAR struct tcb_s *rtcb = this_task();
/* Remove the task from sleeping list */
dq_rem((FAR dq_entry_t *)tcb, list_waitingforsignal());
wd_cancel(&tcb->waitdog);
/* Add the task to ready-to-run task list, and
* perform the context switch if one is needed
*/
if (nxsched_add_readytorun(tcb))
{
up_switch_context(this_task(), rtcb);
}
}
leave_critical_section(flags);
}
/****************************************************************************
* Name: nxsched_usleep
*
* Description:
* The nxsched_usleep() function will cause the calling thread to be
* suspended from execution for the specified number of microseconds.
*
* It can only be resumed through scheduler.
*
* Input Parameters:
* usec - The number of microseconds to sleep.
*
* Returned Value:
* None
*
****************************************************************************/
void nxsched_usleep(useconds_t usec)
{
nxsched_ticksleep(USEC2TICK(usec));
}
/****************************************************************************
* Name: nxsched_msleep
*
* Description:
* The nxsched_msleep() function will cause the calling thread to be
* suspended from execution for the specified number of milliseconds.
*
* It can only be resumed through scheduler.
*
* Input Parameters:
* msec - The number of milliseconds to sleep.
*
* Returned Value:
* None
*
****************************************************************************/
void nxsched_msleep(unsigned int msec)
{
nxsched_ticksleep(MSEC2TICK(msec));
}
/****************************************************************************
* Name: nxsched_sleep
*
* Description:
* The nxsched_sleep() function will cause the calling thread to be
* suspended from execution for the specified number of seconds.
*
* It can only be resumed through scheduler.
*
* Input Parameters:
* sec - The number of seconds to sleep.
*
* Returned Value:
* None
*
****************************************************************************/
void nxsched_sleep(unsigned int sec)
{
nxsched_ticksleep(SEC2TICK(sec));
}
/****************************************************************************
* Name: nxsched_nanosleep
*
* Description:
* Internal nanosleep implementation used by the scheduler. This function
* converts the requested sleep interval into system ticks, performs a
* tick-based blocking sleep, and optionally returns the remaining time if
* the sleep is interrupted by a signal.
*
* Input Parameters:
* rqtp - Requested sleep interval (may be NULL)
* rmtp - If the rmtp argument is non-NULL, the timespec structure
* referenced by it is updated to contain the amount of time
* remaining.
*
* Returned Value:
* Returns OK (0) on success. Returns -EINVAL for an invalid timespec
* argument and -EAGAIN for a zero-length timeout, as required by POSIX.
*
****************************************************************************/
int nxsched_nanosleep(FAR const struct timespec *rqtp,
FAR struct timespec *rmtp)
{
clock_t ticks;
clock_t expect = 0;
clock_t stop;
/* Validate the input timespec */
if (rqtp && (rqtp->tv_nsec < 0 || rqtp->tv_nsec >= 1000000000))
{
return -EINVAL;
}
/* Zero-length sleep: Yield the processor and return -EAGAIN */
if (rqtp && rqtp->tv_sec == 0 && rqtp->tv_nsec == 0)
{
sched_yield();
return -EAGAIN;
}
/* Convert the requested interval to system ticks */
ticks = clock_time2ticks(rqtp);
/* Compute the absolute tick value when the sleep should expire.
* This is used later to determine the remaining time after interruption.
*/
expect = clock_delay2abstick(ticks);
/* Perform the blocking tick-based sleep */
nxsched_ticksleep(ticks);
/* Capture the current tick count after waking up */
stop = clock_systime_ticks();
/* If the caller provided a buffer for the remaining time, compute how much
* of the original interval is left. If the sleep expired normally,
* expect <= stop and the remaining time becomes zero.
*/
if (rmtp)
{
clock_ticks2time(rmtp,
clock_compare(stop, expect) ? expect - stop : 0);
}
return OK;
}