diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml index 99840e7e477f802bded59a72c2ad269b238b7907..56348ec25b01f6745653ab96807f2a8641e8c0e2 100644 --- a/embassy/Cargo.toml +++ b/embassy/Cargo.toml @@ -22,3 +22,7 @@ defmt = { version = "0.3", optional = true } [features] defmt = ["dep:defmt"] + +# Remove any use of `unsafe`. This currently requires +# nightly and -Zpolonius (not ready yet) +try-polonius = [] diff --git a/embassy/src/embassy_sunset.rs b/embassy/src/embassy_sunset.rs index 3fd31c99e030e29f66308bf652887941d1ada727..f0221b25e56a539b1575bedf57a67c0d313dd775 100644 --- a/embassy/src/embassy_sunset.rs +++ b/embassy/src/embassy_sunset.rs @@ -301,48 +301,79 @@ impl<'a> EmbassySunset<'a> { Ok(()) } + /// Returns an `Event` once one is ready. + /// + /// The returned `Event` borrows from the mutex locked in `ph`. pub(crate) async fn progress<'g, 'f>(&'g self, ph: &'f mut ProgressHolder<'g, 'a>) -> Result<Event<'f, 'a>> { + let guard = &mut ph.g; + *guard = None; + + #[cfg(not(feature = "try-polonius"))] + let guardptr = guard as *mut Option<MutexGuard<'g, SunsetRawMutex, Inner<'a>>>; - // poll until we get an actual event to return - let phcopy = ph as *mut ProgressHolder; + // poll progress until we get an actual event to return loop { - // TODO where does this go? + debug_assert!(guard.is_none()); + + // Safety: At the start of the loop iteration nothing is borrowing from + // guard, it is set to None. We dereference through a pointer to lose the 'f + // bound which applies to the Event::Cli/Event::Serv returned variants, + // but not other match arms. + // + // Once polonius is implemented this is unnecessary. polonius-the-crab + // can't be used since it would require an async closure. + #[cfg(not(feature = "try-polonius"))] + let guard = unsafe { &mut *guardptr }; + if self.exit.load(Relaxed) { return Ok(Event::Defunct); } - // let ph = unsafe { &mut *phcopy }; - ph.g = None; - let inner = ph.g.insert(self.inner.lock().await); - self.clear_refcounts(inner)?; - - let ev = inner.runner.progress()?; - trace!("ev {ev:?}"); - match ev { - Event::Cli(_) => return Ok(ev), - Event::Serv(_) => return Ok(ev), - Event::Progressed => continue, - Event::None => (), - _ => todo!("probably a bug"), - } + let idle = { + let inner = guard.insert(self.inner.lock().await); - // Idle until input is received + // Drop any finished channels now we have the lock + self.clear_refcounts(inner)?; + + let ev = inner.runner.progress()?; + trace!("ev {ev:?}"); + + match ev { + // Return borrowed Cli/Serv directly, with a Event<'f, 'a> bound. + Event::Cli(_) => return Ok(ev), + Event::Serv(_) => return Ok(ev), + Event::Progressed => false, + Event::None => true, + _ => todo!("probably a bug"), + } + }; + + // Safety: No borrows of guard remain, can lose the inferred 'f lifetime. + // Not required after polonius. + #[cfg(not(feature = "try-polonius"))] + let guard = unsafe { &mut *guardptr }; + + // Drop the Mutex + *guard = None; // self.wake_channels(inner)?; if self.moribund.load(Relaxed) { // if we're flushing, we exit once there is no progress return Ok(Event::Defunct); } + + if !idle { + // Run runner.progress() again if we made forward progress. + continue; + } + + // Idle until input is received // TODO do we also want to wake in other situations? trace!("idle"); - // drop the mutex while we idle. - // let ph = unsafe { &mut *phcopy }; - ph.g = None; self.progress_notify.wait().await; trace!("woke"); - // TODO: ProgressHolder should be Option<mutex> } } diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs index 6fdf1f628dd77a9e6770c2e1aa2ff9a46c711f86..503efa12fd4c8263e4c932401a1352aaeea7fef2 100644 --- a/embassy/src/lib.rs +++ b/embassy/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] -// #![forbid(unsafe_code)] +#![cfg_attr(feature = "try-polonius", forbid(unsafe_code))] // avoid mysterious missing awaits #![deny(unused_must_use)]