use std::{fmt::Debug, ptr::NonNull, sync::Arc, time::Duration};
use moirai_core::Priority;
use super::state::{TaskState, TaskStateBlock};
pub(crate) unsafe trait StateLease: Debug + Send + 'static {
fn state(&self) -> &TaskState;
fn retire(self);
}
#[derive(Debug)]
pub(crate) struct OwnedStateLease {
block: Arc<TaskStateBlock>,
state: NonNull<TaskState>,
}
unsafe impl Send for OwnedStateLease {}
unsafe impl StateLease for OwnedStateLease {
fn state(&self) -> &TaskState {
let _ = &self.block;
unsafe { self.state.as_ref() }
}
fn retire(self) {
unsafe { TaskState::retire_token(self.state) };
}
}
#[derive(Debug)]
pub(crate) struct SchedulerStateLease {
state: NonNull<TaskState>,
}
unsafe impl Send for SchedulerStateLease {}
unsafe impl StateLease for SchedulerStateLease {
fn state(&self) -> &TaskState {
unsafe { self.state.as_ref() }
}
fn retire(self) {
unsafe { TaskState::retire_token(self.state) };
}
}
impl OwnedStateLease {
fn new(block: Arc<TaskStateBlock>, state: NonNull<TaskState>) -> Self {
Self { block, state }
}
}
impl SchedulerStateLease {
unsafe fn new(state: NonNull<TaskState>) -> Self {
Self { state }
}
}
#[derive(Debug)]
pub(crate) struct TaskLifecycleToken<L: StateLease = OwnedStateLease> {
lease: Option<L>,
}
#[derive(Debug)]
pub(crate) struct RunningTaskToken<L: StateLease = OwnedStateLease> {
lease: Option<L>,
pub(super) started_after_ns: u64,
pub(super) completed: bool,
}
const _: [fn(); 2] = {
const fn assert_send<T: Send>() {}
fn tokens_are_send<L: StateLease>() {
assert_send::<TaskLifecycleToken<L>>();
assert_send::<RunningTaskToken<L>>();
}
[
tokens_are_send::<OwnedStateLease>,
tokens_are_send::<SchedulerStateLease>,
]
};
impl TaskLifecycleToken<OwnedStateLease> {
pub(super) fn new_owned(block: Arc<TaskStateBlock>, state: NonNull<TaskState>) -> Self {
Self {
lease: Some(OwnedStateLease::new(block, state)),
}
}
}
impl TaskLifecycleToken<SchedulerStateLease> {
pub(super) unsafe fn new_scheduled(state: NonNull<TaskState>) -> Self {
Self {
lease: Some(unsafe { SchedulerStateLease::new(state) }),
}
}
}
impl<L: StateLease> TaskLifecycleToken<L> {
fn state(&self) -> &TaskState {
self.lease
.as_ref()
.expect("invariant: lifecycle token retains its state lease")
.state()
}
pub(super) fn release(mut self) {
if let Some(lease) = self.lease.take() {
lease.retire();
}
}
#[inline]
pub(crate) fn set_priority(&self, priority: Priority) {
self.state().set_priority(priority);
}
#[inline]
pub(crate) fn cancel_requested(&self) -> bool {
self.state().cancel_requested()
}
#[inline]
pub(crate) fn cancel(mut self) {
let lease = self
.lease
.take()
.expect("invariant: lifecycle token retains its state lease");
lease.state().mark_cancelled();
lease.retire();
}
#[inline]
pub(crate) fn start_unless_cancelled(self, worker_id: usize) -> Option<RunningTaskToken<L>> {
if self.cancel_requested() {
self.cancel();
None
} else {
Some(self.start(worker_id))
}
}
#[inline]
pub(crate) fn start(mut self, worker_id: usize) -> RunningTaskToken<L> {
let started_after_ns = self.state().mark_started(worker_id);
RunningTaskToken {
lease: self.lease.take(),
started_after_ns,
completed: false,
}
}
}
impl<L: StateLease> Drop for TaskLifecycleToken<L> {
fn drop(&mut self) {
if let Some(lease) = self.lease.take() {
lease.state().mark_cancelled();
lease.retire();
}
}
}
impl<L: StateLease> RunningTaskToken<L> {
#[inline]
pub(crate) fn cancel(mut self) {
self.abandon();
}
#[inline]
pub(crate) fn complete(mut self) -> Duration {
let execution_time = self
.lease
.as_ref()
.expect("invariant: running token retains its state lease")
.state()
.mark_completed_since(self.started_after_ns);
self.completed = true;
execution_time
}
fn abandon(&mut self) {
if !self.completed {
self.lease
.as_ref()
.expect("invariant: running token retains its state lease")
.state()
.mark_cancelled();
self.completed = true;
}
}
}
impl<L: StateLease> Drop for RunningTaskToken<L> {
fn drop(&mut self) {
self.abandon();
if let Some(lease) = self.lease.take() {
lease.retire();
}
}
}