use std::{
panic::Location,
sync::{
Arc,
atomic::{AtomicU8, Ordering},
},
task::{Wake, Waker},
};
use super::FLASH;
use crate::native::sync::Mutex;
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum TaskState {
Parked = 0,
Runnable = 1,
Running = 2,
RunningNotified = 3,
Done = 4,
}
pub(super) struct AtomicTaskState(AtomicU8);
impl AtomicTaskState {
fn new(initial: TaskState) -> Self {
Self(AtomicU8::new(initial as u8))
}
pub(super) fn compare_exchange(&self, current: TaskState, new: TaskState) -> bool {
self.0
.compare_exchange(
current as u8,
new as u8,
Ordering::AcqRel,
Ordering::Acquire,
)
.is_ok()
}
pub(super) fn load(&self) -> TaskState {
Self::unpack(self.0.load(Ordering::Acquire))
}
pub(super) fn store(&self, new: TaskState) {
self.0.store(new as u8, Ordering::Release);
}
pub(super) fn swap(&self, new: TaskState) -> TaskState {
Self::unpack(self.0.swap(new as u8, Ordering::AcqRel))
}
fn unpack(v: u8) -> TaskState {
match v {
0 => TaskState::Parked,
1 => TaskState::Runnable,
2 => TaskState::Running,
3 => TaskState::RunningNotified,
4 => TaskState::Done,
_ => unreachable!("BUG: invalid TaskState discriminant {v}"),
}
}
}
pub(super) enum ParkOutcome {
Parked,
WokenMidPoll,
}
pub(super) enum WakeOutcome {
Resumed,
NotParked,
}
pub(in crate::flash) struct TaskGate {
loc: &'static Location<'static>,
state: AtomicTaskState,
runtime_waker: Mutex<Option<Waker>>,
id: u64,
}
impl TaskGate {
pub(in crate::flash) fn new(id: u64, loc: &'static Location<'static>) -> Arc<Self> {
Arc::new(Self {
id,
loc,
state: AtomicTaskState::new(TaskState::Runnable),
runtime_waker: Mutex::default(),
})
}
pub(in crate::flash) fn complete(&self) {
FLASH.gate_complete(&self.state, self.id);
}
fn forward(&self) {
let w = self.runtime_waker.lock().clone();
if let Some(w) = w {
w.wake();
}
}
pub(in crate::flash) fn id(&self) -> u64 {
self.id
}
pub(in crate::flash) fn loc(&self) -> &'static Location<'static> {
self.loc
}
pub(in crate::flash) fn on_drop(&self) {
FLASH.gate_drop_release(&self.state, self.id);
}
pub(in crate::flash) fn park(&self) {
let _: ParkOutcome = FLASH.gate_park(&self.state, self.id);
}
pub(in crate::flash) fn store_runtime_waker(&self, w: &Waker) {
let mut g = self.runtime_waker.lock();
match g.as_ref() {
Some(existing) if existing.will_wake(w) => {}
_ => *g = Some(w.clone()),
}
}
pub(in crate::flash) fn try_enter_poll(&self) -> bool {
self.state
.compare_exchange(TaskState::Runnable, TaskState::Running)
}
}
impl Wake for TaskGate {
fn wake(self: Arc<Self>) {
self.wake_by_ref();
}
fn wake_by_ref(self: &Arc<Self>) {
loop {
match self.state.load() {
TaskState::Parked => {
match FLASH.gate_wake_parked(&self.state, self.id, self.loc) {
WakeOutcome::Resumed => {
self.forward();
return;
}
WakeOutcome::NotParked => {}
}
}
TaskState::Running => {
if self
.state
.compare_exchange(TaskState::Running, TaskState::RunningNotified)
{
self.forward();
return;
}
}
TaskState::Runnable | TaskState::RunningNotified => {
self.forward();
return;
}
TaskState::Done => return,
}
}
}
}