xref: /drstd/src/std/sys/itron/thread.rs (revision 86982c5e9b2eaa583327251616ee822c36288824)
1 //! Thread implementation backed by μITRON tasks. Assumes `acre_tsk` and
2 //! `exd_tsk` are available.
3 use super::{
4     abi,
5     error::{expect_success, expect_success_aborting, ItronError},
6     task,
7     time::dur2reltims,
8 };
9 use crate::std::{
10     cell::UnsafeCell,
11     ffi::CStr,
12     hint, io,
13     mem::ManuallyDrop,
14     ptr::NonNull,
15     sync::atomic::{AtomicUsize, Ordering},
16     sys::thread_local_dtor::run_dtors,
17     time::Duration,
18 };
19 
20 pub struct Thread {
21     p_inner: NonNull<ThreadInner>,
22 
23     /// The ID of the underlying task.
24     task: abi::ID,
25 }
26 
27 // Safety: There's nothing in `Thread` that ties it to the original creator. It
28 //         can be dropped by any threads.
29 unsafe impl Send for Thread {}
30 // Safety: `Thread` provides no methods that take `&self`.
31 unsafe impl Sync for Thread {}
32 
33 /// State data shared between a parent thread and child thread. It's dropped on
34 /// a transition to one of the final states.
35 struct ThreadInner {
36     /// This field is used on thread creation to pass a closure from
37     /// `Thread::new` to the created task.
38     start: UnsafeCell<ManuallyDrop<Box<dyn FnOnce()>>>,
39 
40     /// A state machine. Each transition is annotated with `[...]` in the
41     /// source code.
42     ///
43     /// ```text
44     ///
45     ///    <P>: parent, <C>: child, (?): don't-care
46     ///
47     ///       DETACHED (-1)  -------------------->  EXITED (?)
48     ///                        <C>finish/exd_tsk
49     ///          ^
50     ///          |
51     ///          | <P>detach
52     ///          |
53     ///
54     ///       INIT (0)  ----------------------->  FINISHED (-1)
55     ///                        <C>finish
56     ///          |                                    |
57     ///          | <P>join/slp_tsk                    | <P>join/del_tsk
58     ///          |                                    | <P>detach/del_tsk
59     ///          v                                    v
60     ///
61     ///       JOINING                              JOINED (?)
62     ///     (parent_tid)
63     ///                                            ^
64     ///             \                             /
65     ///              \  <C>finish/wup_tsk        / <P>slp_tsk-complete/ter_tsk
66     ///               \                         /                      & del_tsk
67     ///                \                       /
68     ///                 '--> JOIN_FINALIZE ---'
69     ///                          (-1)
70     ///
71     lifecycle: AtomicUsize,
72 }
73 
74 // Safety: The only `!Sync` field, `ThreadInner::start`, is only touched by
75 //         the task represented by `ThreadInner`.
76 unsafe impl Sync for ThreadInner {}
77 
78 const LIFECYCLE_INIT: usize = 0;
79 const LIFECYCLE_FINISHED: usize = usize::MAX;
80 const LIFECYCLE_DETACHED: usize = usize::MAX;
81 const LIFECYCLE_JOIN_FINALIZE: usize = usize::MAX;
82 const LIFECYCLE_DETACHED_OR_JOINED: usize = usize::MAX;
83 const LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE: usize = usize::MAX;
84 // there's no single value for `JOINING`
85 
86 // 64KiB for 32-bit ISAs, 128KiB for 64-bit ISAs.
87 pub const DEFAULT_MIN_STACK_SIZE: usize = 0x4000 * crate::std::mem::size_of::<usize>();
88 
89 impl Thread {
90     /// # Safety
91     ///
92     /// See `thread::Builder::spawn_unchecked` for safety requirements.
93     pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
94         let inner = Box::new(ThreadInner {
95             start: UnsafeCell::new(ManuallyDrop::new(p)),
96             lifecycle: AtomicUsize::new(LIFECYCLE_INIT),
97         });
98 
99         unsafe extern "C" fn trampoline(exinf: isize) {
100             let p_inner: *mut ThreadInner = crate::std::ptr::from_exposed_addr_mut(exinf as usize);
101             // Safety: `ThreadInner` is alive at this point
102             let inner = unsafe { &*p_inner };
103 
104             // Safety: Since `trampoline` is called only once for each
105             //         `ThreadInner` and only `trampoline` touches `start`,
106             //         `start` contains contents and is safe to mutably borrow.
107             let p = unsafe { ManuallyDrop::take(&mut *inner.start.get()) };
108             p();
109 
110             // Fix the current thread's state just in case, so that the
111             // destructors won't abort
112             // Safety: Not really unsafe
113             let _ = unsafe { abi::unl_cpu() };
114             let _ = unsafe { abi::ena_dsp() };
115 
116             // Run TLS destructors now because they are not
117             // called automatically for terminated tasks.
118             unsafe { run_dtors() };
119 
120             let old_lifecycle = inner.lifecycle.swap(
121                 LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE,
122                 Ordering::AcqRel,
123             );
124 
125             match old_lifecycle {
126                 LIFECYCLE_DETACHED => {
127                     // [DETACHED → EXITED]
128                     // No one will ever join, so we'll ask the collector task to
129                     // delete the task.
130 
131                     // In this case, `*p_inner`'s ownership has been moved to
132                     // us, and we are responsible for dropping it. The acquire
133                     // ordering ensures that the swap operation that wrote
134                     // `LIFECYCLE_DETACHED` happens-before `Box::from_raw(
135                     // p_inner)`.
136                     // Safety: See above.
137                     let _ = unsafe { Box::from_raw(p_inner) };
138 
139                     // Safety: There are no pinned references to the stack
140                     unsafe { terminate_and_delete_current_task() };
141                 }
142                 LIFECYCLE_INIT => {
143                     // [INIT → FINISHED]
144                     // The parent hasn't decided whether to join or detach this
145                     // thread yet. Whichever option the parent chooses,
146                     // it'll have to delete this task.
147                     // Since the parent might drop `*inner` as soon as it sees
148                     // `FINISHED`, the release ordering must be used in the
149                     // above `swap` call.
150                 }
151                 parent_tid => {
152                     // Since the parent might drop `*inner` and terminate us as
153                     // soon as it sees `JOIN_FINALIZE`, the release ordering
154                     // must be used in the above `swap` call.
155                     //
156                     // To make the task referred to by `parent_tid` visible, we
157                     // must use the acquire ordering in the above `swap` call.
158 
159                     // [JOINING → JOIN_FINALIZE]
160                     // Wake up the parent task.
161                     expect_success(
162                         unsafe {
163                             let mut er = abi::wup_tsk(parent_tid as _);
164                             if er == abi::E_QOVR {
165                                 // `E_QOVR` indicates there's already
166                                 // a parking token
167                                 er = abi::E_OK;
168                             }
169                             er
170                         },
171                         &"wup_tsk",
172                     );
173                 }
174             }
175         }
176 
177         // Safety: `Box::into_raw` returns a non-null pointer
178         let p_inner = unsafe { NonNull::new_unchecked(Box::into_raw(inner)) };
179 
180         let new_task = ItronError::err_if_negative(unsafe {
181             abi::acre_tsk(&abi::T_CTSK {
182                 // Activate this task immediately
183                 tskatr: abi::TA_ACT,
184                 exinf: p_inner.as_ptr().expose_addr() as abi::EXINF,
185                 // The entry point
186                 task: Some(trampoline),
187                 // Inherit the calling task's base priority
188                 itskpri: abi::TPRI_SELF,
189                 stksz: stack,
190                 // Let the kernel allocate the stack,
191                 stk: crate::std::ptr::null_mut(),
192             })
193         })
194         .map_err(|e| e.as_io_error())?;
195 
196         Ok(Self {
197             p_inner,
198             task: new_task,
199         })
200     }
201 
202     pub fn yield_now() {
203         expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq");
204     }
205 
206     pub fn set_name(_name: &CStr) {
207         // nope
208     }
209 
210     pub fn sleep(dur: Duration) {
211         for timeout in dur2reltims(dur) {
212             expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk");
213         }
214     }
215 
216     pub fn join(self) {
217         // Safety: `ThreadInner` is alive at this point
218         let inner = unsafe { self.p_inner.as_ref() };
219         // Get the current task ID. Panicking here would cause a resource leak,
220         // so just abort on failure.
221         let current_task = task::current_task_id_aborting();
222         debug_assert!(usize::try_from(current_task).is_ok());
223         debug_assert_ne!(current_task as usize, LIFECYCLE_INIT);
224         debug_assert_ne!(current_task as usize, LIFECYCLE_DETACHED);
225 
226         let current_task = current_task as usize;
227 
228         match inner.lifecycle.swap(current_task, Ordering::AcqRel) {
229             LIFECYCLE_INIT => {
230                 // [INIT → JOINING]
231                 // The child task will transition the state to `JOIN_FINALIZE`
232                 // and wake us up.
233                 //
234                 // To make the task referred to by `current_task` visible from
235                 // the child task's point of view, we must use the release
236                 // ordering in the above `swap` call.
237                 loop {
238                     expect_success_aborting(unsafe { abi::slp_tsk() }, &"slp_tsk");
239                     // To synchronize with the child task's memory accesses to
240                     // `inner` up to the point of the assignment of
241                     // `JOIN_FINALIZE`, `Ordering::Acquire` must be used for the
242                     // `load`.
243                     if inner.lifecycle.load(Ordering::Acquire) == LIFECYCLE_JOIN_FINALIZE {
244                         break;
245                     }
246                 }
247 
248                 // [JOIN_FINALIZE → JOINED]
249             }
250             LIFECYCLE_FINISHED => {
251                 // [FINISHED → JOINED]
252                 // To synchronize with the child task's memory accesses to
253                 // `inner` up to the point of the assignment of `FINISHED`,
254                 // `Ordering::Acquire` must be used for the above `swap` call.
255             }
256             _ => unsafe { hint::unreachable_unchecked() },
257         }
258 
259         // Terminate and delete the task
260         // Safety: `self.task` still represents a task we own (because this
261         //         method or `detach_inner` is called only once for each
262         //         `Thread`). The task indicated that it's safe to delete by
263         //         entering the `FINISHED` or `JOIN_FINALIZE` state.
264         unsafe { terminate_and_delete_task(self.task) };
265 
266         // In either case, we are responsible for dropping `inner`.
267         // Safety: The contents of `*p_inner` will not be accessed hereafter
268         let _inner = unsafe { Box::from_raw(self.p_inner.as_ptr()) };
269 
270         // Skip the destructor (because it would attempt to detach the thread)
271         crate::std::mem::forget(self);
272     }
273 }
274 
275 impl Drop for Thread {
276     fn drop(&mut self) {
277         // Safety: `ThreadInner` is alive at this point
278         let inner = unsafe { self.p_inner.as_ref() };
279 
280         // Detach the thread.
281         match inner
282             .lifecycle
283             .swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::AcqRel)
284         {
285             LIFECYCLE_INIT => {
286                 // [INIT → DETACHED]
287                 // When the time comes, the child will figure out that no
288                 // one will ever join it.
289                 // The ownership of `*p_inner` is moved to the child thread.
290                 // The release ordering ensures that the above swap operation on
291                 // `lifecycle` happens-before the child thread's
292                 // `Box::from_raw(p_inner)`.
293             }
294             LIFECYCLE_FINISHED => {
295                 // [FINISHED → JOINED]
296                 // The task has already decided that we should delete the task.
297                 // To synchronize with the child task's memory accesses to
298                 // `inner` up to the point of the assignment of `FINISHED`,
299                 // the acquire ordering is required for the above `swap` call.
300 
301                 // Terminate and delete the task
302                 // Safety: `self.task` still represents a task we own (because
303                 //         this method or `join_inner` is called only once for
304                 //         each `Thread`). The task indicated that it's safe to
305                 //         delete by entering the `FINISHED` state.
306                 unsafe { terminate_and_delete_task(self.task) };
307 
308                 // Wwe are responsible for dropping `*p_inner`.
309                 // Safety: The contents of `*p_inner` will not be accessed hereafter
310                 let _ = unsafe { Box::from_raw(self.p_inner.as_ptr()) };
311             }
312             _ => unsafe { hint::unreachable_unchecked() },
313         }
314     }
315 }
316 
317 pub mod guard {
318     pub type Guard = !;
319     pub unsafe fn current() -> Option<Guard> {
320         None
321     }
322     pub unsafe fn init() -> Option<Guard> {
323         None
324     }
325 }
326 
327 /// Terminate and delete the specified task.
328 ///
329 /// This function will abort if `deleted_task` refers to the calling task.
330 ///
331 /// It is assumed that the specified task is solely managed by the caller -
332 /// i.e., other threads must not "resuscitate" the specified task or delete it
333 /// prematurely while this function is still in progress. It is allowed for the
334 /// specified task to exit by its own.
335 ///
336 /// # Safety
337 ///
338 /// The task must be safe to terminate. This is in general not true
339 /// because there might be pinned references to the task's stack.
340 unsafe fn terminate_and_delete_task(deleted_task: abi::ID) {
341     // Terminate the task
342     // Safety: Upheld by the caller
343     match unsafe { abi::ter_tsk(deleted_task) } {
344         // Indicates the task is already dormant, ignore it
345         abi::E_OBJ => {}
346         er => {
347             expect_success_aborting(er, &"ter_tsk");
348         }
349     }
350 
351     // Delete the task
352     // Safety: Upheld by the caller
353     expect_success_aborting(unsafe { abi::del_tsk(deleted_task) }, &"del_tsk");
354 }
355 
356 /// Terminate and delete the calling task.
357 ///
358 /// Atomicity is not required - i.e., it can be assumed that other threads won't
359 /// `ter_tsk` the calling task while this function is still in progress. (This
360 /// property makes it easy to implement this operation on μITRON-derived kernels
361 /// that don't support `exd_tsk`.)
362 ///
363 /// # Safety
364 ///
365 /// The task must be safe to terminate. This is in general not true
366 /// because there might be pinned references to the task's stack.
367 unsafe fn terminate_and_delete_current_task() -> ! {
368     expect_success_aborting(unsafe { abi::exd_tsk() }, &"exd_tsk");
369     // Safety: `exd_tsk` never returns on success
370     unsafe { crate::std::hint::unreachable_unchecked() };
371 }
372 
373 pub fn available_parallelism() -> io::Result<crate::std::num::NonZeroUsize> {
374     super::unsupported()
375 }
376