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