use core::sync::atomic::{AtomicU64, Ordering};
use super::TaskDeadlineError;
use crate::{thread::ThreadId, time::MonotonicDeadline};
pub(super) const TASK_DEADLINE_CLASS_COUNT: usize = 4;
static NEXT_TASK_DEADLINE_NODE_ID: AtomicU64 = AtomicU64::new(1);
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[repr(transparent)]
pub(super) struct TaskDeadlineNodeId(u64);
impl TaskDeadlineNodeId {
const fn from_raw(raw: u64) -> Self {
Self(raw)
}
pub(super) const fn as_u64(self) -> u64 {
self.0
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u8)]
pub(super) enum TaskDeadlineClass {
ParkSoft = 0,
ParkHard = 1,
DeadlineCbs = 2,
DeadlineZeroLag = 3,
}
impl TaskDeadlineClass {
pub(super) const ALL: [Self; TASK_DEADLINE_CLASS_COUNT] = [
Self::ParkSoft,
Self::ParkHard,
Self::DeadlineCbs,
Self::DeadlineZeroLag,
];
pub(super) const fn index(self) -> usize {
self as usize
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum TaskDeadlineNodeKind {
Park,
DeadlineCbs,
DeadlineZeroLag,
}
impl TaskDeadlineNodeKind {
const fn supports(self, class: TaskDeadlineClass) -> bool {
matches!(
(self, class),
(
Self::Park,
TaskDeadlineClass::ParkSoft | TaskDeadlineClass::ParkHard
) | (Self::DeadlineCbs, TaskDeadlineClass::DeadlineCbs)
| (Self::DeadlineZeroLag, TaskDeadlineClass::DeadlineZeroLag)
)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TaskDeadlineToken {
node: TaskDeadlineNodeId,
generation: u64,
}
impl TaskDeadlineToken {
pub const NONE: Self = Self {
node: TaskDeadlineNodeId::from_raw(0),
generation: 0,
};
pub const fn generation(self) -> u64 {
self.generation
}
const fn new(node: TaskDeadlineNodeId, generation: u64) -> Self {
Self { node, generation }
}
pub(super) const fn node(self) -> TaskDeadlineNodeId {
self.node
}
}
#[derive(Debug)]
pub struct TaskDeadlineNode {
thread: ThreadId,
kind: TaskDeadlineNodeKind,
identity: AtomicU64,
sequence: AtomicU64,
}
impl TaskDeadlineNode {
pub const fn for_thread(thread: ThreadId) -> Self {
Self::new(thread, TaskDeadlineNodeKind::Park)
}
pub(crate) const fn deadline_cbs_for_thread(thread: ThreadId) -> Self {
Self::new(thread, TaskDeadlineNodeKind::DeadlineCbs)
}
pub(crate) const fn deadline_zero_lag_for_thread(thread: ThreadId) -> Self {
Self::new(thread, TaskDeadlineNodeKind::DeadlineZeroLag)
}
const fn new(thread: ThreadId, kind: TaskDeadlineNodeKind) -> Self {
Self {
thread,
kind,
identity: AtomicU64::new(0),
sequence: AtomicU64::new(0),
}
}
pub(super) const fn thread(&self) -> ThreadId {
self.thread
}
pub(super) const fn supports(&self, class: TaskDeadlineClass) -> bool {
self.kind.supports(class)
}
pub(super) fn identity(&self) -> Result<TaskDeadlineNodeId, TaskDeadlineError> {
let identity = self.identity.load(Ordering::Acquire);
if identity != 0 {
return Ok(TaskDeadlineNodeId::from_raw(identity));
}
let mut candidate = NEXT_TASK_DEADLINE_NODE_ID.load(Ordering::Relaxed);
loop {
if candidate == u64::MAX {
return Err(TaskDeadlineError::GenerationExhausted);
}
match NEXT_TASK_DEADLINE_NODE_ID.compare_exchange_weak(
candidate,
candidate + 1,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => {
return match self.identity.compare_exchange(
0,
candidate,
Ordering::Release,
Ordering::Acquire,
) {
Ok(_) => Ok(TaskDeadlineNodeId::from_raw(candidate)),
Err(published) => Ok(TaskDeadlineNodeId::from_raw(published)),
};
}
Err(updated) => candidate = updated,
}
}
}
pub(super) fn next_token(
&self,
identity: TaskDeadlineNodeId,
) -> Result<TaskDeadlineToken, TaskDeadlineError> {
debug_assert_eq!(self.identity.load(Ordering::Relaxed), identity.as_u64());
let mut sequence = self.sequence.load(Ordering::Relaxed);
loop {
if sequence == u64::MAX {
return Err(TaskDeadlineError::GenerationExhausted);
}
match self.sequence.compare_exchange_weak(
sequence,
sequence + 1,
Ordering::Relaxed,
Ordering::Relaxed,
) {
Ok(_) => return Ok(TaskDeadlineToken::new(identity, sequence + 1)),
Err(updated) => sequence = updated,
}
}
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TaskDeadlineKind {
ParkTimeout { park_generation: u64 },
DeadlineCbs,
DeadlineZeroLag,
}
impl TaskDeadlineKind {
pub const fn park_timeout(park_generation: u64) -> Self {
Self::ParkTimeout { park_generation }
}
pub const fn park_generation(self) -> Option<u64> {
match self {
Self::ParkTimeout { park_generation } => Some(park_generation),
Self::DeadlineCbs | Self::DeadlineZeroLag => None,
}
}
pub(super) const fn default_class(self) -> TaskDeadlineClass {
match self {
Self::ParkTimeout { .. } => TaskDeadlineClass::ParkSoft,
Self::DeadlineCbs => TaskDeadlineClass::DeadlineCbs,
Self::DeadlineZeroLag => TaskDeadlineClass::DeadlineZeroLag,
}
}
}
#[must_use = "a task-deadline registration must remain owned until cancellation or expiration"]
#[derive(Debug, Eq, PartialEq)]
pub struct TaskDeadlineRegistration {
thread: ThreadId,
token: TaskDeadlineToken,
deadline: MonotonicDeadline,
kind: TaskDeadlineKind,
class: TaskDeadlineClass,
}
impl TaskDeadlineRegistration {
pub(super) const fn new(
thread: ThreadId,
token: TaskDeadlineToken,
deadline: MonotonicDeadline,
kind: TaskDeadlineKind,
class: TaskDeadlineClass,
) -> Self {
Self {
thread,
token,
deadline,
kind,
class,
}
}
pub const fn thread(&self) -> ThreadId {
self.thread
}
pub const fn token(&self) -> TaskDeadlineToken {
self.token
}
pub const fn deadline(&self) -> MonotonicDeadline {
self.deadline
}
pub const fn kind(&self) -> TaskDeadlineKind {
self.kind
}
pub(crate) const fn may_enter_soft_expiry_buffer(&self) -> bool {
matches!(self.class, TaskDeadlineClass::ParkSoft)
}
pub(super) const fn class(&self) -> TaskDeadlineClass {
self.class
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ExpiredTaskDeadline {
thread: ThreadId,
token: TaskDeadlineToken,
deadline: MonotonicDeadline,
valid: bool,
kind: TaskDeadlineKind,
}
impl ExpiredTaskDeadline {
pub const EMPTY: Self = Self {
thread: ThreadId::from_parts(0, 0),
token: TaskDeadlineToken::NONE,
deadline: MonotonicDeadline::ORIGIN,
valid: false,
kind: TaskDeadlineKind::ParkTimeout { park_generation: 0 },
};
pub(super) const fn new(
thread: ThreadId,
token: TaskDeadlineToken,
deadline: MonotonicDeadline,
kind: TaskDeadlineKind,
) -> Self {
Self {
thread,
token,
deadline,
valid: true,
kind,
}
}
pub const fn thread(self) -> Option<ThreadId> {
if self.valid { Some(self.thread) } else { None }
}
pub const fn token(self) -> TaskDeadlineToken {
self.token
}
pub const fn deadline(self) -> Option<MonotonicDeadline> {
if self.valid {
Some(self.deadline)
} else {
None
}
}
pub const fn kind(self) -> Option<TaskDeadlineKind> {
if self.valid { Some(self.kind) } else { None }
}
pub const fn is_valid(self) -> bool {
self.valid
}
}