blob: 7a6a508b027e81ddf8395c9b571f7fd0d421aea0 [file]
/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Thread sleep/wake APIs
*
* Implements k_sleep(), k_usleep(), and the internal z_tick_sleep() helper.
* These APIs suspend the calling thread for a specified duration and rely on
* the scheduler (via z_sched_unready_locked()) and the timeout subsystem
* (via z_add_thread_timeout()) to do the heavy lifting.
*/
#include <zephyr/kernel.h>
#include <ksched.h>
#include <kthread.h>
#include <kswap.h>
#include <timeout_q.h>
#include <zephyr/internal/syscall_handler.h>
#include <zephyr/tracing/tracing.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/math_extras.h>
#include <zephyr/sys/util.h>
/* pending_current is owned by timeslicing.c; we reference it here to avoid
* a spurious timeslice when the thread that is going to sleep gets picked as
* the next thread to run while z_swap() is in progress.
*/
#if defined(CONFIG_SWAP_NONATOMIC) && defined(CONFIG_TIMESLICING)
extern struct k_thread *pending_current;
#endif
/**
* @brief Sleep the current thread for the given number of ticks.
*
* Removes _current from the run queue, arms a timeout for the requested
* duration, then context-switches away. Returns the number of ticks
* remaining if the sleep was cut short by k_wakeup(), or 0 on normal
* wakeup.
*
* @param timeout Sleep duration.
* @return Ticks remaining when woken early, or 0 on normal expiry.
*/
static int32_t z_tick_sleep(k_timeout_t timeout)
{
uint32_t expected_wakeup_ticks;
__ASSERT(!arch_is_in_isr(), "");
/* K_NO_WAIT is treated as a 'yield' */
if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
k_yield();
return 0;
}
k_spinlock_key_t key = k_spin_lock(&_sched_spinlock);
#if defined(CONFIG_TIMESLICING) && defined(CONFIG_SWAP_NONATOMIC)
pending_current = _current;
#endif /* CONFIG_TIMESLICING && CONFIG_SWAP_NONATOMIC */
z_sched_unready_locked(_current);
expected_wakeup_ticks = (uint32_t)z_add_thread_timeout(_current, timeout);
z_mark_thread_as_sleeping(_current);
(void)z_swap(&_sched_spinlock, key);
if (!z_is_aborted_thread_timeout(_current)) {
return 0;
}
/* We require a 32 bit unsigned subtraction to handle a wraparound */
uint32_t left_ticks = expected_wakeup_ticks - sys_clock_tick_get_32();
/* Use signed comparison so past-due wakeups (negative remainder) return 0.
* k_ticks_t may be uint32_t (!CONFIG_TIMEOUT_64BIT), so comparing ticks > 0
* directly would be an unsigned comparison and would misinterpret a negative
* remainder as a large positive value.
*/
int32_t signed_left = (int32_t)left_ticks;
if (signed_left > 0) {
return (k_ticks_t)signed_left;
}
return 0;
}
int32_t z_impl_k_sleep(k_timeout_t timeout)
{
k_ticks_t ticks;
__ASSERT(!arch_is_in_isr(), "");
SYS_PORT_TRACING_FUNC_ENTER(k_thread, sleep, timeout);
ticks = z_tick_sleep(timeout);
/* k_sleep() still returns 32 bit milliseconds for compatibility */
int64_t ms = K_TIMEOUT_EQ(timeout, K_FOREVER) ? K_TICKS_FOREVER :
clamp(k_ticks_to_ms_ceil64(ticks), 0, INT_MAX);
SYS_PORT_TRACING_FUNC_EXIT(k_thread, sleep, timeout, ms);
return (int32_t) ms;
}
#ifdef CONFIG_USERSPACE
static inline int32_t z_vrfy_k_sleep(k_timeout_t timeout)
{
return z_impl_k_sleep(timeout);
}
#include <zephyr/syscalls/k_sleep_mrsh.c>
#endif /* CONFIG_USERSPACE */
int32_t z_impl_k_usleep(int32_t us)
{
int32_t ticks;
SYS_PORT_TRACING_FUNC_ENTER(k_thread, usleep, us);
ticks = k_us_to_ticks_ceil64(us);
ticks = z_tick_sleep(Z_TIMEOUT_TICKS(ticks));
int32_t ret = k_ticks_to_us_ceil64(ticks);
SYS_PORT_TRACING_FUNC_EXIT(k_thread, usleep, us, ret);
return ret;
}
#ifdef CONFIG_USERSPACE
static inline int32_t z_vrfy_k_usleep(int32_t us)
{
return z_impl_k_usleep(us);
}
#include <zephyr/syscalls/k_usleep_mrsh.c>
#endif /* CONFIG_USERSPACE */