]> git.proxmox.com Git - rustc.git/blob - library/std/src/sys/itron/thread.rs
New upstream version 1.69.0+dfsg1
[rustc.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 }