summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/itron/thread.rs
blob: d28f57f33be20566de33bd7a79389a85067105a6 (plain)
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
//! Thread implementation backed by μITRON tasks. Assumes `acre_tsk` and
//! `exd_tsk` are available.
use super::{
    abi,
    error::{expect_success, expect_success_aborting, ItronError},
    task,
    time::dur2reltims,
};
use crate::{
    cell::UnsafeCell,
    ffi::CStr,
    hint, io,
    mem::ManuallyDrop,
    sync::atomic::{AtomicUsize, Ordering},
    sys::thread_local_dtor::run_dtors,
    time::Duration,
};

pub struct Thread {
    inner: ManuallyDrop<Box<ThreadInner>>,

    /// The ID of the underlying task.
    task: abi::ID,
}

/// State data shared between a parent thread and child thread. It's dropped on
/// a transition to one of the final states.
struct ThreadInner {
    /// This field is used on thread creation to pass a closure from
    /// `Thread::new` to the created task.
    start: UnsafeCell<ManuallyDrop<Box<dyn FnOnce()>>>,

    /// A state machine. Each transition is annotated with `[...]` in the
    /// source code.
    ///
    /// ```text
    ///
    ///    <P>: parent, <C>: child, (?): don't-care
    ///
    ///       DETACHED (-1)  -------------------->  EXITED (?)
    ///                        <C>finish/exd_tsk
    ///          ^
    ///          |
    ///          | <P>detach
    ///          |
    ///
    ///       INIT (0)  ----------------------->  FINISHED (-1)
    ///                        <C>finish
    ///          |                                    |
    ///          | <P>join/slp_tsk                    | <P>join/del_tsk
    ///          |                                    | <P>detach/del_tsk
    ///          v                                    v
    ///
    ///       JOINING                              JOINED (?)
    ///     (parent_tid)
    ///                                            ^
    ///             \                             /
    ///              \  <C>finish/wup_tsk        / <P>slp_tsk-complete/ter_tsk
    ///               \                         /                      & del_tsk
    ///                \                       /
    ///                 '--> JOIN_FINALIZE ---'
    ///                          (-1)
    ///
    lifecycle: AtomicUsize,
}

// Safety: The only `!Sync` field, `ThreadInner::start`, is only touched by
//         the task represented by `ThreadInner`.
unsafe impl Sync for ThreadInner {}

const LIFECYCLE_INIT: usize = 0;
const LIFECYCLE_FINISHED: usize = usize::MAX;
const LIFECYCLE_DETACHED: usize = usize::MAX;
const LIFECYCLE_JOIN_FINALIZE: usize = usize::MAX;
const LIFECYCLE_DETACHED_OR_JOINED: usize = usize::MAX;
const LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE: usize = usize::MAX;
// there's no single value for `JOINING`

// 64KiB for 32-bit ISAs, 128KiB for 64-bit ISAs.
pub const DEFAULT_MIN_STACK_SIZE: usize = 0x4000 * crate::mem::size_of::<usize>();

impl Thread {
    /// # Safety
    ///
    /// See `thread::Builder::spawn_unchecked` for safety requirements.
    pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
        let inner = Box::new(ThreadInner {
            start: UnsafeCell::new(ManuallyDrop::new(p)),
            lifecycle: AtomicUsize::new(LIFECYCLE_INIT),
        });

