use alloc::{boxed::Box, format, string::String};
use core::{
cmp::min,
convert::TryInto,
fmt::{self, Debug, Display, Formatter},
marker::PhantomData,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
time::Duration,
};
use cstring_interop::{from_cstring_raw, with_cstring};
use libc::c_void;
use crate::{
bindings,
error::{Error, SentinelError},
};
const TIMEOUT_MAX: u32 = 0xffffffff;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Instant(u64);
impl Instant {
#[inline]
pub fn from_micros(micros: u64) -> Self {
Self(micros)
}
#[inline]
pub fn from_millis(millis: u64) -> Self {
Self(
millis
.checked_mul(1000)
.expect("overflow when creating instant from seconds"),
)
}
pub fn from_secs(secs: u64) -> Self {
Self(
secs.checked_mul(1000000)
.expect("overflow when creating instant from seconds"),
)
}
#[inline]
pub fn as_millis(&self) -> u64 {
self.0 / 1000
}
#[inline]
pub fn as_secs(&self) -> u64 {
self.0 / 1000000
}
#[inline]
pub fn as_micros(&self) -> u64 {
self.0
}
#[inline]
pub fn subsec_millis(&self) -> u64 {
self.0 % 1000000 / 1000
}
#[inline]
pub fn subsec_micros(&self) -> u64 {
self.0 % 1000000
}
#[inline]
pub fn checked_add(self, rhs: Duration) -> Option<Self> {
Some(Self(self.0.checked_add(rhs.as_micros().try_into().ok()?)?))
}
#[inline]
pub fn checked_sub(self, rhs: Duration) -> Option<Instant> {
Some(Self(self.0.checked_sub(rhs.as_micros().try_into().ok()?)?))
}
#[inline]
pub fn checked_sub_instant(self, rhs: Self) -> Option<Duration> {
Some(Duration::from_micros(self.0.checked_sub(rhs.0)?))
}
#[inline]
pub fn checked_mul(self, rhs: u64) -> Option<Instant> {
Some(Self(self.0.checked_mul(rhs)?))
}
}
impl Add<Duration> for Instant {
type Output = Instant;
fn add(self, rhs: Duration) -> Self::Output {
self.checked_add(rhs)
.expect("overflow when adding duration to instant")
}
}
impl Sub<Duration> for Instant {
type Output = Instant;
fn sub(self, rhs: Duration) -> Self::Output {
self.checked_sub(rhs)
.expect("overflow when subtracting duration from instant")
}
}
impl Sub for Instant {
type Output = Duration;
fn sub(self, rhs: Self) -> Self::Output {
self.checked_sub_instant(rhs)
.expect("overflow when subtracting instants")
}
}
impl Mul<u64> for Instant {
type Output = Instant;
fn mul(self, rhs: u64) -> Self::Output {
self.checked_mul(rhs)
.expect("overflow when multiplying instant by scalar")
}
}
impl Div<u64> for Instant {
type Output = Instant;
#[inline]
fn div(self, rhs: u64) -> Self::Output {
Self(self.0 / rhs)
}
}
impl AddAssign<Duration> for Instant {
fn add_assign(&mut self, rhs: Duration) {
*self = *self + rhs;
}
}
impl SubAssign<Duration> for Instant {
fn sub_assign(&mut self, rhs: Duration) {
*self = *self - rhs;
}
}
impl MulAssign<u64> for Instant {
fn mul_assign(&mut self, rhs: u64) {
*self = *self * rhs;
}
}
impl DivAssign<u64> for Instant {
fn div_assign(&mut self, rhs: u64) {
*self = *self / rhs;
}
}
impl Debug for Instant {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}.{:06}s", self.0 / 1000000, self.0 % 1000000)
}
}
impl Display for Instant {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}.{:06}s", self.0 / 1000000, self.0 % 1000000)
}
}
#[inline]
pub fn time_since_start() -> Instant {
Instant::from_micros(unsafe { bindings::micros() })
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Task(bindings::task_t);
impl Task {
pub const DEFAULT_PRIORITY: u32 = bindings::TASK_PRIORITY_DEFAULT;
pub const DEFAULT_STACK_DEPTH: u16 = bindings::TASK_STACK_DEPTH_DEFAULT as u16;
#[inline]
pub fn delay(dur: Duration) {
unsafe {
bindings::task_delay(dur.as_millis() as u32);
}
}
#[inline]
pub fn current() -> Task {
Task(unsafe { bindings::task_get_current() })
}
pub fn find_by_name(name: &str) -> Result<Task, Error> {
let ptr = (with_cstring(name.into(), |name| unsafe {
bindings::task_get_by_name(name.into_raw()).check()
}) as Result<*mut c_void, Error>)?;
if ptr.is_null() {
Err(Error::Custom(format!("task not found: {}", name)))
} else {
Ok(Task(ptr))
}
}
#[inline]
pub fn spawn<F>(f: F) -> Result<Task, Error>
where
F: FnOnce() + Send + 'static,
{
Task::spawn_ext("", Self::DEFAULT_PRIORITY, Self::DEFAULT_STACK_DEPTH, f)
}
pub fn spawn_ext<F>(name: &str, priority: u32, stack_depth: u16, f: F) -> Result<Task, Error>
where
F: FnOnce() + Send + 'static,
{
extern "C" fn run<F: FnOnce()>(arg: *mut libc::c_void) {
let cb_box: Box<F> = unsafe { Box::from_raw(arg as *mut F) };
cb_box()
}
let cb = Box::new(f);
unsafe {
let arg = Box::into_raw(cb);
let r = Task::spawn_raw(name, priority, stack_depth, run::<F>, arg as *mut _);
if r.is_err() {
Box::from_raw(arg);
}
r
}
}
#[inline]
pub fn spawn_raw(
name: &str,
priority: u32,
stack_depth: u16,
f: unsafe extern "C" fn(arg1: *mut libc::c_void),
arg: *mut libc::c_void,
) -> Result<Task, Error> {
with_cstring(name.into(), |cname| {
Ok(Task(
unsafe {
bindings::task_create(Some(f), arg, priority, stack_depth, cname.into_raw())
}
.check()?,
))
})
}
#[inline]
pub fn name(&self) -> String {
unsafe { from_cstring_raw(bindings::task_get_name(self.0)) }
}
#[inline]
pub fn priority(&self) -> u32 {
unsafe { bindings::task_get_priority(self.0) }
}
#[inline]
pub fn state(&self) -> TaskState {
match unsafe { bindings::task_get_state(self.0) } {
bindings::task_state_e_t_E_TASK_STATE_RUNNING => TaskState::Running,
bindings::task_state_e_t_E_TASK_STATE_READY => TaskState::Ready,
bindings::task_state_e_t_E_TASK_STATE_BLOCKED => TaskState::Blocked,
bindings::task_state_e_t_E_TASK_STATE_SUSPENDED => TaskState::Suspended,
bindings::task_state_e_t_E_TASK_STATE_DELETED => TaskState::Deleted,
bindings::task_state_e_t_E_TASK_STATE_INVALID => {
panic!("invalid task handle: {:#010x}", self.0 as usize)
}
x => panic!("bindings::task_get_state returned unexpected value: {}", x),
}
}
#[inline]
pub unsafe fn delete(&self) {
bindings::task_delete(self.0)
}
}
impl Debug for Task {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Task")
.field("name", &self.name())
.field("priority", &self.priority())
.finish()
}
}
unsafe impl Send for Task {}
unsafe impl Sync for Task {}
pub enum TaskState {
Running,
Ready,
Blocked,
Suspended,
Deleted,
}
#[derive(Copy, Clone, Debug)]
pub enum GenericSleep {
NotifyTake(Option<Instant>),
Timestamp(Instant),
}
impl GenericSleep {
pub fn sleep(self) -> u32 {
match self {
GenericSleep::NotifyTake(timeout) => {
let timeout = timeout.map_or(TIMEOUT_MAX, |v| {
v.checked_sub_instant(time_since_start())
.map_or(0, |d| d.as_millis() as u32)
});
unsafe { bindings::task_notify_take(true, timeout) }
}
GenericSleep::Timestamp(v) => {
if let Some(d) = v.checked_sub_instant(time_since_start()) {
Task::delay(d);
}
0
}
}
}
#[inline]
pub fn timeout(self) -> Option<Instant> {
match self {
GenericSleep::NotifyTake(v) => v,
GenericSleep::Timestamp(v) => Some(v),
}
}
pub fn combine(self, other: Self) -> Self {
match (self, other) {
(GenericSleep::Timestamp(a), GenericSleep::Timestamp(b)) => {
GenericSleep::Timestamp(core::cmp::min(a, b))
}
(a, b) => GenericSleep::NotifyTake(
a.timeout()
.map_or(b.timeout(), |a| Some(b.timeout().map_or(a, |b| min(a, b)))),
),
}
}
}
pub trait Selectable<T = ()>: Sized {
fn poll(self) -> Result<T, Self>;
fn sleep(&self) -> GenericSleep;
}
#[inline]
pub fn select_map<'a, T: 'a, U: 'a, F: 'a + FnOnce(T) -> U>(
event: impl Selectable<T> + 'a,
f: F,
) -> impl Selectable<U> + 'a {
struct MapSelect<T, U, E: Selectable<T>, F: FnOnce(T) -> U> {
event: E,
f: F,
_t: PhantomData<T>,
}
impl<T, U, E: Selectable<T>, F: FnOnce(T) -> U> Selectable<U> for MapSelect<T, U, E, F> {
fn poll(self) -> Result<U, Self> {
match self.event.poll() {
Ok(r) => Ok((self.f)(r)),
Err(event) => Err(Self {
event,
f: self.f,
_t: PhantomData,
}),
}
}
fn sleep(&self) -> GenericSleep {
self.event.sleep()
}
}
MapSelect {
event,
f,
_t: PhantomData,
}
}
#[inline]
pub fn select_either<'a, T: 'a>(
fst: impl Selectable<T> + 'a,
snd: impl Selectable<T> + 'a,
) -> impl Selectable<T> + 'a {
struct EitherSelect<T, E1: Selectable<T>, E2: Selectable<T>>(E1, E2, PhantomData<T>);
impl<T, E1: Selectable<T>, E2: Selectable<T>> Selectable<T> for EitherSelect<T, E1, E2> {
fn poll(self) -> Result<T, Self> {
Err(Self(
match self.0.poll() {
Ok(r) => return Ok(r),
Err(e) => e,
},
match self.1.poll() {
Ok(r) => return Ok(r),
Err(e) => e,
},
PhantomData,
))
}
fn sleep(&self) -> GenericSleep {
self.0.sleep().combine(self.1.sleep())
}
}
EitherSelect(fst, snd, PhantomData)
}
#[inline]
pub fn delay(time: Duration) -> impl Selectable {
delay_until(time_since_start() + time)
}
#[inline]
pub fn delay_until(timestamp: Instant) -> impl Selectable {
struct DelaySelect(Instant);
impl Selectable for DelaySelect {
fn poll(self) -> Result<(), Self> {
if time_since_start() >= self.0 {
Ok(())
} else {
Err(self)
}
}
fn sleep(&self) -> GenericSleep {
GenericSleep::Timestamp(self.0)
}
}
DelaySelect(timestamp)
}
mod broadcast;
mod channel;
mod context;
mod event;
mod r#loop;
mod mutex;
mod promise;
mod semaphore;
pub use broadcast::*;
pub use channel::*;
pub use context::*;
pub use event::*;
pub use mutex::*;
pub use promise::*;
pub use r#loop::*;
pub use semaphore::*;