riot_wrappers/thread/
tokenparts.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
//! Zero-sized types for threads to document that something is done (often, done the first time)
//! inside the thread.

use core::marker::PhantomData;
use core::mem::MaybeUninit;

/// Data created for each thread that is spawned.
///
/// It encodes for permission to do anything that can only be done once per thread.
pub type StartToken = TokenParts<true, true, true>;

/// Data necessary to return from a thread that has received the [StartToken] permissions.
///
/// This is created from the initials using [TokenParts::can_end()] to erase any left-over
/// information, and certifies that no actions have been taken that forbid the thread from ever
/// terminating (or if they have been taken, they have been undone).
pub struct EndToken {
    _not_send: PhantomData<*const ()>,
}

/// A [StartToken] that has possibly already lost some of its properties.
///
/// Note that while this item shows up in the documentation, the type is actually hidden and only
/// named as [StartToken]. This ensures that more properties can be added compatibly (because no
/// user ever names the type and would thus run into conflicts when the list of generics grows).
/// This has the downside that TokenParts can not easily be passed to downstream functions, and all
/// splitting has to happen at the top level; this should not be a problem in practice.
///
/// The individual parameters are:
///
/// * `MSG_SEMANTICS`: If this is true, the thread has not assigned semantics to messages it would receive yet.
/// * `MSG_QUEUE`: If this is true, the thread has not yet set up a message queue.
/// * `FLAG_SEMANTICS`: If this is true, the thread has not assigned semantics to flags yet.
///
/// (FLAG_SEMANTICS are not used yet, but are already prepared for a wrapper similar to `msg::v2`).
pub struct TokenParts<const MSG_SEMANTICS: bool, const MSG_QUEUE: bool, const FLAG_SEMANTICS: bool>
{
    pub(super) _not_send: PhantomData<*const ()>,
}

impl TokenParts<true, true, true> {
    /// Claim that the current thread has not done anything yet that is covered by this type
    ///
    /// Do not call yourself; this needs to be public because [`riot_main!`](crate::riot_main!) is
    /// a macro and thus technically called from the main crate.
    pub unsafe fn new() -> Self {
        TokenParts {
            _not_send: PhantomData,
        }
    }
}

impl<const MS: bool, const MQ: bool, const FS: bool> TokenParts<MS, MQ, FS> {
    /// Extract a token that states that code that has access to it is running in a thread (and not
    /// in an interrupt).
    pub fn in_thread(&self) -> InThread {
        // unsafe: TokenParts is not Send, so we can be sure to be in a thread
        unsafe { InThread::new_unchecked() }
    }
}

impl<const MQ: bool, const FS: bool> TokenParts<true, MQ, FS> {
    /// Extract the claim that the thread was not previously configured with any messages that
    /// would be sent to it.
    ///
    /// ## Example
    ///
    /// ```
    /// # #![no_std]
    /// # #![feature(start)]
    /// # #[start]
    /// # fn main(_argc: isize, _argv: *const *const u8) -> isize { panic!("Doc tests are not supposed to be run") }
    /// # use riot_wrappers::thread::*;
    /// fn thread(tok: StartToken) -> EndToken {
    ///     let (tok, semantics) = tok.take_msg_semantics();
    ///     // keep working with semantics and start receiving messages
    ///     //
    ///     // receive messages
    ///     //
    ///     // recover semantics when everyone has returned the license to send messages
    ///     let tok = tok.return_msg_semantics(semantics);
    ///     tok.can_end()
    /// }
    /// ```
    #[cfg(feature = "with_msg_v2")]
    pub fn take_msg_semantics(
        self,
    ) -> (
        TokenParts<false, MQ, FS>,
        crate::msg::v2::NoConfiguredMessages,
    ) {
        (
            TokenParts {
                _not_send: PhantomData,
            },
            // unsafe: This is the only safe way, and by construction running only once per thread.
            // The thread can't terminate because if it takes TokenParts it has to return an
            // end token
            unsafe { crate::msg::v2::NoConfiguredMessages::new() },
        )
    }
}

impl<const MQ: bool, const FS: bool> TokenParts<false, MQ, FS> {
    /// Inverse of [TokenParts::take_msg_semantics], indicating that the thread may be terminated again as far
    /// as message semantics are concerned.
    #[cfg(feature = "with_msg_v2")]
    pub fn return_msg_semantics(
        self,
        semantics: crate::msg::v2::NoConfiguredMessages,
    ) -> TokenParts<true, MQ, FS> {
        drop(semantics);
        TokenParts {
            _not_send: PhantomData,
        }
    }
}

