-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Implement "timeout" support for Rust #78193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
337a2d1
213032f
b5b8d92
e823c22
e9b1585
f8e33c5
ea375ac
e1aa346
9afdb77
f5a4ad9
ec1c9f6
7b168d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// Copyright (c) 2024 Linaro LTD | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
//! Time types similar to `std::time` types. | ||
//! | ||
//! However, the rust-embedded world tends to use `fugit` for time. This has a Duration and | ||
//! Instant type, but they are parameterized by the slice used, and try hard to do the conversion | ||
//! at compile time. | ||
//! | ||
//! std has two time types, a Duration which represents an elapsed amount of time, and Instant, | ||
//! which represents a specific instance in time. | ||
//! | ||
//! Zephyr typically coordinates time in terms of a system tick interval, which comes from | ||
//! `sys_clock_hw_cycles_per_sec()`. | ||
//! | ||
//! The Rust/std semantics require Instant to be monotonically increasing. | ||
//! | ||
//! Zephyr's `sys/time_units.h` header contains numerous optimized macros for manipulating time in | ||
//! these units, specifically for converting between human time units and ticks, trying to avoid | ||
//! division, especially division by non-constants. | ||
|
||
use zephyr_sys::k_timeout_t; | ||
|
||
// The system ticks, is mostly a constant, but there are some boards that use a dynamic tick | ||
// frequency, and thus need to read this at runtime. | ||
#[cfg(CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME)] | ||
compile_error!("Rust does not (yet) support dynamic frequency timer"); | ||
|
||
// Given the above not defined, the system time base comes from a kconfig. | ||
/// The system time base. The system clock has this many ticks per second. | ||
pub const SYS_FREQUENCY: u32 = crate::kconfig::CONFIG_SYS_CLOCK_TICKS_PER_SEC as u32; | ||
|
||
/// Zephyr can be configured for either 64-bit or 32-bit time values. Use the appropriate type | ||
/// internally to match. This should end up the same size as `k_ticks_t`, but unsigned instead of | ||
/// signed. | ||
#[cfg(CONFIG_TIMEOUT_64BIT)] | ||
pub type Tick = u64; | ||
#[cfg(not(CONFIG_TIMEOUT_64BIT))] | ||
pub type Tick = u32; | ||
|
||
/// Duration appropraite for Zephyr calls that expect `k_timeout_t`. The result will be a time | ||
ycsin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// interval from "now" (when the call is made). | ||
pub type Duration = fugit::Duration<Tick, 1, SYS_FREQUENCY>; | ||
|
||
/// An Instant appropriate for Zephyr calls that expect a `k_timeout_t`. The result will be an | ||
/// absolute time in terms of system ticks. | ||
#[cfg(CONFIG_TIMEOUT_64BIT)] | ||
pub type Instant = fugit::Instant<Tick, 1, SYS_FREQUENCY>; | ||
|
||
// The zephry k_timeout_t represents several different types of intervals, based on the range of | ||
ycsin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// the value. It is a signed number of the same size as the Tick here, which effectively means it | ||
// is one bit less. | ||
// | ||
// 0: K_NO_WAIT: indicates the operation should not wait. | ||
// 1 .. Max: indicates a duration in ticks of a delay from "now". | ||
// -1: K_FOREVER: a time that never expires. | ||
// MIN .. -2: A wait for an absolute amount of ticks from the start of the system. | ||
// | ||
// The absolute time offset is only implemented when time is a 64 bit value. This also means that | ||
ycsin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// "Instant" isn't available when time is defined as a 32-bit value. | ||
|
||
// Wrapper around the timeout type, so we can implement From/Info. | ||
pub struct Timeout(pub k_timeout_t); | ||
|
||
// From allows methods to take a time of various types and convert it into a Zephyr timeout. | ||
ycsin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
impl From<Duration> for Timeout { | ||
fn from(value: Duration) -> Timeout { | ||
Timeout(k_timeout_t { ticks: value.ticks() as i64 }) | ||
} | ||
} | ||
|
||
#[cfg(CONFIG_TIMEOUT_64BIT)] | ||
impl From<Instant> for Timeout { | ||
fn from(value: Instant) -> Timeout { | ||
Timeout(k_timeout_t { ticks: -1 - 1 - (value.ticks() as i64) }) | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These conversion should capture cases where the tick value conflicts with "well-known values" ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will put in checks for the magic values. The overflow will be detected in debug builds by the arithmetic overflow checking in Rust debug builds. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The overflow check actually seems more important when time is defined as a 32-bit value. I'm not sure how to do the check without explicitly checking ffor debug assertions in the code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It also looks like that code probably won't even compile on a system with only 32-bit time. I'll have to clean this up as well. |
||
|
||
/// A sleep that waits forever. This is its own type, that is `Into<Timeout>` and can be used | ||
/// anywhere a timeout is needed. | ||
pub struct Forever; | ||
|
||
impl From<Forever> for Timeout { | ||
fn from(_value: Forever) -> Timeout { | ||
Timeout(crate::sys::K_FOREVER) | ||
} | ||
} | ||
|
||
/// A sleep that doesn't ever wait. This is its own type, that is `Info<Timeout>` and can be used | ||
/// anywhere a timeout is needed. | ||
pub struct NoWait; | ||
|
||
impl From<NoWait> for Timeout { | ||
fn from(_valued: NoWait) -> Timeout { | ||
Timeout(crate::sys::K_NO_WAIT) | ||
} | ||
} | ||
|
||
/// Put the current thread to sleep, for the given duration. Uses `k_sleep` for the actual sleep. | ||
/// Returns a duration roughly representing the remaining amount of time if the sleep was woken. | ||
pub fn sleep<T>(timeout: T) -> Duration | ||
where T: Into<Timeout>, | ||
{ | ||
let timeout: Timeout = timeout.into(); | ||
let rest = unsafe { crate::raw::k_sleep(timeout.0) }; | ||
Duration::millis(rest as Tick) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code looks reasonable to me. Was there perhaps a paragraph missing here in the docs? The language sounds as though it might have moved around or come after something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't start the beginning of a section with "However"? I'll look through this and try to figure out if something is missing, or just what misfired in my brain.