104 lines
3.1 KiB
Rust
104 lines
3.1 KiB
Rust
|
use core::arch::asm;
|
||
|
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
|
||
|
|
||
|
// Must be kept in sync with the layout of `State`!
|
||
|
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
||
|
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8;
|
||
|
|
||
|
#[repr(C, align(4))]
|
||
|
pub(crate) struct State {
|
||
|
/// Task is spawned (has a future)
|
||
|
spawned: AtomicBool,
|
||
|
/// Task is in the executor run queue
|
||
|
run_queued: AtomicBool,
|
||
|
/// Task is in the executor timer queue
|
||
|
timer_queued: AtomicBool,
|
||
|
pad: AtomicBool,
|
||
|
}
|
||
|
|
||
|
impl State {
|
||
|
pub const fn new() -> State {
|
||
|
Self {
|
||
|
spawned: AtomicBool::new(false),
|
||
|
run_queued: AtomicBool::new(false),
|
||
|
timer_queued: AtomicBool::new(false),
|
||
|
pad: AtomicBool::new(false),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn as_u32(&self) -> &AtomicU32 {
|
||
|
unsafe { &*(self as *const _ as *const AtomicU32) }
|
||
|
}
|
||
|
|
||
|
/// If task is idle, mark it as spawned + run_queued and return true.
|
||
|
#[inline(always)]
|
||
|
pub fn spawn(&self) -> bool {
|
||
|
compiler_fence(Ordering::Release);
|
||
|
let r = self
|
||
|
.as_u32()
|
||
|
.compare_exchange(
|
||
|
0,
|
||
|
STATE_SPAWNED | STATE_RUN_QUEUED,
|
||
|
Ordering::Relaxed,
|
||
|
Ordering::Relaxed,
|
||
|
)
|
||
|
.is_ok();
|
||
|
compiler_fence(Ordering::Acquire);
|
||
|
r
|
||
|
}
|
||
|
|
||
|
/// Unmark the task as spawned.
|
||
|
#[inline(always)]
|
||
|
pub fn despawn(&self) {
|
||
|
compiler_fence(Ordering::Release);
|
||
|
self.spawned.store(false, Ordering::Relaxed);
|
||
|
}
|
||
|
|
||
|
/// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success.
|
||
|
#[inline(always)]
|
||
|
pub fn run_enqueue(&self) -> bool {
|
||
|
unsafe {
|
||
|
loop {
|
||
|
let state: u32;
|
||
|
asm!("ldrex {}, [{}]", out(reg) state, in(reg) self, options(nostack));
|
||
|
|
||
|
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
|
||
|
asm!("clrex", options(nomem, nostack));
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
let outcome: usize;
|
||
|
let new_state = state | STATE_RUN_QUEUED;
|
||
|
asm!("strex {}, {}, [{}]", out(reg) outcome, in(reg) new_state, in(reg) self, options(nostack));
|
||
|
if outcome == 0 {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Unmark the task as run-queued. Return whether the task is spawned.
|
||
|
#[inline(always)]
|
||
|
pub fn run_dequeue(&self) -> bool {
|
||
|
compiler_fence(Ordering::Release);
|
||
|
|
||
|
let r = self.spawned.load(Ordering::Relaxed);
|
||
|
self.run_queued.store(false, Ordering::Relaxed);
|
||
|
r
|
||
|
}
|
||
|
|
||
|
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
|
||
|
#[cfg(feature = "integrated-timers")]
|
||
|
#[inline(always)]
|
||
|
pub fn timer_enqueue(&self) -> bool {
|
||
|
!self.timer_queued.swap(true, Ordering::Relaxed)
|
||
|
}
|
||
|
|
||
|
/// Unmark the task as timer-queued.
|
||
|
#[cfg(feature = "integrated-timers")]
|
||
|
#[inline(always)]
|
||
|
pub fn timer_dequeue(&self) {
|
||
|
self.timer_queued.store(false, Ordering::Relaxed);
|
||
|
}
|
||
|
}
|