gwr_engine/traits.rs
1// Copyright (c) 2023 Graphcore Ltd. All rights reserved.
2
3//! A set of common traits used across GWR Engine.
4
5use core::mem::size_of;
6use std::fmt::{Debug, Display};
7use std::future::Future;
8use std::pin::Pin;
9use std::rc::Rc;
10
11use async_trait::async_trait;
12use gwr_track::id::Unique;
13
14use crate::types::{AccessType, SimResult};
15
16/// The `TotalBytes` trait is used to determine how many bytes an object
17/// represents
18///
19/// This trait is used to determine how much time an object will take to be
20/// sent.
21pub trait TotalBytes {
22 fn total_bytes(&self) -> usize;
23}
24
25/// The `Routable` trait provides an interface to an object to enable it to be
26/// routed
27pub trait Routable {
28 fn destination(&self) -> u64;
29 fn access_type(&self) -> AccessType;
30}
31
32/// A super-trait that objects that are passed around the simulation have to
33/// implement
34///
35/// - Clone: It would be nice to use `Copy` instead, but given that
36/// things like `Vec` are not `Copy` we have to use `Clone` instead to allow
37/// the application to keep copies of objects sent around.
38/// - Debug: In order to print "{:?}" objects have to at least implement
39/// Debug. We could require Display, but that requires explicit
40/// implementation.
41/// - Routable: Allows routing.
42/// - TotalBytes: Allows rate limiting.
43/// - Unique: Allows for unique identification of `Entities`.
44/// - 'static: Due to the way that futures are implemented, the lifetimes
45/// need to be `static. This means that objects may have to be placed in
46/// `Box` to make the static.
47pub trait SimObject: Clone + Debug + Display + Unique + TotalBytes + 'static {}
48
49// Implementations for basic types that can be sent around the simulation for
50// testing
51
52// i32
53impl TotalBytes for i32 {
54 fn total_bytes(&self) -> usize {
55 size_of::<i32>()
56 }
57}
58
59impl Routable for i32 {
60 fn destination(&self) -> u64 {
61 *self as u64
62 }
63 fn access_type(&self) -> AccessType {
64 match self {
65 0 => AccessType::Read,
66 1 => AccessType::Write,
67 2 => AccessType::WriteNonPosted,
68 _ => AccessType::Control,
69 }
70 }
71}
72
73impl SimObject for i32 {}
74
75// usize
76impl TotalBytes for usize {
77 fn total_bytes(&self) -> usize {
78 size_of::<usize>()
79 }
80}
81
82impl Routable for usize {
83 fn destination(&self) -> u64 {
84 *self as u64
85 }
86 fn access_type(&self) -> AccessType {
87 match self {
88 0 => AccessType::Read,
89 1 => AccessType::Write,
90 2 => AccessType::WriteNonPosted,
91 _ => AccessType::Control,
92 }
93 }
94}
95
96impl SimObject for usize {}
97
98/// The `Event` trait defines an object that can be used as an Event
99///
100/// This is a trait that defines the `listen` function that returns a future
101/// so that it can be used in `async` code.
102///
103/// ```rust
104/// use futures::future::BoxFuture;
105/// pub trait Event<T> {
106/// fn listen(&self) -> BoxFuture<'static, T>;
107/// }
108/// ```
109pub trait Event<T> {
110 #[must_use = "Futures do nothing unless you `.await` or otherwise use them"]
111 fn listen(&self) -> BoxFuture<'static, T>;
112
113 /// Allow cloning of Boxed elements of vector for AllOf/AnyOf
114 ///
115 /// See [stack overflow post](https://stackoverflow.com/questions/69890183/how-can-i-clone-a-vecboxdyn-trait)
116 fn clone_dyn(&self) -> Box<dyn Event<T>>;
117}
118
119/// Provide Clone implementation for boxed Event
120impl<T> Clone for Box<dyn Event<T>> {
121 fn clone(self: &Box<dyn Event<T>>) -> Box<dyn Event<T>> {
122 self.clone_dyn()
123 }
124}
125
126/// Complete any pending transactions.
127pub trait Resolve {
128 /// Complete any pending update.
129 fn resolve(&self);
130}
131
132/// A [`Resolver`] is used to register any [`Resolve`] functions that need to be
133/// called.
134pub trait Resolver {
135 fn add_resolve(&self, resolve: Rc<dyn Resolve + 'static>);
136}
137
138pub type BoxFuture<'a, T> = Pin<std::boxed::Box<dyn Future<Output = T> + 'a>>;
139
140/// The `Runnable` trait defines any active functionality that is spawned by a
141/// component.
142///
143/// This is a trait that defines an `async` function and therefore currently
144/// needs to use the `#[async_trait(?Send)]` decorator that converts it to a
145/// pinned boxed result. A basic implementation of the trait looks like:
146///
147/// ```rust
148/// # use gwr_engine::types::SimResult;
149/// # use async_trait::async_trait;
150/// #[async_trait(?Send)]
151/// pub trait Runnable {
152/// async fn run(&self) -> SimResult {
153/// Ok(())
154/// }
155/// }
156/// ```
157///
158/// A default implementation is provided for any compoment that doesn't have any
159/// active behaviour.
160#[async_trait(?Send)]
161pub trait Runnable {
162 /// Provides the method that defines the active element of this component.
163 ///
164 /// Default implementation is to do nothing.
165 async fn run(&self) -> SimResult {
166 Ok(())
167 }
168}