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::ReadRequest,
66            1 => AccessType::WriteRequest,
67            2 => AccessType::WriteNonPostedRequest,
68            3 => AccessType::ReadResponse,
69            4 => AccessType::WriteNonPostedResponse,
70            _ => AccessType::Control,
71        }
72    }
73}
74
75impl SimObject for i32 {}
76
77// usize
78impl TotalBytes for usize {
79    fn total_bytes(&self) -> usize {
80        size_of::<usize>()
81    }
82}
83
84impl Routable for usize {
85    fn destination(&self) -> u64 {
86        *self as u64
87    }
88    fn access_type(&self) -> AccessType {
89        match self {
90            0 => AccessType::ReadRequest,
91            1 => AccessType::WriteRequest,
92            2 => AccessType::WriteNonPostedRequest,
93            3 => AccessType::ReadResponse,
94            4 => AccessType::WriteNonPostedResponse,
95            _ => AccessType::Control,
96        }
97    }
98}
99
100impl SimObject for usize {}
101
102/// The `Event` trait defines an object that can be used as an Event
103///
104/// This is a trait that defines the `listen` function that returns a future
105/// so that it can be used in `async` code.
106///
107/// ```rust
108/// use futures::future::BoxFuture;
109/// pub trait Event<T> {
110///     fn listen(&self) -> BoxFuture<'static, T>;
111/// }
112/// ```
113pub trait Event<T> {
114    #[must_use = "Futures do nothing unless you `.await` or otherwise use them"]
115    fn listen(&self) -> BoxFuture<'static, T>;
116
117    /// Allow cloning of Boxed elements of vector for AllOf/AnyOf
118    ///
119    /// See [stack overflow post](https://stackoverflow.com/questions/69890183/how-can-i-clone-a-vecboxdyn-trait)
120    fn clone_dyn(&self) -> Box<dyn Event<T>>;
121}
122
123/// Provide Clone implementation for boxed Event
124impl<T> Clone for Box<dyn Event<T>> {
125    fn clone(self: &Box<dyn Event<T>>) -> Box<dyn Event<T>> {
126        self.clone_dyn()
127    }
128}
129
130/// Complete any pending transactions.
131pub trait Resolve {
132    /// Complete any pending update.
133    fn resolve(&self);
134}
135
136/// A [`Resolver`] is used to register any [`Resolve`] functions that need to be
137/// called.
138pub trait Resolver {
139    fn add_resolve(&self, resolve: Rc<dyn Resolve + 'static>);
140}
141
142pub type BoxFuture<'a, T> = Pin<std::boxed::Box<dyn Future<Output = T> + 'a>>;
143
144/// The `Runnable` trait defines any active functionality that is spawned by a
145/// component.
146///
147/// This is a trait that defines an `async` function and therefore currently
148/// needs to use the `#[async_trait(?Send)]` decorator that converts it to a
149/// pinned boxed result. A basic implementation of the trait looks like:
150///
151/// ```rust
152/// # use gwr_engine::types::SimResult;
153/// # use async_trait::async_trait;
154/// #[async_trait(?Send)]
155/// pub trait Runnable {
156///     async fn run(&self) -> SimResult {
157///         Ok(())
158///     }
159/// }
160/// ```
161///
162/// A default implementation is provided for any compoment that doesn't have any
163/// active behaviour.
164#[async_trait(?Send)]
165pub trait Runnable {
166    /// Provides the method that defines the active element of this component.
167    ///
168    /// Default implementation is to do nothing.
169    async fn run(&self) -> SimResult {
170        Ok(())
171    }
172}