twizzler_abi/
thread.rs

1//! Functions for manipulating threads.
2
3use core::sync::atomic::{AtomicU64, Ordering};
4#[cfg(not(feature = "kernel"))]
5use core::time::Duration;
6
7use twizzler_rt_abi::marker::BaseType;
8
9#[cfg(not(feature = "kernel"))]
10use crate::syscall::*;
11use crate::syscall::{ThreadSyncFlags, ThreadSyncOp, ThreadSyncReference, ThreadSyncSleep};
12#[allow(unused_imports)]
13use crate::{
14    object::{ObjID, Protections},
15    syscall::{MapFlags, ThreadSpawnArgs, ThreadSpawnFlags},
16};
17
18pub mod event;
19/// Base type for a thread object.
20#[derive(Default)]
21#[repr(C)]
22pub struct ThreadRepr {
23    version: u32,
24    flags: u32,
25    #[cfg(not(feature = "kernel"))]
26    status: AtomicU64,
27    #[cfg(feature = "kernel")]
28    pub status: AtomicU64,
29    code: AtomicU64,
30}
31
32impl BaseType for ThreadRepr {}
33
34/// Possible execution states for a thread. The transitions available are:
35/// +------------+     +-----------+     +-------------+
36/// |  Sleeping  +<--->+  Running  +<--->+  Suspended  |
37/// +------------+     +-----+-----+     +-------------+
38///                          |
39///                          |   +----------+
40///                          +-->+  Exited  |
41///                              +----------+
42/// The kernel will not transition a thread out of the exited state.
43#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
44#[repr(u8)]
45pub enum ExecutionState {
46    /// The thread is running or waiting to be scheduled on a CPU.
47    Running,
48    /// The thread is sleeping, waiting for a condition in-kernel.
49    Sleeping,
50    /// The thread is suspended, and will not resume until manually transitioned back to running.
51    Suspended,
52    /// The thread has terminated, and will never run again.
53    Exited = 255,
54}
55
56impl ExecutionState {
57    pub fn from_status(status: u64) -> Self {
58        // If we see a status we don't understand, just assume the thread is running.
59        match status & 0xff {
60            1 => ExecutionState::Sleeping,
61            2 => ExecutionState::Suspended,
62            255 => ExecutionState::Exited,
63            _ => ExecutionState::Running,
64        }
65    }
66
67    pub fn to_status(&self) -> u64 {
68        match self {
69            ExecutionState::Running => 0,
70            ExecutionState::Sleeping => 1,
71            ExecutionState::Suspended => 2,
72            ExecutionState::Exited => 255,
73        }
74    }
75}
76
77impl ThreadRepr {
78    pub fn get_state(&self) -> ExecutionState {
79        let status = self.status.load(Ordering::Acquire);
80        ExecutionState::from_status(status)
81    }
82
83    pub fn get_code(&self) -> u64 {
84        self.code.load(Ordering::SeqCst)
85    }
86
87    pub fn set_state(&self, state: ExecutionState, code: u64) -> ExecutionState {
88        let mut old_status = self.status.load(Ordering::SeqCst);
89        loop {
90            let old_state = ExecutionState::from_status(old_status);
91            if old_state == ExecutionState::Exited {
92                return old_state;
93            }
94
95            let status = state as u8 as u64;
96            if state == ExecutionState::Exited {
97                self.code.store(code, Ordering::SeqCst);
98            }
99
100            let result = self.status.compare_exchange(
101                old_status,
102                status,
103                Ordering::SeqCst,
104                Ordering::SeqCst,
105            );
106            match result {
107                Ok(_) => {
108                    if !(old_state == ExecutionState::Running && state == ExecutionState::Sleeping
109                        || old_state == ExecutionState::Sleeping
110                            && state == ExecutionState::Running)
111                        && old_state != state
112                    {
113                        #[cfg(not(feature = "kernel"))]
114                        let _ = sys_thread_sync(
115                            &mut [ThreadSync::new_wake(ThreadSyncWake::new(
116                                ThreadSyncReference::Virtual(&self.status),
117                                usize::MAX,
118                            ))],
119                            None,
120                        );
121                    }
122                    return old_state;
123                }
124                Err(x) => {
125                    old_status = x;
126                }
127            }
128        }
129    }
130
131    /// Create a [ThreadSyncSleep] that will wait until the thread's state matches `state`.
132    pub fn waitable(&self, state: ExecutionState) -> ThreadSyncSleep {
133        ThreadSyncSleep::new(
134            ThreadSyncReference::Virtual(&self.status),
135            state as u64,
136            ThreadSyncOp::Equal,
137            ThreadSyncFlags::INVERT,
138        )
139    }
140
141    /// Create a [ThreadSyncSleep] that will wait until the thread's state is _not_ `state`.
142    pub fn waitable_until_not(&self, state: ExecutionState) -> ThreadSyncSleep {
143        ThreadSyncSleep::new(
144            ThreadSyncReference::Virtual(&self.status),
145            state as u64,
146            ThreadSyncOp::Equal,
147            ThreadSyncFlags::empty(),
148        )
149    }
150
151    #[cfg(not(feature = "kernel"))]
152    /// Wait for a thread's status to change, optionally timing out. Return value is None if timeout
153    /// occurs, or Some((ExecutionState, code)) otherwise.
154    pub fn wait(
155        &self,
156        expected: ExecutionState,
157        timeout: Option<Duration>,
158    ) -> Option<(ExecutionState, u64)> {
159        let mut status = self.get_state();
160        loop {
161            if status != expected {
162                return Some((status, self.code.load(Ordering::SeqCst)));
163            }
164            let op = self.waitable_until_not(expected);
165            sys_thread_sync(&mut [ThreadSync::new_sleep(op)], timeout).unwrap();
166            status = self.get_state();
167            if timeout.is_some() && status == expected {
168                return None;
169            }
170        }
171    }
172
173    #[cfg(not(feature = "kernel"))]
174    /// Wait for a thread's status reach a target value, or exited, optionally timing out. The
175    /// actual execution state of the thread is returned.
176    pub fn wait_until(
177        &self,
178        target: ExecutionState,
179        timeout: Option<Duration>,
180    ) -> Option<(ExecutionState, u64)> {
181        let mut status = self.get_state();
182        loop {
183            if status == target {
184                return Some((status, self.code.load(Ordering::SeqCst)));
185            }
186            let op = self.waitable(target);
187            sys_thread_sync(&mut [ThreadSync::new_sleep(op)], timeout).unwrap();
188            status = self.get_state();
189            if timeout.is_some() && status != target {
190                return None;
191            }
192        }
193    }
194}