use std::sync::atomic::{AtomicBool, Ordering};
use crate::identity::TimelineId;
use crate::bus::error::{BusError, Result};
use crate::bus::time::RobotInstant;
mod sealed {
pub trait Sealed {}
}
pub trait StepStamp: sealed::Sealed {
fn instant(&self) -> RobotInstant;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StepToken {
at: RobotInstant,
}
impl StepToken {
pub(crate) const fn mint(at: RobotInstant) -> Self {
StepToken { at }
}
}
impl sealed::Sealed for StepToken {}
impl StepStamp for StepToken {
fn instant(&self) -> RobotInstant {
self.at
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct WorldStepToken {
at: RobotInstant,
}
impl sealed::Sealed for WorldStepToken {}
impl StepStamp for WorldStepToken {
fn instant(&self) -> RobotInstant {
self.at
}
}
#[allow(
dead_code,
reason = "compiled in every profile because a domain module never asks which profile it is in; its only consumer is a module one profile declares"
)]
pub(crate) struct TimelineAuthority {
timeline: TimelineId,
}
#[allow(
dead_code,
reason = "compiled in every profile because a domain module never asks which profile it is in; its only consumer is a module one profile declares"
)]
static TIMELINE_AUTHORITY_HELD: AtomicBool = AtomicBool::new(false);
#[allow(
dead_code,
reason = "compiled in every profile because a domain module never asks which profile it is in; its only consumer is a module one profile declares"
)]
impl TimelineAuthority {
pub(crate) fn mint(timeline: TimelineId) -> Result<Self> {
if TIMELINE_AUTHORITY_HELD.swap(true, Ordering::AcqRel) {
return Err(BusError::DuplicateTimelineAuthority);
}
Ok(TimelineAuthority { timeline })
}
pub const fn timeline(&self) -> TimelineId {
self.timeline
}
pub fn replace_timeline(&mut self, timeline: TimelineId) {
self.timeline = timeline;
}
pub const fn completed_step(&self, ticks: u64) -> WorldStepToken {
WorldStepToken {
at: RobotInstant::new(self.timeline, ticks),
}
}
}
impl Drop for TimelineAuthority {
fn drop(&mut self) {
TIMELINE_AUTHORITY_HELD.store(false, Ordering::Release);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bus::test_support::timeline;
#[test]
fn only_one_timeline_authority_exists_at_a_time() {
let first = TimelineAuthority::mint(timeline(1)).expect("first authority should mint");
assert!(
TimelineAuthority::mint(timeline(2)).is_err(),
"a second authority must be rejected at startup"
);
assert_eq!(first.completed_step(50).instant().ticks(), 50);
drop(first);
TimelineAuthority::mint(timeline(3)).expect("the slot is released on drop");
}
}