gwr_engine/
engine.rs

1// Copyright (c) 2023 Graphcore Ltd. All rights reserved.
2
3use std::cell::RefCell;
4use std::future::Future;
5use std::rc::Rc;
6
7use gwr_track::entity::{Entity, toplevel};
8use gwr_track::tracker::stdout_tracker;
9use gwr_track::{Tracker, trace};
10
11use crate::executor::{self, Executor, Spawner};
12use crate::time::clock::Clock;
13use crate::types::{Component, Eventable, SimResult};
14
15/// Use a default clock frequency of 1GHz.
16const DEFAULT_CLOCK_MHZ: f64 = 1000.0;
17
18pub struct Registry {
19    pub entity: Rc<Entity>,
20    components: RefCell<Vec<Component>>,
21}
22
23impl Registry {
24    fn new(parent: &Rc<Entity>) -> Self {
25        Self {
26            entity: Rc::new(Entity::new(parent, "registry")),
27            components: RefCell::new(Vec::new()),
28        }
29    }
30
31    pub fn spawn_components(&self, spawner: &Spawner) {
32        let mut guard = self.components.borrow_mut();
33
34        trace!(self.entity ; "Spawning {} components", guard.len());
35
36        for component in guard.drain(..) {
37            spawner.spawn(async move { component.run().await });
38        }
39    }
40
41    pub fn register(&self, component: Component) {
42        let mut guard = self.components.borrow_mut();
43        guard.push(component);
44    }
45}
46
47pub struct Engine {
48    pub executor: Executor,
49    spawner: Spawner,
50    toplevel: Rc<Entity>,
51    tracker: Tracker,
52    registry: Registry,
53}
54
55impl Engine {
56    /// Create a standalone engine.
57    pub fn new(tracker: &Tracker) -> Self {
58        let toplevel = toplevel(tracker, "top");
59        let (executor, spawner) = executor::new_executor_and_spawner(&toplevel);
60        let registry = Registry::new(&toplevel);
61        Self {
62            executor,
63            spawner,
64            toplevel,
65            tracker: tracker.clone(),
66            registry,
67        }
68    }
69
70    /// Register a component that will be run as the simulation starts
71    pub fn register(&self, component: Component) {
72        self.registry.register(component);
73    }
74
75    pub fn run(&mut self) -> SimResult {
76        self.registry.spawn_components(&self.spawner);
77
78        // Pass an atomic bool that will never be set to true
79        let finished = Rc::new(RefCell::new(false));
80        self.executor.run(&finished)
81    }
82
83    pub fn run_until<T: Default + Copy + 'static>(&mut self, event: Eventable<T>) -> SimResult {
84        self.registry.spawn_components(&self.spawner);
85
86        // Create an atomic bool that is set to true as soon as the event fires.
87        let finished = Rc::new(RefCell::new(false));
88        {
89            let finished = finished.clone();
90            self.spawner.spawn(async move {
91                event.listen().await;
92                *finished.borrow_mut() = true;
93                Ok(())
94            });
95        }
96
97        self.executor.run(&finished)
98    }
99
100    #[must_use]
101    pub fn spawner(&self) -> Spawner {
102        self.spawner.clone()
103    }
104
105    pub fn spawn(&self, future: impl Future<Output = SimResult> + 'static) {
106        self.spawner.spawn(future);
107    }
108
109    #[must_use]
110    pub fn default_clock(&mut self) -> Clock {
111        self.executor.get_clock(DEFAULT_CLOCK_MHZ)
112    }
113
114    #[must_use]
115    pub fn clock_mhz(&mut self, freq_mhz: f64) -> Clock {
116        self.executor.get_clock(freq_mhz)
117    }
118
119    #[must_use]
120    pub fn clock_ghz(&mut self, freq_ghz: f64) -> Clock {
121        self.executor.get_clock(freq_ghz * 1000.0)
122    }
123
124    #[must_use]
125    pub fn time_now_ns(&self) -> f64 {
126        self.executor.time_now_ns()
127    }
128
129    #[must_use]
130    pub fn top(&self) -> &Rc<Entity> {
131        &self.toplevel
132    }
133
134    #[must_use]
135    pub fn tracker(&self) -> Tracker {
136        self.tracker.clone()
137    }
138}
139
140/// Create a default engine that sends [`Track`](gwr_track::Track) events to
141/// stdout.
142///
143/// This is provided to keep documentation examples simple with fewer
144/// concepts to have to consider at once.
145impl Default for Engine {
146    fn default() -> Self {
147        let tracker = stdout_tracker(log::Level::Info);
148        Self::new(&tracker)
149    }
150}
151
152impl Drop for Engine {
153    fn drop(&mut self) {
154        // The tracker can be using a buffered writer and so it needs to be shut down
155        // cleanly to ensure that it is flushed properly.
156        self.tracker.shutdown();
157    }
158}