Skip to main content

gwr_components/
delay.rs

1// Copyright (c) 2023 Graphcore Ltd. All rights reserved.
2
3//! A component that adds `delay_ticks` between receiving anything and sending
4//! it on to its output.
5//!
6//! The `Delay` can be configured such that it will return an error if the
7//! output is ever blocked. Otherwise it will implicitly assert back-pressure on
8//! the input.
9//!
10//! # Ports
11//!
12//! This component has the following ports:
13//!  - One [input port](gwr_engine::port::InPort): `rx`
14//!  - One [output port](gwr_engine::port::OutPort): `tx`
15
16//! # Function
17//!
18//! Fundamentally the [Delay]'s functionality is to:
19//!
20//! ```rust
21//! # use std::rc::Rc;
22//! # use async_trait::async_trait;
23//! # use gwr_engine::port::{InPort, OutPort};
24//! # use gwr_engine::sim_error;
25//! # use gwr_engine::time::clock::Clock;
26//! # use gwr_engine::traits::SimObject;
27//! # use gwr_engine::types::SimResult;
28//! # use gwr_track::entity::Entity;
29//! #
30//! # async fn run_tx<T>(
31//! #     entity: Rc<Entity>,
32//! #     mut tx: OutPort<T>,
33//! #     clock: &Clock,
34//! #     mut rx: InPort<T>,
35//! #     delay_ticks: u64,
36//! # ) -> SimResult
37//! # where
38//! #     T: SimObject,
39//! # {
40//! loop {
41//!     let value = rx.get()?.await;
42//!     clock.wait_ticks(delay_ticks).await;
43//!     tx.put(value)?.await;
44//! }
45//! # }
46//! ```
47//!
48//! However, the problem with this is that the input ends up being blocked if
49//! the output does not instantly consume the value. Therefore the [Delay] is
50//! actually split into two halves that manage the ports independently.
51//!
52//! ## Input
53//!
54//! A simplified view of how the input side works is:
55//!
56//! ```rust
57//! # use std::cell::RefCell;
58//! # use std::collections::VecDeque;
59//! # use std::rc::Rc;
60//! # use async_trait::async_trait;
61//! # use gwr_engine::events::repeated::Repeated;
62//! # use gwr_engine::port::{InPort, OutPort};
63//! # use gwr_engine::sim_error;
64//! # use gwr_engine::time::clock::{Clock, ClockTick};
65//! # use gwr_engine::traits::SimObject;
66//! # use gwr_engine::types::SimResult;
67//! # use gwr_track::entity::Entity;
68//! #
69//! # async fn run_rx<T>(
70//! #     entity: Rc<Entity>,
71//! #     mut rx: InPort<T>,
72//! #     clock: &Clock,
73//! #     pending: Rc<RefCell<VecDeque<(T, ClockTick)>>>,
74//! #     pending_changed: Repeated<usize>,
75//! #     delay_ticks: u64,
76//! # ) -> SimResult
77//! # where
78//! #     T: SimObject,
79//! # {
80//! loop {
81//!     // Receive value from input
82//!     let value = rx.get()?.await;
83//!
84//!     // Compute time at which it should leave Delay
85//!     let mut tick = clock.tick_now();
86//!     tick.set_tick(tick.tick() + delay_ticks as u64);
87//!
88//!     // Send to the output side
89//!     pending.borrow_mut().push_back((value, tick));
90//!
91//!     // Wake up output if required
92//!     pending_changed.notify();
93//! }
94//!  # }
95//! ```
96
97//!
98//! ## Output
99//!
100//! A simplified view of how the output side works is:
101//!
102//! ```rust
103//! # use std::cell::RefCell;
104//! # use std::collections::VecDeque;
105//! # use std::rc::Rc;
106//! # use async_trait::async_trait;
107//! # use gwr_engine::events::repeated::Repeated;
108//! # use gwr_engine::port::{InPort, OutPort};
109//! # use gwr_engine::sim_error;
110//! # use gwr_engine::time::clock::{Clock, ClockTick};
111//! # use gwr_engine::traits::{Event, SimObject};
112//! # use gwr_engine::types::SimResult;
113//! # use gwr_track::entity::Entity;
114//! #
115//! # async fn run_tx<T>(
116//! #     entity: Rc<Entity>,
117//! #     mut tx: OutPort<T>,
118//! #     clock: &Clock,
119//! #     pending: Rc<RefCell<VecDeque<(T, ClockTick)>>>,
120//! #     pending_changed: Repeated<usize>,
121//! # ) -> SimResult
122//! # where
123//! #     T: SimObject,
124//! # {
125//! loop {
126//!     // Get next value and tick at which to send value
127//!     if let Some((value, tick)) = pending.borrow_mut().pop_front() {
128//!         // Wait for correct time
129//!         let tick_now = clock.tick_now();
130//!         clock.wait_ticks(tick.tick() - tick_now.tick()).await;
131//!
132//!         // Send value
133//!         tx.put(value)?.await;
134//!     } else {
135//!         // Wait to be notified of new data
136//!         pending_changed.listen().await;
137//!     }
138//! }
139//! # }
140//! ```
141//!
142//! ## Using a [Delay]
143//!
144//! A [Delay] simply needs to be created with the latency through it and
145//! connected between components.
146//!
147//! ```rust
148//! # use std::cell::RefCell;
149//! # use std::rc::Rc;
150//! #
151//! # use gwr_components::delay::Delay;
152//! # use gwr_components::sink::Sink;
153//! # use gwr_components::source::Source;
154//! # use gwr_components::store::ObjectStore;
155//! # use gwr_components::{connect_port, option_box_repeat};
156//! # use gwr_engine::engine::Engine;
157//! # use gwr_engine::port::{InPort, OutPort};
158//! # use gwr_engine::run_simulation;
159//! # use gwr_engine::test_helpers::start_test;
160//! # use gwr_engine::time::clock::Clock;
161//! # use gwr_engine::traits::SimObject;
162//! # use gwr_engine::types::SimResult;
163//! #
164//! # fn source_sink() -> SimResult {
165//! #     let mut engine = start_test(file!());
166//! #     let clock = engine.default_clock();
167//! #
168//! #     let delay_ticks = 3;
169//! #     let num_puts = delay_ticks * 10;
170//! #
171//! #     let top = engine.top();
172//! #     let to_send: Option<Box<dyn Iterator<Item = _>>> = option_box_repeat!(500 ; num_puts);
173//!     // Create the components
174//!     let source = Source::new_and_register(&engine, top, "source", to_send);
175//!     let delay = Delay::new_and_register(&engine, &clock, top, "delay", delay_ticks);
176//!     let sink = Sink::new_and_register(&engine, &clock, top, "sink");
177//!
178//!     // Connect the ports
179//!     connect_port!(source, tx => delay, rx)?;
180//!     connect_port!(delay, tx => sink, rx)?;
181//!
182//!     run_simulation!(engine);
183//! #
184//! #     let num_sunk = sink.num_sunk();
185//! #     assert_eq!(num_sunk, num_puts);
186//! #     Ok(())
187//! # }
188//! ```
189use std::cell::RefCell;
190use std::cmp::Ordering;
191use std::collections::VecDeque;
192use std::rc::Rc;
193
194use async_trait::async_trait;
195use gwr_engine::engine::Engine;
196use gwr_engine::events::repeated::Repeated;
197use gwr_engine::executor::Spawner;
198use gwr_engine::port::{InPort, OutPort, PortStateResult};
199use gwr_engine::sim_error;
200use gwr_engine::time::clock::{Clock, ClockTick};
201use gwr_engine::traits::{Event, Runnable, SimObject};
202use gwr_engine::types::SimResult;
203use gwr_model_builder::{EntityDisplay, EntityGet};
204use gwr_track::entity::Entity;
205use gwr_track::tracker::aka::Aka;
206
207use crate::{connect_tx, port_rx, take_option};
208
209#[derive(EntityGet, EntityDisplay)]
210pub struct Delay<T>
211where
212    T: SimObject,
213{
214    entity: Rc<Entity>,
215    spawner: Spawner,
216    clock: Clock,
217    delay_ticks: RefCell<usize>,
218
219    rx: RefCell<Option<InPort<T>>>,
220    pending: Rc<RefCell<VecDeque<(T, ClockTick)>>>,
221    pending_changed: Repeated<()>,
222    output_changed: Repeated<()>,
223    tx: RefCell<Option<OutPort<T>>>,
224
225    error_on_output_stall: RefCell<bool>,
226}
227
228impl<T> Delay<T>
229where
230    T: SimObject,
231{
232    pub fn new_and_register_with_renames(
233        engine: &Engine,
234        clock: &Clock,
235        parent: &Rc<Entity>,
236        name: &str,
237        aka: Option<&Aka>,
238        delay_ticks: usize,
239    ) -> Rc<Self> {
240        let spawner = engine.spawner();
241        let entity = Rc::new(Entity::new(parent, name));
242        let tx = OutPort::new_with_renames(&entity, "tx", aka);
243        let rx = InPort::new_with_renames(engine, clock, &entity, "rx", aka);
244        let rc_self = Rc::new(Self {
245            entity,
246            spawner,
247            clock: clock.clone(),
248            delay_ticks: RefCell::new(delay_ticks),
249            rx: RefCell::new(Some(rx)),
250            pending: Rc::new(RefCell::new(VecDeque::new())),
251            pending_changed: Repeated::default(),
252            output_changed: Repeated::default(),
253            tx: RefCell::new(Some(tx)),
254            error_on_output_stall: RefCell::new(false),
255        });
256        engine.register(rc_self.clone());
257        rc_self
258    }
259
260    pub fn new_and_register(
261        engine: &Engine,
262        clock: &Clock,
263        parent: &Rc<Entity>,
264        name: &str,
265        delay_ticks: usize,
266    ) -> Rc<Self> {
267        Self::new_and_register_with_renames(engine, clock, parent, name, None, delay_ticks)
268    }
269
270    pub fn set_error_on_output_stall(&self) {
271        *self.error_on_output_stall.borrow_mut() = true;
272    }
273
274    pub fn connect_port_tx(&self, port_state: PortStateResult<T>) -> SimResult {
275        connect_tx!(self.tx, connect ; port_state)
276    }
277
278    pub fn port_rx(&self) -> PortStateResult<T> {
279        port_rx!(self.rx, state)
280    }
281
282    /// Change the delay value. Can only be done before the simulation has
283    /// started.
284    pub fn set_delay(&self, delay_ticks: usize) -> SimResult {
285        if self.rx.borrow().is_none() {
286            return sim_error!(
287                "{}: can't change the delay after the simulation has started",
288                self.entity
289            );
290        }
291        *self.delay_ticks.borrow_mut() = delay_ticks;
292        Ok(())
293    }
294}
295
296#[async_trait(?Send)]
297impl<T> Runnable for Delay<T>
298where
299    T: SimObject,
300{
301    async fn run(&self) -> SimResult {
302        // Spawn the other end of the delay
303        let tx = take_option!(self.tx);
304
305        let entity = self.entity.clone();
306        let clock = self.clock.clone();
307        let pending = self.pending.clone();
308        let pending_changed = self.pending_changed.clone();
309        let output_changed = self.output_changed.clone();
310        let error_on_output_stall = *self.error_on_output_stall.borrow();
311        self.spawner.spawn(async move {
312            run_tx(
313                entity,
314                tx,
315                &clock,
316                pending,
317                pending_changed,
318                output_changed,
319                error_on_output_stall,
320            )
321            .await
322        });
323
324        let mut rx = take_option!(self.rx);
325        let delay_ticks = *self.delay_ticks.borrow();
326        loop {
327            let value = rx.get()?.await;
328            self.entity.track_enter(value.id());
329
330            let mut tick = self.clock.tick_now();
331            tick.set_tick(tick.tick() + delay_ticks as u64);
332
333            self.pending.borrow_mut().push_back((value, tick));
334            self.pending_changed.notify();
335
336            if delay_ticks > 0 && !*self.error_on_output_stall.borrow() {
337                // Enforce back-pressure by waiting until there is room in the pending queue
338                while self.pending.borrow().len() >= delay_ticks {
339                    self.output_changed.listen().await;
340                }
341            }
342        }
343    }
344}
345
346async fn run_tx<T>(
347    entity: Rc<Entity>,
348    mut tx: OutPort<T>,
349    clock: &Clock,
350    pending: Rc<RefCell<VecDeque<(T, ClockTick)>>>,
351    pending_changed: Repeated<()>,
352    output_changed: Repeated<()>,
353    error_on_output_stall: bool,
354) -> SimResult
355where
356    T: SimObject,
357{
358    loop {
359        let next = pending.borrow_mut().pop_front();
360
361        match next {
362            Some((value, tick)) => {
363                let tick_now = clock.tick_now();
364                match tick.cmp(&tick_now) {
365                    Ordering::Greater => {
366                        clock.wait_ticks(tick.tick() - tick_now.tick()).await;
367                    }
368                    Ordering::Less => {
369                        if error_on_output_stall {
370                            return sim_error!("{entity} delay output stalled");
371                        }
372                    }
373                    Ordering::Equal => {
374                        // Do nothing - no need to pause
375                    }
376                }
377
378                entity.track_exit(value.id());
379                tx.put(value)?.await;
380                output_changed.notify();
381            }
382            None => {
383                pending_changed.listen().await;
384            }
385        }
386    }
387}