Skip to main content

kernel_api/threading/
state.rs

1use core::fmt::{Debug, Formatter};
2use core::sync::atomic::{AtomicU64, Ordering};
3
4const fn into_raw(state: ThreadState) -> u64 {
5	// SAFETY: pointer is derived from a reference to a `repr(u8)` enum
6	let tag = u64::from(unsafe { *(&raw const state).cast::<u8>() });
7
8	match state {
9		ThreadState::Killed(exit_code) | ThreadState::Zombie(exit_code) => {
10			let upper = exit_code.cast_unsigned();
11			(u64::from(upper) << 32) | tag
12		}
13		_ => tag,
14	}
15}
16
17fn from_raw(raw: u64) -> ThreadState {
18	let tag = raw.truncate::<u8>();
19
20	match tag {
21		0 => ThreadState::Ready,
22		1 => ThreadState::Running,
23		2 => ThreadState::Parked,
24		3 => ThreadState::NearlyParked,
25		4 => ThreadState::Killed((raw >> 32).truncate::<u32>().cast_signed()),
26		5 => ThreadState::Zombie((raw >> 32).truncate::<u32>().cast_signed()),
27		_ => unreachable!("invalid thread state"),
28	}
29}
30
31/// An atomic version of [`ThreadState`].
32pub struct AtomicThreadState(AtomicU64);
33
34impl AtomicThreadState {
35	/// Creates a new `AtomicThreadState` from the given [`ThreadState`].
36	///
37	/// # Examples
38	///
39	/// ```
40	/// use kernel_api::threading::{ThreadState, AtomicThreadState};
41	///
42	/// let state = AtomicThreadState::new(ThreadState::Running);
43	/// assert!(state.running());
44	/// ```
45	#[must_use]
46	pub const fn new(state: ThreadState) -> Self {
47		Self(AtomicU64::new(into_raw(state)))
48	}
49
50	/// Atomically updates `self` to `state`.
51	///
52	/// `store` takes an [`Ordering`] argument which describes the memory ordering of this operation.
53	/// Possible values are [`SeqCst`](`Ordering::SeqCst`), [`Release`](`Ordering::Release`) and [`Relaxed`](`Ordering::Relaxed`).
54	///
55	/// # Panics
56	///
57	/// Panics if `ordering` is [`Acquire`](`Ordering::Acquire`) or [`AcqRel`](`Ordering::AcqRel`).
58	///
59	/// # Examples
60	///
61	/// ```
62	/// use kernel_api::threading::{ThreadState, AtomicThreadState};
63	/// use core::sync::atomic::Ordering;
64	///
65	/// let state = AtomicThreadState::new(ThreadState::Running);
66	/// assert!(state.running());
67	/// state.store(ThreadState::Killed(-1), Ordering::SeqCst);
68	/// assert!(!state.running());
69	/// ```
70	pub fn store(&self, state: ThreadState, ordering: Ordering) {
71		self.0.store(into_raw(state), ordering);
72	}
73
74	/// Atomically retrieves the current state of `self`.
75	///
76	/// `load` takes an [`Ordering`] argument which describes the memory ordering of this operation.
77	/// Possible values are [`SeqCst`](`Ordering::SeqCst`), [`Acquire`](`Ordering::Acquire`) and [`Relaxed`](`Ordering::Relaxed`).
78	///
79	/// # Panics
80	///
81	/// Panics if `ordering` is [`Release`](`Ordering::Release`) or [`AcqRel`](`Ordering::AcqRel`).
82	///
83	/// # Examples
84	///
85	/// ```
86	/// use kernel_api::threading::{ThreadState, AtomicThreadState};
87	/// use core::sync::atomic::Ordering;
88	/// use core::assert_matches;
89	///
90	/// let state = AtomicThreadState::new(ThreadState::Running);
91	/// let state = state.load(Ordering::SeqCst);
92	/// assert_matches!(state, ThreadState::Running);
93	/// ```
94	pub fn load(&self, ordering: Ordering) -> ThreadState{
95		from_raw(self.0.load(ordering))
96	}
97
98	/// Stores a value into the thread state if the current state is the same as
99	/// the `current` state.
100	///
101	/// The return value is a result indicating whether the new state was written and
102	/// containing the previous state. On success this value is guaranteed to be equal to
103	/// `current`.
104	///
105	/// `compare_exchange` takes two [`Ordering`] arguments to describe the memory
106	/// ordering of this operation. `success` describes the required ordering for the
107	/// read-modify-write operation that takes place if the comparison with `current` succeeds.
108	/// `failure` describes the required ordering for the load operation that takes place when
109	/// the comparison fails. Using [`Acquire`](`Ordering::Acquire`) as success ordering makes the store part
110	/// of this operation [`Relaxed`](`Ordering::Relaxed`), and using [`Release`](`Ordering::Release`) makes the successful load
111	/// [`Relaxed`](`Ordering::Relaxed`). The failure ordering can only be [`SeqCst`](`Ordering::SeqCst`), [`Acquire`](`Ordering::Acquire`) or [`Relaxed`](`Ordering::Relaxed`).
112	///
113	/// # Examples
114	///
115	/// ```
116	/// use kernel_api::threading::{ThreadState, AtomicThreadState};
117	/// use core::sync::atomic::Ordering;
118	///
119	/// let state = AtomicThreadState::new(ThreadState::Killed(0));
120	///
121	/// assert_eq!(state.compare_exchange(ThreadState::Killed(0), ThreadState::Running,
122	///                                      Ordering::Acquire,
123	///                                      Ordering::Relaxed),
124	///            Ok(ThreadState::Killed(0)));
125	/// assert_eq!(some_var.load(Ordering::Relaxed), ThreadState::Running);
126	///
127	/// assert_eq!(some_var.compare_exchange(ThreadState::Runnable, ThreadState::Parked,
128	///                                      Ordering::SeqCst,
129	///                                      Ordering::Acquire),
130	///            Err(ThreadState::Running));
131	/// assert_eq!(some_var.load(Ordering::Relaxed), ThreadState::Running);
132	/// ```
133	#[expect(clippy::missing_errors_doc, reason = "doesn't make sense in this context")]
134	pub fn compare_exchange(&self, current: ThreadState, new: ThreadState, success: Ordering, failure: Ordering) -> Result<ThreadState, ThreadState> {
135		let current = into_raw(current);
136		let new = into_raw(new);
137		self.0.compare_exchange(current, new, success, failure)
138				.map(from_raw)
139				.map_err(from_raw)
140	}
141
142	/// Fetches the current state, and applies a function to it that returns an optional
143	/// new state. Returns a `Result` of `Ok(previous_state)` if the function returned `Some(_)`, else
144	/// `Err(previous_state)`.
145	///
146	/// Note: This may call the function multiple times if the value has been changed from other threads in
147	/// the meantime, as long as the function returns `Some(_)`, but the function will have been applied
148	/// only once to the stored value.
149	///
150	/// `fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation.
151	/// The first describes the required ordering for when the operation finally succeeds while the second
152	/// describes the required ordering for loads. These correspond to the success and failure orderings of
153	/// [`compare_exchange`] respectively.
154	///
155	/// Using [`Acquire`](`Ordering::Acquire`) as success ordering makes the store part
156	/// of this operation [`Relaxed`](`Ordering::Relaxed`), and using [`Release`](`Ordering::Release`) makes the final successful load
157	/// [`Relaxed`](`Ordering::Relaxed`). The (failed) load ordering can only be [`SeqCst`](`Ordering::SeqCst`), [`Acquire`](`Ordering::Acquire`) or [`Relaxed`](`Ordering::Relaxed`).
158	///
159	/// # Examples
160	///
161	/// ```rust
162	/// use kernel_api::threading::{ThreadState, AtomicThreadState};
163	/// use core::sync::atomic::Ordering;
164	///
165	/// let state = AtomicThreadState::new(ThreadState::Killed(0));
166	/// assert_eq!(state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(ThreadState::Running));
167	/// assert_eq!(
168	///     state.fetch_update(
169	///         Ordering::SeqCst,
170	///         Ordering::SeqCst,
171	///         |old_state| {
172	///             match old_state {
173	///                 ThreadState::Killed(code) => Some(ThreadState::Killed(code + 1)),
174	///                 _ => None,
175	///             }
176	///         }
177	///     ),
178	///     Ok(ThreadState::Killed(0))
179	/// );
180	/// assert_eq!(state.load(Ordering::SeqCst), ThreadState::Killed(1));
181	/// ```
182	#[expect(clippy::missing_errors_doc, reason = "doesn't make sense in this context")]
183	pub fn fetch_update(&self, set_order: Ordering, fetch_order: Ordering, mut f: impl FnMut(ThreadState) -> Option<ThreadState>) -> Result<ThreadState, ThreadState> {
184		self.0.fetch_update(set_order, fetch_order, |val| f(from_raw(val)).map(into_raw))
185				.map(from_raw)
186				.map_err(from_raw)
187	}
188	
189	/// Returns `true` if the thread is actively running.
190	///
191	/// # Examples
192	///
193	/// ```
194	/// use kernel_api::threading::{ThreadState, AtomicThreadState};
195	///
196	/// let running = AtomicThreadState::new(ThreadState::Running);
197	/// assert!(running.running());
198	///
199	/// let dead = AtomicThreadState::new(ThreadState::Killed(0));
200	/// assert!(!dead.running());
201	/// ```
202	pub fn running(&self) -> bool {
203		matches!(
204			self.load(Ordering::SeqCst),
205			ThreadState::Running,
206		)
207	}
208
209	/// Returns `true` if the thread could be run in its current state.
210	///
211	/// # Examples
212	///
213	/// ```
214	/// use kernel_api::threading::{ThreadState, AtomicThreadState};
215	///
216	/// let runnable = AtomicThreadState::new(ThreadState::Runnable);
217	/// assert!(runnable.runnable());
218	///
219	/// let dead = AtomicThreadState::new(ThreadState::Killed(0));
220	/// assert!(!dead.runnable());
221	/// ```
222	pub fn runnable(&self) -> bool {
223		matches!(
224			self.load(Ordering::SeqCst),
225			ThreadState::Running | ThreadState::Ready | ThreadState::NearlyParked,
226		)
227	}
228}
229
230impl Debug for AtomicThreadState {
231	fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
232		write!(f, "AtomicThreadState {{ {:?} }}", self.load(Ordering::Relaxed))
233	}
234}
235
236/// The current state of a thread.
237#[derive(Debug, Copy, Clone)]
238#[repr(u8)]
239pub enum ThreadState {
240	/// The thread is able to run, but has not yet been scheduled.
241	Ready = 0,
242	/// The thread is actively running.
243	Running = 1,
244	/// The thread is parked.
245	Parked = 2,
246	/// The thread is in the process of being parked.
247	NearlyParked = 3,
248	/// The thread has exited with a return code and is waiting to be removed from runqueues.
249	Killed(i32) = 4,
250	/// The thread has exited with a return code.
251	Zombie(i32) = 5,
252}