impl<const MS: bool, const FS: bool> TokenParts<MS, true, FS> {
    /// Set up a message queue of given size N, and run the function `f` after that has been set
    /// up. `f` gets passed all the remaining thread invariants.
    ///
    /// As this doesn't deal with the message semantics, it can't be sure whether at function
    /// return time all other system components have stopped sending messages; the easy way out is
    /// to require the function to diverge.
    ///
    /// ## Example
    ///
    /// ```
    /// # #![no_std]
    /// # #![feature(start)]
    /// # #[start]
    /// # fn fake_start(_argc: isize, _argv: *const *const u8) -> isize { panic!("Doc tests are not supposed to be run") }
    /// # use riot_wrappers::thread::*;
    /// fn thread(tok: StartToken) -> EndToken {
    ///     tok.with_message_queue::<4, _>(|tok| {
    ///         loop {
    ///             // ...
    ///         }
    ///     })
    /// }
    /// ```
    // Could also be
    //     pub fn with_message_queue<const N: usize, F: FnOnce(TokenParts<MS, false>) -> (R, crate::msg::v2::NoConfiguredMessages), R>(self, f: F) -> (R, TokenParts<true, false>) {
    // but not only would this be harder on the closure expression side, it's also slightly
    // unsound: The `true` MS would mean that the NoConfiguredMessages could be taken out again and
    // used to configure semantics, and then all of a sudden the still-configured message queue
    // would be sent to again.
    pub fn with_message_queue<
        const N: usize,
        F: FnOnce(TokenParts<MS, false, FS>) -> crate::never::Never,
    >(
        self,
        f: F,
    ) -> ! {
        const {
            assert!(
                N.count_ones() == 1,
                "Message queue sizes need to be powers of 2"
            )
        };
        let mut queue: MaybeUninit<[riot_sys::msg_t; N]> = MaybeUninit::uninit();
        // unsafe: All requirements of the C function are satisfied
        unsafe { riot_sys::msg_init_queue(queue.as_mut_ptr() as _, N as _) };
        f(TokenParts {
            _not_send: PhantomData,
        })
    }
}

impl<const MQ: bool> TokenParts<true, MQ, true> {
    /// Certify that nothing has been done in this thread that precludes the termination of the
    /// thread
    ///
    /// MessageSemantics need to have been returned (or never taken) for the next thread on this
    /// PID would get weird messages.
    ///
    /// The MessageQueue is not checked for -- as all MessageSemantics have been returned (and thus
    /// nothing can safely send messages any more), even if there is a queue somewhere on the
    /// stack, it wouldn't be touched by others any more. (Of course, that's moot with the current
    /// mechanism of [TokenParts::with_message_queue()] as that diverges anyway).
    pub fn can_end(self) -> EndToken {
        EndToken {
            _not_send: PhantomData,
        }
    }
}

/// Zero-size statement that the current code is not running in an interrupt
#[derive(Copy, Clone, Debug)]
pub struct InThread {
    _not_send: PhantomData<*const ()>,
}

/// Zero-size statement that the current code is running in an interrupt
#[derive(Copy, Clone, Debug)]
pub struct InIsr {
    _not_send: PhantomData<*const ()>,
}

impl InThread {
    unsafe fn new_unchecked() -> Self {
        InThread {
            _not_send: PhantomData,
        }
    }

    /// Check that the code is running in thread mode
    ///
    /// Note that this is actually running code; to avoid that, call [`TokenParts::in_thread()`],
    /// which is a purely type-level procedure.
    pub fn new() -> Result<Self, InIsr> {
        match crate::interrupt::irq_is_in() {
            true => Err(unsafe { InIsr::new_unchecked() }),
            false => Ok(unsafe { InThread::new_unchecked() }),
        }
    }

    /// Wrap a `value` in a [`ValueInThread`]. This makes it non-Send, but may make additional
    /// (safe) methods on it, using the knowledge that it is still being used inside a thread.
    pub fn promote<T>(self, value: T) -> ValueInThread<T> {
        ValueInThread {
            value,
            _in_thread: self,
        }
    }
}

impl InIsr {
    unsafe fn new_unchecked() -> Self {
        InIsr {
            _not_send: PhantomData,
        }
    }

    /// Check that the code is running in IRQ mode
    pub fn new() -> Result<Self, InThread> {
        match InThread::new() {
            Ok(i) => Err(i),
            Err(i) => Ok(i),
        }
    }
}

/// A value combined with an [`InThread`] marker
///
/// This does barely implement anything on its own, but the module implementing `T` might provide
/// extra methods.
///
/// This makes the wrapped value not `Send`.
// Making the type fundamental results in ValueInThread<&Mutex<T>> being shown at Mutex's page.
#[derive(Copy, Clone)]
#[cfg_attr(feature = "nightly_docs", fundamental)]
pub struct ValueInThread<T> {
    value: T,
    _in_thread: InThread,
}

impl<T> ValueInThread<T> {
    /// Extract the wrapped value
    ///
    /// This does not produce the original `in_thread` value; these are easy enough to re-obtain or
    /// to keep a copy of around.
    pub fn into_inner(self) -> T {
        self.value
    }
}

impl<T> core::ops::Deref for ValueInThread<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.value
    }
}

impl<T> core::ops::DerefMut for ValueInThread<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.value
    }
}