Skip to content
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

Standardize use of furi::time::Duration #179

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
### Added

- `flipperzero::dialogs::DialogFileBrowserOptions`
- `as_ticks()` method to `flipperzero::furi::time::Duration`
- `flipperzero::furi::thread::sleep_ticks` function to sleep for exact duration
- `TryFrom<core::time::Duration>` for `flipperzero::furi::time::Duration`

### Changed

Expand All @@ -18,6 +21,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

### Removed

- `flipperzero::furi::duration_to_ticks` in favour of `TryFrom` traits

## [0.12.0]

### Added
Expand Down
12 changes: 5 additions & 7 deletions crates/flipperzero/src/furi/message_queue.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use core::ffi::c_void;
use core::ptr::NonNull;
use core::time::Duration;

use flipperzero_sys as sys;
use flipperzero_sys::furi::{duration_to_ticks, Status};
use flipperzero_sys::furi::Status;

use crate::furi;
use crate::furi::time::Duration;

/// MessageQueue provides a safe wrapper around the furi message queue primitive.
pub struct MessageQueue<M: Sized> {
Expand All @@ -30,13 +30,12 @@ impl<M: Sized> MessageQueue<M> {
// Attempts to add the message to the end of the queue, waiting up to timeout ticks.
pub fn put(&self, msg: M, timeout: Duration) -> furi::Result<()> {
let mut msg = core::mem::ManuallyDrop::new(msg);
let timeout_ticks = duration_to_ticks(timeout);

let status: Status = unsafe {
sys::furi_message_queue_put(
self.hnd.as_ptr(),
&mut msg as *mut _ as *const c_void,
timeout_ticks,
timeout.as_ticks(),
)
.into()
};
Expand All @@ -46,13 +45,12 @@ impl<M: Sized> MessageQueue<M> {

// Attempts to read a message from the front of the queue within timeout ticks.
pub fn get(&self, timeout: Duration) -> furi::Result<M> {
let timeout_ticks = duration_to_ticks(timeout);
let mut out = core::mem::MaybeUninit::<M>::uninit();
let status: Status = unsafe {
sys::furi_message_queue_get(
self.hnd.as_ptr(),
out.as_mut_ptr() as *mut c_void,
timeout_ticks,
timeout.as_ticks(),
)
.into()
};
Expand Down Expand Up @@ -102,7 +100,7 @@ impl<M: Sized> Drop for MessageQueue<M> {

#[flipperzero_test::tests]
mod tests {
use core::time::Duration;
use super::*;

use flipperzero_sys::furi::Status;

Expand Down
29 changes: 27 additions & 2 deletions crates/flipperzero/src/furi/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use core::{
ptr::NonNull,
str,
};
use core::{time, u32};

#[cfg(feature = "alloc")]
use alloc::{
Expand All @@ -18,6 +19,8 @@ use alloc::{

use flipperzero_sys as sys;

use crate::furi::time::Duration;

#[cfg(feature = "alloc")]
const MIN_STACK_SIZE: usize = 1024;

Expand Down Expand Up @@ -191,18 +194,40 @@ pub fn yield_now() {
unsafe { sys::furi_thread_yield() };
}

/// Puts the current thread to sleep for at least the specified amount of time.
/// Puts the current thread to sleep for at least `duration`.
///
/// Durations under 1 hour are accurate to microseconds, while durations of
/// 1 hour or more are only accurate to milliseconds.
///
/// Will panic if requested to sleep for durations more than `2^32` microseconds (~49 days).
///
/// See [`sleep_ticks`] to sleep based on system timer ticks.
pub fn sleep(duration: core::time::Duration) {
if duration > time::Duration::from_millis(u32::MAX as u64) {
panic!("sleep exceeds maximum supported duration")
}

unsafe {
// For durations of 1h+, use delay_ms so uint32_t doesn't overflow
if duration < core::time::Duration::from_secs(3600) {
if duration < time::Duration::from_secs(3600) {
sys::furi_delay_us(duration.as_micros() as u32);
} else {
sys::furi_delay_ms(duration.as_millis() as u32);
}
}
}

/// Puts the current thread to sleep for at least `duration`.
///
/// The maximum supported duration is `2^32` ticks (system timer dependent).
///
/// See [`sleep`] to sleep based on arbitary duration.
pub fn sleep_ticks(duration: Duration) {
dcoles marked this conversation as resolved.
Show resolved Hide resolved
unsafe {
sys::furi_delay_tick(duration.as_ticks());
}
}

/// A unique identifier for a running thread.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
Expand Down
50 changes: 48 additions & 2 deletions crates/flipperzero/src/furi/time.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use core::cmp::Ordering;
use core::error;
use core::fmt;
use core::iter::Sum;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
use core::time;

use flipperzero_sys as sys;
use ufmt::derive::uDebug;
Expand Down Expand Up @@ -269,6 +272,12 @@ impl Duration {
self.0 == 0
}

/// Duration as Furi Kernel ticks.
#[inline]
pub const fn as_ticks(&self) -> u32 {
self.0
}

/// Returns the total number of whole seconds contained by this `Duration`.
#[inline]
#[must_use]
Expand Down Expand Up @@ -464,9 +473,36 @@ impl<'a> Sum<&'a Duration> for Duration {
}
}

#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, uDebug)]
pub struct TryFromDurationError;

impl error::Error for TryFromDurationError {}

impl fmt::Display for TryFromDurationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("duration exceeds supported representation")
}
}

impl TryFrom<time::Duration> for Duration {
type Error = TryFromDurationError;

fn try_from(value: time::Duration) -> Result<Self, Self::Error> {
let nanos: u64 = value
.as_nanos()
.try_into()
.map_err(|_| TryFromDurationError)?;
let ticks: u32 = ns_to_ticks(nanos)
.try_into()
.map_err(|_| TryFromDurationError)?;

Ok(Duration(ticks))
}
}

#[flipperzero_test::tests]
mod tests {
use super::{ticks_to_ns, Duration, Instant, MAX_INTERVAL_DURATION_TICKS};
use super::*;
use crate::println;

#[cfg(feature = "alloc")]
Expand Down Expand Up @@ -602,7 +638,7 @@ mod tests {
// Check that the same result occurs when adding/subtracting each duration one at a time as when
// adding/subtracting them all at once.
#[track_caller]
fn check<T: Eq + Copy + core::fmt::Debug>(
fn check<T: Eq + Copy + fmt::Debug>(
start: Option<T>,
op: impl Fn(&T, Duration) -> Option<T>,
) {
Expand All @@ -622,4 +658,14 @@ mod tests {
check(instant.checked_add(Duration(100)), Instant::checked_sub);
check(instant.checked_add(Duration::MAX), Instant::checked_sub);
}

#[test]
fn duration_try_from() {
assert_eq!(Duration::try_from(time::Duration::ZERO), Ok(Duration(0)));

assert_eq!(
Duration::try_from(time::Duration::MAX),
Err(TryFromDurationError)
)
}
}
10 changes: 0 additions & 10 deletions crates/sys/src/furi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

use core::ffi::c_char;
use core::fmt::Display;
use core::time::Duration;

/// Operation status.
/// The Furi API switches between using `enum FuriStatus`, `int32_t` and `uint32_t`.
Expand Down Expand Up @@ -127,12 +126,3 @@ impl<T> Drop for UnsafeRecord<T> {
}
}
}

/// Convert [`Duration`] to ticks.
#[inline]
pub fn duration_to_ticks(duration: Duration) -> u32 {
// This maxes out at about 50 days
let duration_ms: u32 = duration.as_millis().try_into().unwrap_or(u32::MAX);

unsafe { crate::furi_ms_to_ticks(duration_ms) }
}
Loading