Remove Forever, switch to static_cell.

This commit is contained in:
Dario Nieuwenhuis
2022-08-22 15:51:44 +02:00
parent 1b95990258
commit 478f472784
45 changed files with 139 additions and 220 deletions

View File

@ -31,3 +31,4 @@ futures-util = { version = "0.3.17", features = [ "channel" ] }
# Enable critical-section implementation for std, for tests
critical-section = { version = "1.1", features = ["std"] }
static_cell = "1.0"

View File

@ -471,10 +471,10 @@ mod tests {
use futures_executor::ThreadPool;
use futures_timer::Delay;
use futures_util::task::SpawnExt;
use static_cell::StaticCell;
use super::*;
use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
use crate::Forever;
fn capacity<T, const N: usize>(c: &ChannelState<T, N>) -> usize {
c.queue.capacity() - c.queue.len()
@ -549,8 +549,8 @@ mod tests {
async fn receiver_receives_given_try_send_async() {
let executor = ThreadPool::new().unwrap();
static CHANNEL: Forever<Channel<CriticalSectionRawMutex, u32, 3>> = Forever::new();
let c = &*CHANNEL.put(Channel::new());
static CHANNEL: StaticCell<Channel<CriticalSectionRawMutex, u32, 3>> = StaticCell::new();
let c = &*CHANNEL.init(Channel::new());
let c2 = c;
assert!(executor
.spawn(async move {
@ -571,8 +571,8 @@ mod tests {
async fn senders_sends_wait_until_capacity() {
let executor = ThreadPool::new().unwrap();
static CHANNEL: Forever<Channel<CriticalSectionRawMutex, u32, 1>> = Forever::new();
let c = &*CHANNEL.put(Channel::new());
static CHANNEL: StaticCell<Channel<CriticalSectionRawMutex, u32, 1>> = StaticCell::new();
let c = &*CHANNEL.init(Channel::new());
assert!(c.try_send(1).is_ok());
let c2 = c;

View File

@ -1,95 +0,0 @@
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use atomic_polyfill::{AtomicBool, Ordering};
/// Type with static lifetime that may be written to once at runtime.
///
/// This may be used to initialize static objects at runtime, typically in the init routine.
/// This is useful for objects such as Embassy's RTC, which cannot be initialized in a const
/// context.
///
/// Note: IF a global mutable variable is desired, use a CriticalSectionMutex or ThreadModeMutex instead.
///
/// ```
/// use embassy_util::Forever;
/// // Using an integer for the sake of keeping this example self-contained,
/// // see https://github.com/embassy-rs/embassy/wiki/Getting-Started for a more "proper" example.
/// static SOME_INT: Forever<u32> =Forever::new();
///
/// // put returns a mutable pointer to the object stored in the forever, which may then be passed
/// // around.
/// let mut x = SOME_INT.put(42);
/// assert_eq!(*x, 42);
/// ```
pub struct Forever<T> {
used: AtomicBool,
t: UnsafeCell<MaybeUninit<T>>,
}
unsafe impl<T> Send for Forever<T> {}
unsafe impl<T> Sync for Forever<T> {}
impl<T> Forever<T> {
/// Create a new `Forever`.
#[inline(always)]
pub const fn new() -> Self {
Self {
used: AtomicBool::new(false),
t: UnsafeCell::new(MaybeUninit::uninit()),
}
}
/// Store a value in this `Forever`, returning a mutable reference to it.
///
/// Using this method, the compiler usually constructs `val` in the stack and then moves
/// it into the `Forever`. If `T` is big, this is likely to cause stack overflows.
/// Considering using [`Signal::put_with`] instead, which will construct it in-place inside the `Forever`.
///
/// # Panics
///
/// Panics if this `Forever` already has a value stored in it.
#[inline(always)]
#[allow(clippy::mut_from_ref)]
pub fn put(&'static self, val: T) -> &'static mut T {
self.put_with(|| val)
}
/// Store the closure return value in this `Forever`, returning a mutable reference to it.
///
/// The advantage over [`Forever::put`] is that this method allows the closure to construct
/// the `T` value in-place directly inside the `Forever`, saving stack space.
///
/// # Panics
///
/// Panics if this `Forever` already has a value stored in it.
#[inline(always)]
#[allow(clippy::mut_from_ref)]
pub fn put_with(&'static self, val: impl FnOnce() -> T) -> &'static mut T {
if self
.used
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_err()
{
panic!("Forever.put() called multiple times");
}
let p: &'static mut MaybeUninit<T> = unsafe { &mut *self.t.get() };
p.write(val())
}
/// Unsafely get a mutable reference to the contents of this Forever.
///
/// # Safety
///
/// This is undefined behavior if:
///
/// - The `Forever` has not been initialized yet (with `put' or `put_with`), or
/// - A reference to the contents (mutable or not) already exists.
#[inline(always)]
#[allow(clippy::mut_from_ref)]
pub unsafe fn steal(&self) -> &mut T {
let p: &mut MaybeUninit<T> = &mut *self.t.get();
p.assume_init_mut()
}
}

View File

@ -16,10 +16,8 @@ pub mod mutex;
pub mod pipe;
pub mod waitqueue;
mod forever;
mod select;
mod yield_now;
pub use forever::*;
pub use select::*;
pub use yield_now::*;

View File

@ -461,10 +461,10 @@ mod io_impls {
mod tests {
use futures_executor::ThreadPool;
use futures_util::task::SpawnExt;
use static_cell::StaticCell;
use super::*;
use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex};
use crate::Forever;
fn capacity<const N: usize>(c: &PipeState<N>) -> usize {
N - c.buffer.len()
@ -528,8 +528,8 @@ mod tests {
async fn receiver_receives_given_try_write_async() {
let executor = ThreadPool::new().unwrap();
static CHANNEL: Forever<Pipe<CriticalSectionRawMutex, 3>> = Forever::new();
let c = &*CHANNEL.put(Pipe::new());
static CHANNEL: StaticCell<Pipe<CriticalSectionRawMutex, 3>> = StaticCell::new();
let c = &*CHANNEL.init(Pipe::new());
let c2 = c;
let f = async move {
assert_eq!(c2.try_write(&[42]), Ok(1));