reactor_rt/
triggers.rs

1/*
2 * Copyright (c) 2021, TU Dresden.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice,
8 *    this list of conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 *    this list of conditions and the following disclaimer in the documentation
12 *    and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
21 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
22 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25use std::fmt::{Debug, Formatter};
26use std::hash::Hash;
27use std::ops::Range;
28use std::time::Instant;
29
30use index_vec::Idx;
31
32use crate::impl_types::TriggerIdImpl;
33use crate::EventTag;
34
35/// Common trait for actions, ports, and timer objects handed
36/// to reaction functions. This is meant to be used through the
37/// API of [ReactionCtx](crate::ReactionCtx) instead of directly.
38pub trait ReactionTrigger<T> {
39    /// Returns whether the trigger is present, given that
40    /// the current logical time is the parameter.
41    #[inline]
42    fn is_present(&self, now: &EventTag, start: &Instant) -> bool {
43        self.use_value_ref(now, start, |opt| opt.is_some())
44    }
45
46    /// Copies the value out, if it is present. Whether a *value*
47    /// is present is not in general the same thing as whether *this trigger*
48    /// [Self::is_present]. See [crate::ReactionCtx::get].
49    fn get_value(&self, now: &EventTag, start: &Instant) -> Option<T>
50    where
51        T: Copy;
52
53    /// Execute an action using the current value of this trigger.
54    /// The closure is called even if the value is absent (with a [None]
55    /// argument).
56    fn use_value_ref<O>(&self, now: &EventTag, start: &Instant, action: impl FnOnce(Option<&T>) -> O) -> O;
57}
58
59#[cfg(not(feature = "no-unsafe"))]
60pub trait ReactionTriggerWithRefAccess<T> {
61    /// Returns a reference to the value, if it is present. Whether a *value*
62    /// is present is not in general the same thing as whether *this trigger*
63    /// [Self::is_present]. See [crate::ReactionCtx::get_ref].
64    ///
65    /// This does not require the value to be Copy, however, the implementation
66    /// of this method currently may require unsafe code. The method is therefore
67    /// not offered when compiling with the `no-unsafe` feature.
68    fn get_value_ref(&self, now: &EventTag, start: &Instant) -> Option<&T>;
69}
70
71/// Something on which we can declare a trigger dependency
72/// in the dependency graph.
73#[doc(hidden)]
74pub trait TriggerLike {
75    fn get_id(&self) -> TriggerId;
76}
77
78/// The ID of a trigger component.
79#[derive(Eq, PartialEq, Copy, Clone, Hash, Ord, PartialOrd)]
80pub struct TriggerId(TriggerIdImpl);
81
82// Historical note: in the past, TriggerId was a newtype over a GlobalId.
83// The structure of GlobalId was nice, as it allows us to print nice debug
84// info. But it also forces us to use inefficient data structures to get a Map<TriggerId, ...>,
85// because ids were not allocated consecutively. We were previously using
86// hashmaps, now we use IndexVec.
87// Also the structure of GlobalId used to set relatively low
88// ceilings on the number of components and reactions of each
89// reactor. Previously, we could have max 2^16 (reactions+components)
90// per reactor. Now we can have 2^16 reactions per reactor,
91// and range(usize) total components.
92
93impl TriggerId {
94    pub const STARTUP: TriggerId = TriggerId(0);
95    pub const SHUTDOWN: TriggerId = TriggerId(1);
96
97    pub(crate) const FIRST_REGULAR: TriggerId = TriggerId(2);
98
99    #[allow(unused)]
100    pub(crate) fn new(id: TriggerIdImpl) -> Self {
101        assert!(id > 1, "0-1 are reserved for startup & shutdown!");
102        TriggerId(id)
103    }
104
105    #[allow(unused)]
106    pub(crate) fn get_and_incr(&mut self) -> Result<Self, ()> {
107        let id = *self;
108        *self = id.next()?;
109        Ok(id)
110    }
111
112    pub(crate) fn next(&self) -> Result<Self, ()> {
113        self.0.checked_add(1).map(TriggerId).ok_or(())
114    }
115
116    /// Returns an iterator that iterates over the range `(self+1)..(self+1+len)`.
117    /// Returns `Err` on overflow.
118    pub(crate) fn iter_next_range(&self, len: usize) -> Result<impl Iterator<Item = Self>, ()> {
119        if let Some(upper) = self.0.checked_add(1 + (len as TriggerIdImpl)) {
120            Ok(((self.0 + 1)..upper).map(TriggerId))
121        } else {
122            Err(())
123        }
124    }
125
126    pub(crate) fn next_range(&self, len: usize) -> Result<Range<Self>, ()> {
127        if let Some(upper) = self.0.checked_add(1 + (len as TriggerIdImpl)) {
128            Ok(Range { start: self.next()?, end: Self::new(upper) })
129        } else {
130            Err(())
131        }
132    }
133
134    pub(crate) fn iter_range(range: &Range<TriggerId>) -> impl Iterator<Item = TriggerId> {
135        (range.start.0..range.end.0).map(TriggerId)
136    }
137}
138
139impl Idx for TriggerId {
140    fn from_usize(idx: usize) -> Self {
141        // note that this is basically an unchecked call to the ctor
142        // when Self::new checks
143        TriggerId(idx as TriggerIdImpl)
144    }
145
146    #[allow(clippy::unnecessary_cast)]
147    fn index(self) -> usize {
148        // The cast may be unnecessary if TriggerIdImpl resolves
149        // to usize, but that depends on compile-time features.
150        self.0 as usize
151    }
152}
153
154impl Debug for TriggerId {
155    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
156        match *self {
157            TriggerId::STARTUP => write!(f, "startup"),
158            TriggerId::SHUTDOWN => write!(f, "shutdown"),
159            TriggerId(id) => write!(f, "{}", id),
160        }
161    }
162}