use std::{
panic::Location,
task::{Wake, Waker},
};
use super::{
FLASH, credit,
state::{AtomicTaskState, ParkOutcome, TaskDiag, TaskState, WakeOutcome},
};
use crate::{sync::Arc, system::lock::Mutex};
#[derive(fieldwork::Fieldwork)]
#[fieldwork(opt_in, get)]
pub(in crate::flash) struct TaskGate {
#[field(get(vis = "pub(in crate::flash)", doc = "Returns this task's spawn site."))]
loc: &'static Location<'static>,
diag: Arc<TaskDiag>,
runtime_waker: Mutex<Option<Waker>>,
#[field(get(
vis = "pub(in crate::flash)",
doc = "Returns this task's active_async slot id."
))]
id: u64,
}
impl TaskGate {
pub(super) fn new(id: u64, loc: &'static Location<'static>, diag: Arc<TaskDiag>) -> Arc<Self> {
Arc::new(Self {
id,
loc,
diag,
runtime_waker: Mutex::default(),
})
}
pub(in crate::flash) fn complete(&self) {
FLASH.gate_complete(self.state(), self.id);
}
pub(super) fn diag(&self) -> Arc<TaskDiag> {
Arc::clone(&self.diag)
}
fn forward(&self) {
let w = self.runtime_waker.lock().clone();
if let Some(w) = w {
w.wake();
}
}
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);
}
fn state(&self) -> &AtomicTaskState {
&self.diag.state
}
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 {
let entered = self
.state()
.compare_exchange(TaskState::Runnable, TaskState::Running);
if entered {
self.diag.enter_poll(credit::current_thread_key());
}
entered
}
}
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,
}
}
}
}