mod clock_speed;
mod handle;
mod time;
#[cfg(test)]
mod test;
use atomic_arena::Key;
pub use clock_speed::*;
pub use handle::*;
pub use time::*;
use std::sync::{
Arc,
atomic::{AtomicBool, AtomicU64, Ordering},
};
use crate::{
Parameter, Value,
command::{ValueChangeCommand, read_commands_into_parameters},
command_writers_and_readers,
info::Info,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ClockId(pub(crate) Key);
#[derive(Debug)]
pub(crate) struct ClockShared {
ticking: AtomicBool,
ticks: AtomicU64,
fractional_position: AtomicU64,
removed: AtomicBool,
}
impl ClockShared {
#[must_use]
pub fn new() -> Self {
Self {
ticking: AtomicBool::new(false),
ticks: AtomicU64::new(0),
fractional_position: AtomicU64::new(0.0f64.to_bits()),
removed: AtomicBool::new(false),
}
}
#[must_use]
pub fn ticking(&self) -> bool {
self.ticking.load(Ordering::SeqCst)
}
#[must_use]
pub fn ticks(&self) -> u64 {
self.ticks.load(Ordering::SeqCst)
}
#[must_use]
pub fn fractional_position(&self) -> f64 {
f64::from_bits(self.fractional_position.load(Ordering::SeqCst))
}
#[must_use]
pub fn is_marked_for_removal(&self) -> bool {
self.removed.load(Ordering::SeqCst)
}
pub fn mark_for_removal(&self) {
self.removed.store(true, Ordering::SeqCst);
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum State {
NotStarted,
Started {
ticks: u64,
fractional_position: f64,
},
}
pub(crate) struct Clock {
command_readers: CommandReaders,
shared: Arc<ClockShared>,
ticking: bool,
speed: Parameter<ClockSpeed>,
state: State,
}
impl Clock {
#[must_use]
pub(crate) fn new(speed: Value<ClockSpeed>, id: ClockId) -> (Self, ClockHandle) {
let (command_writers, command_readers) = command_writers_and_readers();
let shared = Arc::new(ClockShared::new());
(
Self {
command_readers,
shared: shared.clone(),
ticking: false,
speed: Parameter::new(speed, ClockSpeed::TicksPerMinute(120.0)),
state: State::NotStarted,
},
ClockHandle {
id,
shared,
command_writers,
},
)
}
#[must_use]
pub(crate) fn without_handle(speed: Value<ClockSpeed>) -> Self {
let (_, command_readers) = command_writers_and_readers();
Self {
command_readers,
shared: Arc::new(ClockShared::new()),
ticking: false,
speed: Parameter::new(speed, ClockSpeed::TicksPerMinute(120.0)),
state: State::NotStarted,
}
}
#[must_use]
pub(crate) fn shared(&self) -> Arc<ClockShared> {
self.shared.clone()
}
#[must_use]
pub(crate) fn state(&self) -> State {
self.state
}
#[must_use]
pub(crate) fn ticking(&self) -> bool {
self.ticking
}
pub(crate) fn on_start_processing(&mut self) {
read_commands_into_parameters!(self, speed);
if let Some(ticking) = self.command_readers.set_ticking.read() {
self.set_ticking(ticking);
}
if self.command_readers.reset.read().is_some() {
self.reset();
}
self.update_shared();
}
fn set_ticking(&mut self, ticking: bool) {
self.ticking = ticking;
self.shared.ticking.store(ticking, Ordering::SeqCst);
}
fn reset(&mut self) {
self.state = State::NotStarted;
self.shared.ticks.store(0, Ordering::SeqCst);
}
fn update_shared(&mut self) {
let (ticks, fractional_position) = match &self.state {
State::NotStarted => (0, 0.0),
State::Started {
ticks,
fractional_position,
} => (*ticks, *fractional_position),
};
self.shared.ticks.store(ticks, Ordering::SeqCst);
self.shared
.fractional_position
.store(fractional_position.to_bits(), Ordering::SeqCst);
}
pub(crate) fn update(&mut self, dt: f64, info: &Info) -> Option<u64> {
self.speed.update(dt, info);
if !self.ticking {
return None;
}
let mut new_tick_count = None;
if self.state == State::NotStarted {
self.state = State::Started {
ticks: 0,
fractional_position: 0.0,
};
new_tick_count = Some(0);
}
if let State::Started {
ticks,
fractional_position: tick_timer,
} = &mut self.state
{
*tick_timer += self.speed.value().as_ticks_per_second() * dt;
while *tick_timer >= 1.0 {
*tick_timer -= 1.0;
*ticks += 1;
new_tick_count = Some(*ticks);
}
} else {
panic!("clock state should be Started by now");
}
new_tick_count
}
}
impl Default for Clock {
fn default() -> Self {
Self::without_handle(Value::Fixed(ClockSpeed::TicksPerSecond(0.0)))
}
}
command_writers_and_readers! {
set_speed: ValueChangeCommand<ClockSpeed>,
set_ticking: bool,
reset: (),
}