vex_rt/rtos/
context.rs

1use alloc::{
2    sync::{Arc, Weak},
3    vec::Vec,
4};
5use by_address::ByAddress;
6use core::{cmp::min, time::Duration};
7use owner_monad::OwnerMut;
8use raii_map::set::{insert, Set, SetHandle};
9
10use super::{
11    handle_event, time_since_start, Event, EventHandle, GenericSleep, Instant, Mutex, Selectable,
12};
13use crate::select_merge;
14
15type ContextValue = (Option<Instant>, Mutex<Option<ContextData>>);
16
17#[derive(Clone)]
18#[repr(transparent)]
19/// Represents an ongoing operation which could be cancelled in the future.
20/// Inspired by contexts in the Go programming language.
21///
22/// # Concepts
23///
24/// Contexts have a few important concepts: "cancellation", "parent" and
25/// "deadline". A context can be cancelled by calling its [`Context::cancel()`]
26/// method; this notifies any tasks which are waiting on its [`Context::done()`]
27/// event. It is also cancelled automatically if and when its parent context is
28/// cancelled, and when the last copy of it goes out of scope. A "deadline"
29/// allows a context to be automatically cancelled at a certain timestamp; this
30/// is implemented without creating extra tasks/threads.
31///
32/// # Forking
33///
34/// A context can be "forked", which creates a new child context. This new
35/// context can optionally be created with a deadline.
36pub struct Context(Arc<ContextValue>);
37
38impl Context {
39    #[inline]
40    /// Creates a new global context (i.e., one which has no parent or
41    /// deadline).
42    pub fn new_global() -> Self {
43        Self::new_internal(&[], None)
44    }
45
46    #[inline]
47    /// Cancels a context. This is a no-op if the context is already cancelled.
48    pub fn cancel(&self) {
49        cancel(&self.0.as_ref().1);
50    }
51
52    /// A [`Selectable`] event which occurs when the context is
53    /// cancelled. The sleep amount takes the context deadline into
54    /// consideration.
55    pub fn done(&'_ self) -> impl Selectable + '_ {
56        struct ContextSelect<'a>(&'a Context, EventHandle<ContextHandle>);
57
58        impl<'a> Selectable for ContextSelect<'a> {
59            fn poll(self) -> Result<(), Self> {
60                let mut lock = self.0 .0 .1.lock();
61                let opt = &mut lock.as_mut();
62                if opt.is_some() {
63                    if self.0 .0 .0.map_or(false, |v| v <= time_since_start()) {
64                        opt.take();
65                        Ok(())
66                    } else {
67                        Err(self)
68                    }
69                } else {
70                    Ok(())
71                }
72            }
73            fn sleep(&self) -> GenericSleep {
74                GenericSleep::NotifyTake(self.0 .0 .0)
75            }
76        }
77
78        ContextSelect(self, handle_event(ContextHandle(Arc::downgrade(&self.0))))
79    }
80
81    /// Creates a [`Selectable`] event which occurs when either the given
82    /// `event` resolves, or when the context is cancelled, whichever occurs
83    /// first.
84    pub fn wrap<'a, T: 'a>(
85        &'a self,
86        event: impl Selectable<T> + 'a,
87    ) -> impl Selectable<Option<T>> + 'a {
88        select_merge! {
89            r = event => Some(r),
90            _ = self.done() => None,
91        }
92    }
93
94    fn new_internal(parents: &[&Self], mut deadline: Option<Instant>) -> Self {
95        deadline = parents
96            .iter()
97            .filter_map(|parent| parent.0 .0)
98            .min()
99            .map_or(deadline, |d1| Some(deadline.map_or(d1, |d2| min(d1, d2))));
100        let ctx = Self(Arc::new((deadline, Mutex::new(None))));
101        let mut parent_handles = Vec::new();
102        parent_handles.reserve_exact(parents.len());
103        for parent in parents {
104            if let Some(handle) = insert(
105                ContextHandle(Arc::downgrade(&parent.0)),
106                ctx.0.clone().into(),
107            ) {
108                parent_handles.push(handle);
109            } else {
110                return ctx;
111            }
112        }
113        *ctx.0 .1.lock() = Some(ContextData {
114            _parents: parent_handles,
115            event: Event::new(),
116            children: Set::new(),
117        });
118        ctx
119    }
120}
121
122/// Describes an object from which a child context can be created. Implemented
123/// for contexts and for slices of contexts.
124pub trait ParentContext {
125    /// Forks a context. The new context's parent(s) are `self`.
126    fn fork(&self) -> Context;
127
128    /// Forks a context. Equivalent to [`Self::fork()`], except that the new
129    /// context has a deadline which is the earliest of those in `self` and
130    /// the one provided.
131    fn fork_with_deadline(&self, deadline: Instant) -> Context;
132
133    /// Forks a context. Equivalent to [`Self::fork_with_deadline()`], except
134    /// that the deadline is calculated from the current time and the
135    /// provided timeout duration.
136    fn fork_with_timeout(&self, timeout: Duration) -> Context {
137        self.fork_with_deadline(time_since_start() + timeout)
138    }
139}
140
141impl ParentContext for Context {
142    #[inline]
143    fn fork(&self) -> Context {
144        [self].fork()
145    }
146
147    #[inline]
148    fn fork_with_deadline(&self, deadline: Instant) -> Context {
149        [self].fork_with_deadline(deadline)
150    }
151}
152
153impl ParentContext for [&Context] {
154    #[inline]
155    fn fork(&self) -> Context {
156        Context::new_internal(self, None)
157    }
158
159    #[inline]
160    fn fork_with_deadline(&self, deadline: Instant) -> Context {
161        Context::new_internal(self, Some(deadline))
162    }
163}
164
165struct ContextData {
166    _parents: Vec<SetHandle<ByAddress<Arc<ContextValue>>, ContextHandle>>,
167    event: Event,
168    children: Set<ByAddress<Arc<ContextValue>>>,
169}
170
171impl Drop for ContextData {
172    fn drop(&mut self) {
173        self.event.notify();
174        for child in self.children.iter() {
175            cancel(&child.1)
176        }
177    }
178}
179
180struct ContextHandle(Weak<ContextValue>);
181
182impl OwnerMut<Event> for ContextHandle {
183    fn with<'a, U>(&'a mut self, f: impl FnOnce(&mut Event) -> U) -> Option<U>
184    where
185        Event: 'a,
186    {
187        Some(f(&mut self.0.upgrade()?.as_ref().1.lock().as_mut()?.event))
188    }
189}
190
191impl OwnerMut<Set<ByAddress<Arc<ContextValue>>>> for ContextHandle {
192    fn with<'a, U>(
193        &'a mut self,
194        f: impl FnOnce(&mut Set<ByAddress<Arc<ContextValue>>>) -> U,
195    ) -> Option<U>
196    where
197        ContextValue: 'a,
198    {
199        Some(f(&mut self
200            .0
201            .upgrade()?
202            .as_ref()
203            .1
204            .lock()
205            .as_mut()?
206            .children))
207    }
208}
209
210fn cancel(m: &Mutex<Option<ContextData>>) {
211    m.lock().take();
212}
213
214/// Provides a wrapper for [`Context`] objects which permits the management of
215/// sequential, non-overlapping contexts.
216pub struct ContextWrapper(Option<Context>);
217
218impl ContextWrapper {
219    #[inline]
220    /// Creates a new `ContextWrapper` objects.
221    pub fn new() -> Self {
222        Self(None)
223    }
224
225    /// Gets the current context, if one exists.
226    pub fn current(&self) -> Option<&Context> {
227        self.0.as_ref()
228    }
229
230    /// Cancels the last context, creating a new global context in its place
231    /// (which is returned).
232    pub fn replace(&mut self) -> Context {
233        if let Some(ctx) = self.0.take() {
234            ctx.cancel();
235        }
236        let ctx = Context::new_global();
237        self.0 = Some(ctx.clone());
238        ctx
239    }
240
241    /// Cancels the last context, creating a new context as a child of the given
242    /// context in its place.
243    pub fn replace_ext(&mut self, ctx: impl ParentContext) -> Context {
244        if let Some(ctx) = self.0.take() {
245            ctx.cancel();
246        }
247        let ctx = ctx.fork();
248        self.0 = Some(ctx.clone());
249        ctx
250    }
251}
252
253impl Default for ContextWrapper {
254    #[inline]
255    fn default() -> Self {
256        Self::new()
257    }
258}