use core::cell::UnsafeCell;
use crate::platform::*;
pub const IDLE: u32 = 0b00;
pub const WAIT: u32 = 0b01;
pub const SENT: u32 = 0b10;
#[cfg(not(feature = "shuttle"))]
pub struct Signal<T = ()> {
state: AtomicU32,
value: UnsafeCell<Option<T>>,
}
#[cfg(not(feature = "shuttle"))]
impl<T: Send> Signal<T> {
pub fn new() -> Self {
Self {
state: AtomicU32::new(IDLE),
value: UnsafeCell::new(None),
}
}
pub unsafe fn try_recv(&self) -> Option<T> {
if self.state.load(Ordering::Acquire) & SENT != 0 {
let value_ref = unsafe { &mut *self.value.get() };
let value = value_ref.take().unwrap();
Some(value)
} else {
None
}
}
#[cold]
pub unsafe fn recv(&self) -> T {
loop {
let state = self.state.fetch_or(WAIT, Ordering::Acquire);
if state & SENT != 0 {
let value_ref = unsafe { &mut *self.value.get() };
return value_ref.take().unwrap();
}
atomic_wait::wait(&self.state, state);
}
}
#[inline(always)]
pub unsafe fn send(signal: *const Self, value: T) {
let state = unsafe { (*signal).state.load(Ordering::Relaxed) };
if state & SENT != 0 {
panic!("attempted to send value over signal, but signal has already been sent");
}
let value_ref = unsafe { &(*signal).value };
unsafe { *value_ref.get() = Some(value) };
let state = unsafe { (*signal).state.fetch_or(SENT, Ordering::Release) };
if state & WAIT != 0 {
atomic_wait::wake_one(unsafe { &(*signal).state });
}
}
}
impl<T: Send> Default for Signal<T> {
fn default() -> Self {
Self::new()
}
}
unsafe impl<T: Send> Sync for Signal<T> {}
#[cfg(feature = "shuttle")]
pub struct Signal<T = ()> {
mutex: Mutex<Option<T>>,
condvar: Condvar,
}
#[cfg(feature = "shuttle")]
impl<T: Send> Signal<T> {
pub fn new() -> Self {
Self {
mutex: Mutex::new(None),
condvar: Condvar::new(),
}
}
pub unsafe fn try_recv(&self) -> Option<T> {
self.mutex.lock().unwrap().take()
}
#[cold]
pub unsafe fn recv(&self) -> T {
let mut state = self.mutex.lock().unwrap();
loop {
match state.take() {
Some(value) => return value,
None => state = self.condvar.wait(state).unwrap(),
}
}
}
#[inline(always)]
pub unsafe fn send(signal: *const Self, value: T) {
let state = unsafe { &*signal };
*state.mutex.lock().unwrap() = Some(value);
state.condvar.notify_all();
}
}