Skip to main content

gwr_engine/time/
clock.rs

1// Copyright (c) 2023 Graphcore Ltd. All rights reserved.
2
3//! This module represents the time during a simulation.
4//!
5//! Time is made up of a cycle count and a phase.
6
7use core::cmp::Ordering;
8use std::cell::{Cell, RefCell};
9use std::future::Future;
10use std::pin::Pin;
11use std::rc::Rc;
12use std::task::{Context, Poll, Waker};
13
14use futures::future::FusedFuture;
15
16use crate::traits::{Resolve, Resolver};
17
18pub mod phase {
19    pub const BEGIN: u32 = 0;
20    pub const END: u32 = u32::MAX;
21}
22
23/// ClockTick structure for representing a number of Clock ticks and a phase.
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25pub struct ClockTick {
26    /// Clock ticks.
27    tick: u64,
28
29    /// Clock phase.
30    phase: u32,
31}
32
33impl ClockTick {
34    #[must_use]
35    pub fn new() -> Self {
36        Self {
37            tick: 0,
38            phase: phase::BEGIN,
39        }
40    }
41
42    /// Get the current clock tick.
43    #[must_use]
44    pub fn tick(&self) -> u64 {
45        self.tick
46    }
47
48    /// Get the current clock phase.
49    #[must_use]
50    pub fn phase(&self) -> u32 {
51        self.phase
52    }
53
54    /// Change the default constructor value of `tick`.
55    pub fn set_tick(&mut self, tick: u64) -> ClockTick {
56        self.tick = tick;
57        *self
58    }
59
60    /// Change the default constructor value of `phase`.
61    pub fn set_phase(&mut self, phase: u32) -> ClockTick {
62        self.phase = phase;
63        *self
64    }
65}
66
67impl Default for ClockTick {
68    fn default() -> Self {
69        Self::new()
70    }
71}
72
73/// Define the comparison operation for SimTime.
74impl Ord for ClockTick {
75    fn cmp(&self, other: &Self) -> Ordering {
76        match self.tick.cmp(&other.tick) {
77            Ordering::Greater => Ordering::Greater,
78            Ordering::Less => Ordering::Less,
79            Ordering::Equal => self.phase.cmp(&other.phase),
80        }
81    }
82}
83
84impl PartialOrd for ClockTick {
85    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
86        Some(self.cmp(other))
87    }
88}
89
90impl std::fmt::Display for ClockTick {
91    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
92        write!(f, "{}.{:?}", self.tick, self.phase)
93    }
94}
95
96/// State representing a clock.
97#[derive(Clone)]
98pub struct Clock {
99    /// Frequency of the clock in MHz.
100    /// *Note*: Should never be changed as it is registered at this frequency.
101    freq_mhz: f64,
102
103    pub shared_state: Rc<ClockState>,
104}
105
106pub struct TaskWaker {
107    /// Internal identifier for a scheduled clock wait.
108    pub id: u64,
109
110    /// The Waker to use to make a task active again.
111    pub waker: Waker,
112
113    /// When a task is scheduled in the future it may be a background task
114    /// that will simply run forever in which case it will set `can_exit` to
115    /// true.
116    pub can_exit: bool,
117}
118
119/// Shared state between futures using a Clock and the Clock itself.
120pub struct ClockState {
121    now: RefCell<ClockTick>,
122
123    next_waiter_id: Cell<u64>,
124
125    /// Queue of futures waiting for the right time.
126    pub waiting: RefCell<Vec<Vec<TaskWaker>>>,
127
128    /// Queue of times at which those futures are to be woken. This is kept
129    /// sorted by time so that the first entry is the next to be woken.
130    pub waiting_times: RefCell<Vec<ClockTick>>,
131
132    /// Registered [`Resolve`] functions.
133    pub to_resolve: RefCell<Vec<Rc<dyn Resolve + 'static>>>,
134}
135
136impl ClockState {
137    fn schedule(&self, schedule_time: ClockTick, cx: &mut Context<'_>, can_exit: bool) -> u64 {
138        let waiter_id = self.next_waiter_id.get();
139        self.next_waiter_id.set(waiter_id + 1);
140
141        let mut waiting_times = self.waiting_times.borrow_mut();
142        let mut waiting = self.waiting.borrow_mut();
143        if let Some(index) = waiting_times.iter().position(|&x| x == schedule_time) {
144            // Time already exists, add this task
145            waiting[index].push(TaskWaker {
146                id: waiter_id,
147                waker: cx.waker().clone(),
148                can_exit,
149            });
150        } else {
151            // Time not found, insert at the correct location
152            match waiting_times.iter().position(|x| *x < schedule_time) {
153                Some(index) => {
154                    // Insert at an arbitrary index
155                    waiting_times.insert(index, schedule_time);
156                    waiting.insert(
157                        index,
158                        vec![TaskWaker {
159                            id: waiter_id,
160                            waker: cx.waker().clone(),
161                            can_exit,
162                        }],
163                    );
164                }
165                None => {
166                    // Insert at the head
167                    waiting_times.push(schedule_time);
168                    waiting.push(vec![TaskWaker {
169                        id: waiter_id,
170                        waker: cx.waker().clone(),
171                        can_exit,
172                    }]);
173                }
174            }
175        }
176
177        waiter_id
178    }
179
180    fn unschedule(&self, schedule_time: ClockTick, waiter_id: u64) {
181        let mut waiting_times = self.waiting_times.borrow_mut();
182        let mut waiting = self.waiting.borrow_mut();
183
184        if let Some(time_index) = waiting_times.iter().position(|&x| x == schedule_time)
185            && let Some(waiter_index) = waiting[time_index].iter().position(|w| w.id == waiter_id)
186        {
187            waiting[time_index].remove(waiter_index);
188            if waiting[time_index].is_empty() {
189                waiting.remove(time_index);
190                waiting_times.remove(time_index);
191            }
192        }
193    }
194
195    fn advance_time(&self, to_time: ClockTick) {
196        self.resolve();
197
198        assert!(to_time >= *self.now.borrow(), "Time moving backwards");
199        *self.now.borrow_mut() = to_time;
200    }
201
202    fn resolve(&self) {
203        for r in self.to_resolve.borrow_mut().drain(..) {
204            r.resolve();
205        }
206    }
207}
208
209impl Clock {
210    /// Create a new [Clock] at the specified frequency.
211    #[must_use]
212    pub fn new(freq_mhz: f64) -> Self {
213        let shared_state = Rc::new(ClockState {
214            now: RefCell::new(ClockTick {
215                tick: 0,
216                phase: phase::BEGIN,
217            }),
218            next_waiter_id: Cell::new(0),
219            waiting: RefCell::new(Vec::new()),
220            waiting_times: RefCell::new(Vec::new()),
221            to_resolve: RefCell::new(Vec::new()),
222        });
223
224        Self {
225            freq_mhz,
226            shared_state,
227        }
228    }
229
230    /// Advance the time on this clock
231    pub fn advance_time(&self, to_time: ClockTick) {
232        self.shared_state.advance_time(to_time);
233    }
234
235    /// Returns the clocks frequency in MHz.
236    #[must_use]
237    pub fn freq_mhz(&self) -> f64 {
238        self.freq_mhz
239    }
240
241    /// Returns the current [ClockTick].
242    #[must_use]
243    pub fn tick_now(&self) -> ClockTick {
244        *self.shared_state.now.borrow()
245    }
246
247    /// Returns the current time in `ns`.
248    #[must_use]
249    pub fn time_now_ns(&self) -> f64 {
250        let now = *self.shared_state.now.borrow();
251        self.to_ns(&now)
252    }
253
254    /// Returns the time in `ns` of the next event registered with this clock.
255    #[must_use]
256    pub fn time_of_next(&self) -> f64 {
257        match self.shared_state.waiting_times.borrow().last() {
258            Some(clock_time) => self.to_ns(clock_time),
259            None => f64::MAX,
260        }
261    }
262
263    /// Returns the phase of the next event registered with this clock.
264    #[must_use]
265    pub fn phase_of_next(&self) -> u32 {
266        match self.shared_state.waiting_times.borrow().last() {
267            Some(clock_time) => clock_time.phase(),
268            None => u32::MAX,
269        }
270    }
271
272    /// Convert the given [ClockTick] to a time in `ns` for this clock.
273    #[must_use]
274    pub fn to_ns(&self, clock_time: &ClockTick) -> f64 {
275        clock_time.tick as f64 / self.freq_mhz * 1000.0
276    }
277
278    /// Returns a [ClockDelay] future which must be `await`ed to delay the
279    /// specified number of ticks.
280    #[must_use = "Futures do nothing unless you `.await` or otherwise use them"]
281    pub fn wait_ticks(&self, ticks: u64) -> ClockDelay {
282        let mut until = self.tick_now();
283        until.tick += ticks;
284        until.phase = phase::BEGIN;
285        ClockDelay {
286            shared_state: self.shared_state.clone(),
287            until,
288            can_exit: false,
289            waiter_id: None,
290            done: false,
291        }
292    }
293
294    /// Returns a [ClockDelay] future which must be `await`ed to delay the
295    /// specified number of ticks. However, if the remainder of the simulation
296    /// completes then this future is allowed to not complete. This allows the
297    /// user to create tasks that can run continuously as long as the rest of
298    /// the simulation continues to run.
299    #[must_use = "Futures do nothing unless you `.await` or otherwise use them"]
300    pub fn wait_ticks_or_exit(&self, ticks: u64) -> ClockDelay {
301        let mut until = self.tick_now();
302        until.tick += ticks;
303        until.phase = phase::BEGIN;
304        ClockDelay {
305            shared_state: self.shared_state.clone(),
306            until,
307            can_exit: true,
308            waiter_id: None,
309            done: false,
310        }
311    }
312
313    #[must_use = "Futures do nothing unless you `.await` or otherwise use them"]
314    pub fn next_tick_and_phase(&self, phase: u32) -> ClockDelay {
315        let mut until = self.tick_now();
316        until.tick += 1;
317        until.phase = phase;
318        ClockDelay {
319            shared_state: self.shared_state.clone(),
320            until,
321            can_exit: false,
322            waiter_id: None,
323            done: false,
324        }
325    }
326
327    #[must_use = "Futures do nothing unless you `.await` or otherwise use them"]
328    pub fn wait_phase(&self, phase: u32) -> ClockDelay {
329        let mut until = self.tick_now();
330        assert!(phase >= until.phase, "Time going backwards");
331        until.phase = phase;
332        ClockDelay {
333            shared_state: self.shared_state.clone(),
334            until,
335            can_exit: false,
336            waiter_id: None,
337            done: false,
338        }
339    }
340
341    /// Advance to the next tick after the specified time.
342    pub fn advance_to(&self, time_ns: f64) {
343        let now_ns = self.time_now_ns();
344        assert!(now_ns < time_ns);
345        let diff_ns = time_ns - now_ns;
346        let ticks = (diff_ns * (self.freq_mhz / 1000.0)).ceil();
347
348        let mut until = self.tick_now();
349        until.tick += ticks as u64;
350        until.phase = phase::BEGIN;
351
352        self.shared_state.advance_time(until);
353    }
354}
355
356/// The default clocks is simply to use a 1GHz clock so ticks are 1ns.
357impl Default for Clock {
358    fn default() -> Self {
359        Self::new(1000.0)
360    }
361}
362
363/// The comparison operators for Clocks - use the next pending Waker time.
364impl PartialEq for Clock {
365    fn eq(&self, other: &Self) -> bool {
366        self.time_of_next() == other.time_of_next() && self.phase_of_next() == other.phase_of_next()
367    }
368}
369impl Eq for Clock {}
370
371impl Ord for Clock {
372    fn cmp(&self, other: &Self) -> Ordering {
373        self.time_of_next()
374            .total_cmp(&other.time_of_next())
375            .then_with(|| self.phase_of_next().cmp(&other.phase_of_next()))
376    }
377}
378
379impl PartialOrd for Clock {
380    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
381        Some(self.cmp(other))
382    }
383}
384
385impl Resolver for Clock {
386    fn add_resolve(&self, resolve: Rc<dyn Resolve + 'static>) {
387        self.shared_state.to_resolve.borrow_mut().push(resolve);
388    }
389}
390
391/// Future returned by the clock to manage advancing time using async functions.
392pub struct ClockDelay {
393    shared_state: Rc<ClockState>,
394    until: ClockTick,
395    can_exit: bool,
396    waiter_id: Option<u64>,
397    done: bool,
398}
399
400impl Future for ClockDelay {
401    type Output = ();
402    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
403        if self.done {
404            return Poll::Ready(());
405        }
406
407        if self.until > *self.shared_state.now.borrow() {
408            if let Some(waiter_id) = self.waiter_id {
409                self.shared_state.unschedule(self.until, waiter_id);
410            }
411            let waiter_id = self.shared_state.schedule(self.until, cx, self.can_exit);
412            self.waiter_id = Some(waiter_id);
413            Poll::Pending
414        } else {
415            self.waiter_id = None;
416            self.done = true;
417            Poll::Ready(())
418        }
419    }
420}
421
422impl FusedFuture for ClockDelay {
423    fn is_terminated(&self) -> bool {
424        self.done
425    }
426}
427
428impl Drop for ClockDelay {
429    fn drop(&mut self) {
430        if let Some(waiter_id) = self.waiter_id.take() {
431            self.shared_state.unschedule(self.until, waiter_id);
432        }
433    }
434}
435
436#[cfg(test)]
437mod tests {
438    use std::mem::drop;
439    use std::pin::Pin;
440    use std::task::Context;
441
442    use futures::future::FusedFuture;
443    use futures::task::noop_waker;
444
445    use super::*;
446
447    #[test]
448    fn convert_to_ns() {
449        let clk_ghz = Clock::new(1000.0);
450        assert_eq!(1.0, clk_ghz.to_ns(&ClockTick::new().set_tick(1)));
451
452        let slow_clk = Clock::new(0.5);
453        assert_eq!(2000.0, slow_clk.to_ns(&ClockTick::new().set_tick(1)));
454    }
455
456    #[test]
457    fn clock_tick_accessors_default_display_and_ordering() {
458        let default_tick = ClockTick::default();
459        assert_eq!(default_tick.tick(), 0);
460        assert_eq!(default_tick.phase(), phase::BEGIN);
461        assert_eq!(default_tick.to_string(), "0.0");
462
463        let earlier = ClockTick::new().set_tick(1);
464        let later = ClockTick::new().set_tick(2);
465        assert!(earlier < later);
466        assert_eq!(earlier.partial_cmp(&later), Some(Ordering::Less));
467    }
468
469    #[test]
470    fn clock_tick_phase_accessors_display_and_ordering() {
471        let tick = ClockTick::new().set_tick(1).set_phase(2);
472        assert_eq!(tick.phase(), 2);
473        assert_eq!(tick.to_string(), "1.2");
474
475        let earlier_phase = ClockTick::new().set_tick(1).set_phase(1);
476        assert!(earlier_phase < tick);
477
478        let later_tick = ClockTick::new().set_tick(2).set_phase(0);
479        assert!(later_tick > tick);
480    }
481
482    #[test]
483    fn clock_default_advance_and_ordering() {
484        let clock = Clock::default();
485        assert_eq!(clock.freq_mhz(), 1000.0);
486
487        clock.advance_to(2.1);
488        assert_eq!(clock.tick_now().tick(), 3);
489        assert_eq!(clock.tick_now().phase(), phase::BEGIN);
490        assert_eq!(clock.time_now_ns(), 3.0);
491
492        let earlier = Clock::new(1000.0);
493        let later = Clock::new(1000.0);
494        drop(earlier.wait_ticks(1));
495        drop(later.wait_ticks(2));
496
497        assert!(earlier == later);
498        assert_eq!(earlier.partial_cmp(&later), Some(Ordering::Equal));
499    }
500
501    #[test]
502    fn unschedule_unknown_waiter_is_a_noop() {
503        let clock = Clock::new(1000.0);
504        let scheduled_time = ClockTick::new().set_tick(1);
505
506        clock
507            .shared_state
508            .waiting_times
509            .borrow_mut()
510            .push(scheduled_time);
511        clock.shared_state.waiting.borrow_mut().push(Vec::new());
512
513        clock.shared_state.unschedule(scheduled_time, 7);
514
515        assert_eq!(clock.shared_state.waiting_times.borrow().len(), 1);
516        assert_eq!(clock.shared_state.waiting.borrow().len(), 1);
517    }
518
519    #[test]
520    fn unschedule_waiter_keeps_time_when_other_waiters_remain() {
521        let clock = Clock::new(1000.0);
522        let scheduled_time = ClockTick::new().set_tick(1);
523        let waker = noop_waker();
524        let mut cx = Context::from_waker(&waker);
525
526        let first_waiter_id = clock.shared_state.schedule(scheduled_time, &mut cx, false);
527        let second_waiter_id = clock.shared_state.schedule(scheduled_time, &mut cx, false);
528
529        clock
530            .shared_state
531            .unschedule(scheduled_time, first_waiter_id);
532
533        let waiting_times = clock.shared_state.waiting_times.borrow();
534        let waiting = clock.shared_state.waiting.borrow();
535
536        assert_eq!(waiting_times.as_slice(), &[scheduled_time]);
537        assert_eq!(waiting.len(), 1);
538        assert_eq!(waiting[0].len(), 1);
539        assert_eq!(waiting[0][0].id, second_waiter_id);
540    }
541
542    #[test]
543    fn clock_delay_is_fused_after_completion() {
544        let clock = Clock::new(1000.0);
545        let waker = noop_waker();
546        let mut cx = Context::from_waker(&waker);
547        let mut delay = clock.wait_ticks(1);
548
549        assert!(!delay.is_terminated());
550        assert_eq!(Pin::new(&mut delay).poll(&mut cx), Poll::Pending);
551        assert!(!delay.is_terminated());
552
553        clock.advance_time(ClockTick::new().set_tick(1));
554        assert_eq!(Pin::new(&mut delay).poll(&mut cx), Poll::Ready(()));
555        assert!(delay.is_terminated());
556        assert_eq!(Pin::new(&mut delay).poll(&mut cx), Poll::Ready(()));
557        assert!(delay.is_terminated());
558    }
559
560    #[test]
561    fn phase_wait_helpers_schedule_phase_delays() {
562        let clock = Clock::new(1000.0);
563
564        let next_tick = clock.next_tick_and_phase(3);
565        assert_eq!(next_tick.until.tick(), 1);
566        assert_eq!(next_tick.until.phase(), 3);
567
568        let same_tick = clock.wait_phase(1);
569        assert_eq!(same_tick.until.tick(), 0);
570        assert_eq!(same_tick.until.phase(), 1);
571    }
572
573    #[test]
574    fn tick_waits_resume_in_end_phase() {
575        let clock = Clock::new(1000.0);
576
577        let begin = clock.wait_phase(phase::END);
578        assert_eq!(begin.until.tick(), 0);
579        assert_eq!(begin.until.phase(), phase::END);
580
581        clock.advance_time(ClockTick::new().set_phase(phase::END));
582
583        let delay = clock.wait_ticks(1);
584        assert_eq!(delay.until.tick(), 1);
585        assert_eq!(delay.until.phase(), phase::BEGIN);
586    }
587}