diff --git a/.gitignore b/.gitignore index 25d18d5a..784c6bac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -/target +target Cargo.lock third_party diff --git a/embassy-std/src/lib.rs b/embassy-std/src/lib.rs index 2fb814b4..009ba028 100644 --- a/embassy-std/src/lib.rs +++ b/embassy-std/src/lib.rs @@ -1,9 +1,10 @@ -use embassy::executor::Executor; +use embassy::executor::{raw, Spawner}; use embassy::time::TICKS_PER_SECOND; use embassy::time::{Alarm, Clock}; -use embassy::util::Forever; use rand_core::{OsRng, RngCore}; +use std::marker::PhantomData; use std::mem::MaybeUninit; +use std::ptr; use std::sync::{Condvar, Mutex}; use std::time::{Duration as StdDuration, Instant as StdInstant}; @@ -29,7 +30,8 @@ static mut ALARM_AT: u64 = u64::MAX; pub struct StdAlarm; impl Alarm for StdAlarm { - fn set_callback(&self, _callback: fn()) {} + fn set_callback(&self, _callback: fn(*mut ()), _ctx: *mut ()) {} + fn set(&self, timestamp: u64) { unsafe { ALARM_AT = timestamp } } @@ -39,57 +41,91 @@ impl Alarm for StdAlarm { } } -static EXECUTOR: Forever = Forever::new(); - -lazy_static::lazy_static! { - static ref MUTEX: Mutex = Mutex::new(false); - static ref CONDVAR: Condvar = Condvar::new(); +struct Signaler { + mutex: Mutex, + condvar: Condvar, } -pub fn init() -> &'static Executor { - unsafe { - CLOCK_ZERO.as_mut_ptr().write(StdInstant::now()); - embassy::time::set_clock(&StdClock); - embassy::rand::set_rand(&StdRand); +impl Signaler { + fn new() -> Self { + Self { + mutex: Mutex::new(false), + condvar: Condvar::new(), + } + } - EXECUTOR.put(Executor::new_with_alarm(&StdAlarm, || { - let mut signaled = MUTEX.lock().unwrap(); - *signaled = true; - CONDVAR.notify_one(); - })) + fn wait(&self) { + let mut signaled = self.mutex.lock().unwrap(); + while !*signaled { + let alarm_at = unsafe { ALARM_AT }; + if alarm_at == u64::MAX { + signaled = self.condvar.wait(signaled).unwrap(); + } else { + let now = StdClock.now(); + if now >= alarm_at { + break; + } + + let left = alarm_at - now; + let dur = StdDuration::new( + left / (TICKS_PER_SECOND as u64), + (left % (TICKS_PER_SECOND as u64) * 1_000_000_000 / (TICKS_PER_SECOND as u64)) + as u32, + ); + let (signaled2, timeout) = self.condvar.wait_timeout(signaled, dur).unwrap(); + signaled = signaled2; + if timeout.timed_out() { + break; + } + } + } + *signaled = false; + } + + fn signal(ctx: *mut ()) { + let this = unsafe { &*(ctx as *mut Self) }; + let mut signaled = this.mutex.lock().unwrap(); + *signaled = true; + this.condvar.notify_one(); } } -pub fn run(executor: &'static Executor) -> ! { - unsafe { +pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + signaler: Signaler, +} + +impl Executor { + pub fn new() -> Self { + unsafe { + CLOCK_ZERO.as_mut_ptr().write(StdInstant::now()); + embassy::time::set_clock(&StdClock); + embassy::rand::set_rand(&StdRand); + } + + Self { + inner: raw::Executor::new(Signaler::signal, ptr::null_mut()), + not_send: PhantomData, + signaler: Signaler::new(), + } + } + + pub fn set_alarm(&mut self, alarm: &'static dyn Alarm) { + self.inner.set_alarm(alarm); + } + + /// Runs the executor. + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + self.inner.set_signal_ctx(&self.signaler as *const _ as _); + + init(unsafe { self.inner.spawner() }); + loop { - executor.run(); - - let mut signaled = MUTEX.lock().unwrap(); - while !*signaled { - let alarm_at = ALARM_AT; - if alarm_at == u64::MAX { - signaled = CONDVAR.wait(signaled).unwrap(); - } else { - let now = StdClock.now(); - if now >= alarm_at { - break; - } - - let left = alarm_at - now; - let dur = StdDuration::new( - left / (TICKS_PER_SECOND as u64), - (left % (TICKS_PER_SECOND as u64) * 1_000_000_000 - / (TICKS_PER_SECOND as u64)) as u32, - ); - let (signaled2, timeout) = CONDVAR.wait_timeout(signaled, dur).unwrap(); - signaled = signaled2; - if timeout.timed_out() { - break; - } - } - } - *signaled = false; + unsafe { self.inner.run_queued() }; + self.signaler.wait(); } } } diff --git a/embassy/src/executor/mod.rs b/embassy/src/executor/mod.rs index 7c74fa58..6e4bded0 100644 --- a/embassy/src/executor/mod.rs +++ b/embassy/src/executor/mod.rs @@ -113,13 +113,6 @@ pub struct Spawner { } impl Spawner { - fn new(executor: &'static raw::Executor) -> Self { - Self { - executor, - not_send: PhantomData, - } - } - pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { let task = token.raw_task; mem::forget(token); @@ -165,13 +158,6 @@ unsafe impl Sync for SendSpawner {} /// /// If you want to spawn tasks from another thread, use [SendSpawner]. impl SendSpawner { - fn new(executor: &'static raw::Executor) -> Self { - Self { - executor, - not_send: PhantomData, - } - } - pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { let header = token.raw_task; mem::forget(token); @@ -207,7 +193,7 @@ impl Executor { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(Spawner::new(&self.inner)); + init(unsafe { self.inner.spawner() }); loop { unsafe { self.inner.run_queued() }; @@ -253,7 +239,7 @@ impl IrqExecutor { pub fn start(&'static mut self, init: impl FnOnce(Spawner) + Send) { self.irq.disable(); - init(Spawner::new(&self.inner)); + init(unsafe { self.inner.spawner() }); self.irq.set_handler( |ctx| unsafe { diff --git a/embassy/src/executor/raw.rs b/embassy/src/executor/raw.rs index 927b6a42..1f7e48f5 100644 --- a/embassy/src/executor/raw.rs +++ b/embassy/src/executor/raw.rs @@ -1,5 +1,6 @@ use core::cell::Cell; use core::cmp::min; +use core::marker::PhantomData; use core::ptr; use core::ptr::NonNull; use core::sync::atomic::{AtomicU32, Ordering}; @@ -67,7 +68,7 @@ impl Task { } } -pub(crate) struct Executor { +pub struct Executor { run_queue: RunQueue, timer_queue: TimerQueue, signal_fn: fn(*mut ()), @@ -76,7 +77,7 @@ pub(crate) struct Executor { } impl Executor { - pub(crate) const fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { + pub const fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { Self { run_queue: RunQueue::new(), timer_queue: TimerQueue::new(), @@ -86,23 +87,27 @@ impl Executor { } } - pub(crate) fn set_alarm(&mut self, alarm: &'static dyn Alarm) { + pub fn set_alarm(&mut self, alarm: &'static dyn Alarm) { self.alarm = Some(alarm); } + pub fn set_signal_ctx(&mut self, signal_ctx: *mut ()) { + self.signal_ctx = signal_ctx; + } + unsafe fn enqueue(&self, item: *mut Task) { if self.run_queue.enqueue(item) { (self.signal_fn)(self.signal_ctx) } } - pub(crate) unsafe fn spawn(&'static self, task: NonNull) { + pub unsafe fn spawn(&'static self, task: NonNull) { let task = task.as_ref(); task.executor.set(self); self.enqueue(task as *const _ as _); } - pub(crate) unsafe fn run_queued(&self) { + pub unsafe fn run_queued(&'static self) { if self.alarm.is_some() { self.timer_queue.dequeue_expired(Instant::now(), |p| { p.as_ref().enqueue(); @@ -138,6 +143,13 @@ impl Executor { alarm.set(next_expiration.as_ticks()); } } + + pub unsafe fn spawner(&'static self) -> super::Spawner { + super::Spawner { + executor: self, + not_send: PhantomData, + } + } } pub use super::waker::task_from_waker;