]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/itron/thread.rs
Auto merge of #107843 - bjorn3:sync_cg_clif-2023-02-09, r=bjorn3
[rust.git] / library / std / src / sys / itron / thread.rs
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::{
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::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::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
121                 .lifecycle
122                 .swap(LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE, Ordering::AcqRel);
123
124             match old_lifecycle {
125                 LIFECYCLE_DETACHED => {
126                     // [DETACHED → EXITED]
127                     // No one will ever join, so we'll ask the collector task to
128                     // delete the task.
129
130                     // In this case, `*p_inner`'s ownership has been moved to
131                     // us, and we are responsible for dropping it. The acquire
132                     // ordering ensures that the swap operation that wrote
133                     // `LIFECYCLE_DETACHED` happens-before `Box::from_raw(
134                     // p_inner)`.
135                     // Safety: See above.
136                     let _ = unsafe { Box::from_raw(p_inner) };
137
138                     // Safety: There are no pinned references to the stack
139                     unsafe { terminate_and_delete_current_task() };
140                 }
141                 LIFECYCLE_INIT => {
142                     // [INIT → FINISHED]
143                     // The parent hasn't decided whether to join or detach this
144                     // thread yet. Whichever option the parent chooses,
145                     // it'll have to delete this task.
146                     // Since the parent might drop `*inner` as soon as it sees
147                     // `FINISHED`, the release ordering must be used in the
148                     // above `swap` call.
149                 }
150                 parent_tid => {
151                     // Since the parent might drop `*inner` and terminate us as
152                     // soon as it sees `JOIN_FINALIZE`, the release ordering
153                     // must be used in the above `swap` call.
154                     //
155                     // To make the task referred to by `parent_tid` visible, we
156                     // must use the acquire ordering in the above `swap` call.
157
158                     // [JOINING → JOIN_FINALIZE]
159                     // Wake up the parent task.
160                     expect_success(
161                         unsafe {
162                             let mut er = abi::wup_tsk(parent_tid as _);
163                             if er == abi::E_QOVR {
164                                 // `E_QOVR` indicates there's already
165                                 // a parking token
166                                 er = abi::E_OK;
167                             }
168                             er
169                         },
170                         &"wup_tsk",
171                     );
172                 }
173             }
174         }
175
176         // Safety: `Box::into_raw` returns a non-null pointer
177         let p_inner = unsafe { NonNull::new_unchecked(Box::into_raw(inner)) };
178
179         let new_task = ItronError::err_if_negative(unsafe {
180             abi::acre_tsk(&abi::T_CTSK {
181                 // Activate this task immediately
182                 tskatr: abi::TA_ACT,
183                 exinf: p_inner.as_ptr().expose_addr() as abi::EXINF,
184                 // The entry point
185                 task: Some(trampoline),
186                 // Inherit the calling task's base priority
187                 itskpri: abi::TPRI_SELF,
188                 stksz: stack,
189                 // Let the kernel allocate the stack,
190                 stk: crate::ptr::null_mut(),
191             })
192         })
193         .map_err(|e| e.as_io_error())?;
194
195         Ok(Self { p_inner, task: new_task })
196     }
197
198     pub fn yield_now() {
199         expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq");
200     }
201
202     pub fn set_name(_name: &CStr) {
203         // nope
204     }
205
206     pub fn sleep(dur: Duration) {
207         for timeout in dur2reltims(dur) {
208             expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk");
209         }
210     }
211
212     pub fn join(self) {
213         // Safety: `ThreadInner` is alive at this point
214         let inner = unsafe { self.p_inner.as_ref() };
215         // Get the current task ID. Panicking here would cause a resource leak,
216         // so just abort on failure.
217         let current_task = task::current_task_id_aborting();
218         debug_assert!(usize::try_from(current_task).is_ok());
219         debug_assert_ne!(current_task as usize, LIFECYCLE_INIT);
220         debug_assert_ne!(current_task as usize, LIFECYCLE_DETACHED);
221
222         let current_task = current_task as usize;
223
224         match inner.lifecycle.swap(current_task, Ordering::AcqRel) {
225             LIFECYCLE_INIT => {
226                 // [INIT → JOINING]
227                 // The child task will transition the state to `JOIN_FINALIZE`
228                 // and wake us up.
229                 //
230                 // To make the task referred to by `current_task` visible from
231                 // the child task's point of view, we must use the release
232                 // ordering in the above `swap` call.
233                 loop {
234                     expect_success_aborting(unsafe { abi::slp_tsk() }, &"slp_tsk");
235                     // To synchronize with the child task's memory accesses to
236                     // `inner` up to the point of the assignment of
237                     // `JOIN_FINALIZE`, `Ordering::Acquire` must be used for the
238                     // `load`.
239                     if inner.lifecycle.load(Ordering::Acquire) == LIFECYCLE_JOIN_FINALIZE {
240                         break;
241                     }
242                 }
243
244                 // [JOIN_FINALIZE → JOINED]
245             }
246             LIFECYCLE_FINISHED => {
247                 // [FINISHED → JOINED]
248                 // To synchronize with the child task's memory accesses to
249                 // `inner` up to the point of the assignment of `FINISHED`,
250                 // `Ordering::Acquire` must be used for the above `swap` call`.
251             }
252             _ => unsafe { hint::unreachable_unchecked() },
253         }
254
255         // Terminate and delete the task
256         // Safety: `self.task` still represents a task we own (because this
257         //         method or `detach_inner` is called only once for each
258         //         `Thread`). The task indicated that it's safe to delete by
259         //         entering the `FINISHED` or `JOIN_FINALIZE` state.
260         unsafe { terminate_and_delete_task(self.task) };
261
262         // In either case, we are responsible for dropping `inner`.
263         // Safety: The contents of `*p_inner` will not be accessed hereafter
264         let _inner = unsafe { Box::from_raw(self.p_inner.as_ptr()) };
265
266         // Skip the destructor (because it would attempt to detach the thread)
267         crate::mem::forget(self);
268     }
269 }
270
271 impl Drop for Thread {
272     fn drop(&mut self) {
273         // Safety: `ThreadInner` is alive at this point
274         let inner = unsafe { self.p_inner.as_ref() };
275
276         // Detach the thread.
277         match inner.lifecycle.swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::AcqRel) {
278             LIFECYCLE_INIT => {
279                 // [INIT → DETACHED]
280                 // When the time comes, the child will figure out that no
281                 // one will ever join it.
282                 // The ownership of `*p_inner` is moved to the child thread.
283                 // The release ordering ensures that the above swap operation on
284                 // `lifecycle` happens-before the child thread's
285                 // `Box::from_raw(p_inner)`.
286             }
287             LIFECYCLE_FINISHED => {
288                 // [FINISHED → JOINED]
289                 // The task has already decided that we should delete the task.
290                 // To synchronize with the child task's memory accesses to
291                 // `inner` up to the point of the assignment of `FINISHED`,
292                 // the acquire ordering is required for the above `swap` call.
293
294                 // Terminate and delete the task
295                 // Safety: `self.task` still represents a task we own (because
296                 //         this method or `join_inner` is called only once for
297                 //         each `Thread`). The task indicated that it's safe to
298                 //         delete by entering the `FINISHED` state.
299                 unsafe { terminate_and_delete_task(self.task) };
300
301                 // Wwe are responsible for dropping `*p_inner`.
302                 // Safety: The contents of `*p_inner` will not be accessed hereafter
303                 let _ = unsafe { Box::from_raw(self.p_inner.as_ptr()) };
304             }
305             _ => unsafe { hint::unreachable_unchecked() },
306         }
307     }
308 }
309
310 pub mod guard {
311     pub type Guard = !;
312     pub unsafe fn current() -> Option<Guard> {
313         None
314     }
315     pub unsafe fn init() -> Option<Guard> {
316         None
317     }
318 }
319
320 /// Terminate and delete the specified task.
321 ///
322 /// This function will abort if `deleted_task` refers to the calling task.
323 ///
324 /// It is assumed that the specified task is solely managed by the caller -
325 /// i.e., other threads must not "resuscitate" the specified task or delete it
326 /// prematurely while this function is still in progress. It is allowed for the
327 /// specified task to exit by its own.
328 ///
329 /// # Safety
330 ///
331 /// The task must be safe to terminate. This is in general not true
332 /// because there might be pinned references to the task's stack.
333 unsafe fn terminate_and_delete_task(deleted_task: abi::ID) {
334     // Terminate the task
335     // Safety: Upheld by the caller
336     match unsafe { abi::ter_tsk(deleted_task) } {
337         // Indicates the task is already dormant, ignore it
338         abi::E_OBJ => {}
339         er => {
340             expect_success_aborting(er, &"ter_tsk");
341         }
342     }
343
344     // Delete the task
345     // Safety: Upheld by the caller
346     expect_success_aborting(unsafe { abi::del_tsk(deleted_task) }, &"del_tsk");
347 }
348
349 /// Terminate and delete the calling task.
350 ///
351 /// Atomicity is not required - i.e., it can be assumed that other threads won't
352 /// `ter_tsk` the calling task while this function is still in progress. (This
353 /// property makes it easy to implement this operation on μITRON-derived kernels
354 /// that don't support `exd_tsk`.)
355 ///
356 /// # Safety
357 ///
358 /// The task must be safe to terminate. This is in general not true
359 /// because there might be pinned references to the task's stack.
360 unsafe fn terminate_and_delete_current_task() -> ! {
361     expect_success_aborting(unsafe { abi::exd_tsk() }, &"exd_tsk");
362     // Safety: `exd_tsk` never returns on success
363     unsafe { crate::hint::unreachable_unchecked() };
364 }
365
366 pub fn available_parallelism() -> io::Result<crate::num::NonZeroUsize> {
367     super::unsupported()
368 }