        unsafe extern "C" fn trampoline(exinf: isize) {
            // Safety: `ThreadInner` is alive at this point
            let inner = unsafe { &*(exinf as *const ThreadInner) };

            // Safety: Since `trampoline` is called only once for each
            //         `ThreadInner` and only `trampoline` touches `start`,
            //         `start` contains contents and is safe to mutably borrow.
            let p = unsafe { ManuallyDrop::take(&mut *inner.start.get()) };
            p();

            // Fix the current thread's state just in case, so that the
            // destructors won't abort
            // Safety: Not really unsafe
            let _ = unsafe { abi::unl_cpu() };
            let _ = unsafe { abi::ena_dsp() };

            // Run TLS destructors now because they are not
            // called automatically for terminated tasks.
            unsafe { run_dtors() };

            let old_lifecycle = inner
                .lifecycle
                .swap(LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE, Ordering::Release);

            match old_lifecycle {
                LIFECYCLE_DETACHED => {
                    // [DETACHED → EXITED]
                    // No one will ever join, so we'll ask the collector task to
                    // delete the task.

                    // In this case, `inner`'s ownership has been moved to us,
                    // And we are responsible for dropping it. The acquire
                    // ordering is not necessary because the parent thread made
                    // no memory access needing synchronization since the call
                    // to `acre_tsk`.
                    // Safety: See above.
                    let _ = unsafe { Box::from_raw(inner as *const _ as *mut ThreadInner) };

                    // Safety: There are no pinned references to the stack
                    unsafe { terminate_and_delete_current_task() };
                }
                LIFECYCLE_INIT => {
                    // [INIT → FINISHED]
                    // The parent hasn't decided whether to join or detach this
                    // thread yet. Whichever option the parent chooses,
                    // it'll have to delete this task.
                    // Since the parent might drop `*inner` as soon as it sees
                    // `FINISHED`, the release ordering must be used in the
                    // above `swap` call.
                }
                parent_tid => {
                    // Since the parent might drop `*inner` and terminate us as
                    // soon as it sees `JOIN_FINALIZE`, the release ordering
                    // must be used in the above `swap` call.

                    // [JOINING → JOIN_FINALIZE]
                    // Wake up the parent task.
                    expect_success(
                        unsafe {
                            let mut er = abi::wup_tsk(parent_tid as _);
                            if er == abi::E_QOVR {
                                // `E_QOVR` indicates there's already
                                // a parking token
                                er = abi::E_OK;
                            }
                            er
                        },
                        &"wup_tsk",
                    );
                }
            }
        }

        let inner_ptr = (&*inner) as *const ThreadInner;

        let new_task = ItronError::err_if_negative(unsafe {
            abi::acre_tsk(&abi::T_CTSK {
                // Activate this task immediately
                tskatr: abi::TA_ACT,
                exinf: inner_ptr as abi::EXINF,
                // The entry point
                task: Some(trampoline),
                // Inherit the calling task's base priority
                itskpri: abi::TPRI_SELF,
                stksz: stack,
                // Let the kernel allocate the stack,
                stk: crate::ptr::null_mut(),
            })
        })
        .map_err(|e| e.as_io_error())?;

        Ok(Self { inner: ManuallyDrop::new(inner), task: new_task })
    }

    pub fn yield_now() {
        expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq");
    }

    pub fn set_name(_name: &CStr) {
        // nope
    }

    pub fn sleep(dur: Duration) {
        for timeout in dur2reltims(dur) {
            expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk");
        }
    }

    pub fn join(mut self) {
        let inner = &*self.inner;
        // Get the current task ID. Panicking here would cause a resource leak,
        // so just abort on failure.
        let current_task = task::current_task_id_aborting();
        debug_assert!(usize::try_from(current_task).is_ok());
        debug_assert_ne!(current_task as usize, LIFECYCLE_INIT);
        debug_assert_ne!(current_task as usize, LIFECYCLE_DETACHED);

        let current_task = current_task as usize;

        match inner.lifecycle.swap(current_task, Ordering::Acquire) {
            LIFECYCLE_INIT => {
                // [INIT → JOINING]
                // The child task will transition the state to `JOIN_FINALIZE`
                // and wake us up.
                loop {
                    expect_success_aborting(unsafe { abi::slp_tsk() }, &"slp_tsk");
                    // To synchronize with the child task's memory accesses to
                    // `inner` up to the point of the assignment of
                    // `JOIN_FINALIZE`, `Ordering::Acquire` must be used for the
                    // `load`.
                    if inner.lifecycle.load(Ordering::Acquire) == LIFECYCLE_JOIN_FINALIZE {
                        break;
                    }
                }

                // [JOIN_FINALIZE → JOINED]
            }
            LIFECYCLE_FINISHED => {
                // [FINISHED → JOINED]
                // To synchronize with the child task's memory accesses to
                // `inner` up to the point of the assignment of `FINISHED`,
                // `Ordering::Acquire` must be used for the above `swap` call`.
            }
            _ => unsafe { hint::unreachable_unchecked() },
        }

        // Terminate and delete the task
        // Safety: `self.task` still represents a task we own (because this
        //         method or `detach_inner` is called only once for each
        //         `Thread`). The task indicated that it's safe to delete by
        //         entering the `FINISHED` or `JOIN_FINALIZE` state.
        unsafe { terminate_and_delete_task(self.task) };

        // In either case, we are responsible for dropping `inner`.
        // Safety: The contents of `self.inner` will not be accessed hereafter
        let _inner = unsafe { ManuallyDrop::take(&mut self.inner) };

        // Skip the destructor (because it would attempt to detach the thread)
        crate::mem::forget(self);
    }
}

