ax_task/thread/
tick_work.rs1use alloc::sync::Arc;
4use core::sync::atomic::{AtomicU64, Ordering};
5
6use super::ThreadId;
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum SchedulerTickMode {
15 User,
17 System,
19}
20
21#[derive(Debug)]
26pub struct SchedulerTickCpuTime {
27 user_ns: AtomicU64,
28 system_ns: AtomicU64,
29}
30
31impl SchedulerTickCpuTime {
32 pub const fn new() -> Self {
34 Self {
35 user_ns: AtomicU64::new(0),
36 system_ns: AtomicU64::new(0),
37 }
38 }
39
40 pub fn snapshot(&self) -> SchedulerTickCpuTimeSnapshot {
42 SchedulerTickCpuTimeSnapshot {
43 user_ns: self.user_ns.load(Ordering::Acquire),
44 system_ns: self.system_ns.load(Ordering::Acquire),
45 }
46 }
47
48 pub(crate) fn sample(&self, mode: SchedulerTickMode, tick_ns: u64) {
49 let total = match mode {
50 SchedulerTickMode::User => &self.user_ns,
51 SchedulerTickMode::System => &self.system_ns,
52 };
53 total
54 .try_update(Ordering::Relaxed, Ordering::Relaxed, |current| {
55 Some(current.saturating_add(tick_ns))
56 })
57 .expect("infallible scheduler-tick CPU-time update failed");
58 }
59}
60
61impl Default for SchedulerTickCpuTime {
62 fn default() -> Self {
63 Self::new()
64 }
65}
66
67#[derive(Clone, Copy, Debug, Eq, PartialEq)]
69pub struct SchedulerTickCpuTimeSnapshot {
70 user_ns: u64,
71 system_ns: u64,
72}
73
74impl SchedulerTickCpuTimeSnapshot {
75 pub const fn user_ns(self) -> u64 {
77 self.user_ns
78 }
79
80 pub const fn system_ns(self) -> u64 {
82 self.system_ns
83 }
84}
85
86#[derive(Debug)]
92pub struct SchedulerTickGate {
93 state: AtomicU64,
94}
95
96impl SchedulerTickGate {
97 const ENABLED: u64 = 1;
98 const GENERATION_STEP: u64 = 2;
99
100 pub const fn new() -> Self {
102 Self {
103 state: AtomicU64::new(0),
104 }
105 }
106
107 pub fn set_enabled(&self, enabled: bool) {
113 let mut observed = self.state.load(Ordering::Acquire);
114 loop {
115 if (observed & Self::ENABLED != 0) == enabled {
116 return;
117 }
118 let generation = observed
119 .checked_add(Self::GENERATION_STEP)
120 .expect("scheduler tick gate generation overflow");
121 let updated = (generation & !Self::ENABLED) | u64::from(enabled);
122 match self.state.compare_exchange_weak(
123 observed,
124 updated,
125 Ordering::AcqRel,
126 Ordering::Acquire,
127 ) {
128 Ok(_) => return,
129 Err(current) => observed = current,
130 }
131 }
132 }
133
134 fn enabled_generation(&self) -> Option<u64> {
135 let state = self.state.load(Ordering::Acquire);
136 (state & Self::ENABLED != 0).then_some(state)
137 }
138
139 fn generation_is_enabled(&self, generation: u64) -> bool {
140 self.state.load(Ordering::Acquire) == generation
141 }
142}
143
144impl Default for SchedulerTickGate {
145 fn default() -> Self {
146 Self::new()
147 }
148}
149
150#[derive(Clone, Copy, Debug, Eq, PartialEq)]
161pub enum SchedulerTickWorkDisposition {
162 Complete,
164 Retry,
166}
167
168pub type SchedulerTickTaskWork = unsafe extern "Rust" fn(
170 data: usize,
171 thread: ThreadId,
172 observed_ns: u64,
173) -> SchedulerTickWorkDisposition;
174
175#[derive(Clone, Debug)]
176pub(crate) struct SchedulerTickWork {
177 gate: Arc<SchedulerTickGate>,
178 callback: SchedulerTickTaskWork,
179}
180
181impl SchedulerTickWork {
182 pub(crate) const fn new(gate: Arc<SchedulerTickGate>, callback: SchedulerTickTaskWork) -> Self {
183 Self { gate, callback }
184 }
185
186 pub(crate) fn enabled_generation(&self) -> Option<u64> {
187 self.gate.enabled_generation()
188 }
189
190 pub(crate) fn generation_is_enabled(&self, generation: u64) -> bool {
191 self.gate.generation_is_enabled(generation)
192 }
193
194 pub(crate) fn gate(&self) -> Arc<SchedulerTickGate> {
195 Arc::clone(&self.gate)
196 }
197
198 pub(crate) unsafe fn invoke(
199 &self,
200 data: usize,
201 thread: ThreadId,
202 observed_ns: u64,
203 ) -> SchedulerTickWorkDisposition {
204 unsafe { (self.callback)(data, thread, observed_ns) }
205 }
206}
207
208#[derive(Debug)]
210pub(crate) struct SchedulerTickWorkClaim {
211 work: SchedulerTickWork,
212 generation: u64,
213 observed_ns: u64,
214}
215
216impl SchedulerTickWorkClaim {
217 pub(crate) const fn new(work: SchedulerTickWork, generation: u64, observed_ns: u64) -> Self {
218 Self {
219 work,
220 generation,
221 observed_ns,
222 }
223 }
224
225 pub(crate) const fn generation(&self) -> u64 {
226 self.generation
227 }
228
229 pub(crate) fn generation_is_enabled(&self) -> bool {
230 self.work.generation_is_enabled(self.generation)
231 }
232
233 pub(crate) unsafe fn invoke(
234 &self,
235 data: usize,
236 thread: ThreadId,
237 ) -> SchedulerTickWorkDisposition {
238 unsafe { self.work.invoke(data, thread, self.observed_ns) }
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn periodic_tick_samples_only_the_published_execution_mode() {
248 let accounting = SchedulerTickCpuTime::new();
249
250 accounting.sample(SchedulerTickMode::User, 10);
251 accounting.sample(SchedulerTickMode::System, 10);
252
253 assert_eq!(
254 accounting.snapshot(),
255 SchedulerTickCpuTimeSnapshot {
256 user_ns: 10,
257 system_ns: 10,
258 }
259 );
260 }
261}