impl Drop for Thread {
    fn drop(&mut self) {
        // Detach the thread.
        match self.inner.lifecycle.swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::Acquire) {
            LIFECYCLE_INIT => {
                // [INIT → DETACHED]
                // When the time comes, the child will figure out that no
                // one will ever join it.
                // The ownership of `self.inner` is moved to the child thread.
                // However, the release ordering is not necessary because we
                // made no memory access needing synchronization since the call
                // to `acre_tsk`.
            }
            LIFECYCLE_FINISHED => {
                // [FINISHED → JOINED]
                // The task has already decided that we should delete the task.
                // To synchronize with the child task's memory accesses to
                // `inner` up to the point of the assignment of `FINISHED`,
                // the acquire ordering is required for the above `swap` call.

                // Terminate and delete the task
                // Safety: `self.task` still represents a task we own (because
                //         this method or `join_inner` is called only once for
                //         each `Thread`). The task  indicated that it's safe to
                //         delete by entering the `FINISHED` state.
                unsafe { terminate_and_delete_task(self.task) };

                // Wwe are responsible for dropping `inner`.
                // Safety: The contents of `self.inner` will not be accessed
                //         hereafter
                unsafe { ManuallyDrop::drop(&mut self.inner) };
            }
            _ => unsafe { hint::unreachable_unchecked() },
        }
    }
}

pub mod guard {
    pub type Guard = !;
    pub unsafe fn current() -> Option<Guard> {
        None
    }
    pub unsafe fn init() -> Option<Guard> {
        None
    }
}

/// Terminate and delete the specified task.
///
/// This function will abort if `deleted_task` refers to the calling task.
///
/// It is assumed that the specified task is solely managed by the caller -
/// i.e., other threads must not "resuscitate" the specified task or delete it
/// prematurely while this function is still in progress. It is allowed for the
/// specified task to exit by its own.
///
/// # Safety
///
/// The task must be safe to terminate. This is in general not true
/// because there might be pinned references to the task's stack.
unsafe fn terminate_and_delete_task(deleted_task: abi::ID) {
    // Terminate the task
    // Safety: Upheld by the caller
    match unsafe { abi::ter_tsk(deleted_task) } {
        // Indicates the task is already dormant, ignore it
        abi::E_OBJ => {}
        er => {
            expect_success_aborting(er, &"ter_tsk");
        }
    }

    // Delete the task
    // Safety: Upheld by the caller
    expect_success_aborting(unsafe { abi::del_tsk(deleted_task) }, &"del_tsk");
}

/// Terminate and delete the calling task.
///
/// Atomicity is not required - i.e., it can be assumed that other threads won't
/// `ter_tsk` the calling task while this function is still in progress. (This
/// property makes it easy to implement this operation on μITRON-derived kernels
/// that don't support `exd_tsk`.)
///
/// # Safety
///
/// The task must be safe to terminate. This is in general not true
/// because there might be pinned references to the task's stack.
unsafe fn terminate_and_delete_current_task() -> ! {
    expect_success_aborting(unsafe { abi::exd_tsk() }, &"exd_tsk");
    // Safety: `exd_tsk` never returns on success
    unsafe { crate::hint::unreachable_unchecked() };
}

pub fn available_parallelism() -> io::Result<crate::num::NonZeroUsize> {
    super::unsupported()
}