ax_runtime/thread/lifecycle/
extension.rs1use super::*;
4
5pub(in crate::thread) static RUNTIME_THREAD_EXTENSION_OPS: ThreadExtensionOps =
6 ThreadExtensionOps {
7 on_switch_in: runtime_thread_switch_in_hook,
8 on_switch_out: runtime_thread_switch_out_hook,
9 on_exit: runtime_thread_exit_hook,
10 on_deadline_overrun: runtime_thread_deadline_overrun_hook,
11 drop: runtime_thread_drop_hook,
12 };
13
14pub(in crate::thread) unsafe fn runtime_thread_extension(data: usize) -> ThreadExtension {
15 let os_extension = unsafe { runtime_thread_data_from_raw(data) }
16 .os_extension
17 .as_ref();
18 let scheduler_tick_cpu_time = os_extension.and_then(ThreadExtension::scheduler_tick_cpu_time);
19 let scheduler_tick_gate = os_extension.and_then(ThreadExtension::scheduler_tick_work_gate);
20 let forwards_running_policy = os_extension
21 .and_then(ThreadExtension::running_policy_applied_hook)
22 .is_some();
23 let mut extension = unsafe { ThreadExtension::new(data, &RUNTIME_THREAD_EXTENSION_OPS) };
26 if let Some(accounting) = scheduler_tick_cpu_time {
27 extension = extension.with_scheduler_tick_cpu_time(accounting);
28 }
29 if let Some(gate) = scheduler_tick_gate {
30 extension =
34 unsafe { extension.with_scheduler_tick_work(gate, runtime_thread_scheduler_tick_hook) };
35 }
36 if forwards_running_policy {
37 extension = unsafe {
41 extension.with_running_policy_applied_hook(runtime_thread_policy_applied_hook)
42 };
43 }
44 extension
45}
46
47unsafe extern "Rust" fn runtime_thread_switch_in_hook(
48 data: usize,
49 thread: ThreadId,
50 base_policy: SchedulePolicy,
51 charged_runtime_ns: u64,
52) {
53 let runtime = unsafe { runtime_thread_data_from_raw(data) };
54 if let Some(extension) = runtime.os_extension.as_ref() {
55 unsafe {
58 (extension.ops().on_switch_in)(
59 extension.data(),
60 thread,
61 base_policy,
62 charged_runtime_ns,
63 )
64 };
65 }
66}
67
68unsafe extern "Rust" fn runtime_thread_switch_out_hook(
69 data: usize,
70 thread: ThreadId,
71 reason: SwitchReason,
72) {
73 let runtime = unsafe { runtime_thread_data_from_raw(data) };
74 if let Some(extension) = runtime.os_extension.as_ref() {
75 unsafe { (extension.ops().on_switch_out)(extension.data(), thread, reason) };
77 }
78}
79
80unsafe extern "Rust" fn runtime_thread_exit_hook(data: usize, thread: ThreadId) {
81 let runtime = unsafe { runtime_thread_data_from_raw(data) };
82 if let Some(extension) = runtime.os_extension.as_ref() {
83 unsafe { (extension.ops().on_exit)(extension.data(), thread) };
85 }
86 publish_runtime_exit_completion(runtime);
90}
91
92pub(super) fn publish_runtime_exit_completion(runtime: &RuntimeThreadData) {
93 if runtime
94 .exit_completed
95 .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
96 .is_ok()
97 {
98 runtime.join_wait.notify_all();
99 }
100}
101
102unsafe extern "Rust" fn runtime_thread_deadline_overrun_hook(data: usize, thread: ThreadId) {
103 let runtime = unsafe { runtime_thread_data_from_raw(data) };
104 if let Some(extension) = runtime.os_extension.as_ref() {
105 unsafe { (extension.ops().on_deadline_overrun)(extension.data(), thread) };
107 }
108}
109
110unsafe extern "Rust" fn runtime_thread_policy_applied_hook(
111 data: usize,
112 thread: ThreadId,
113 base_policy: SchedulePolicy,
114 observed_ns: u64,
115) {
116 let runtime = unsafe { runtime_thread_data_from_raw(data) };
117 let Some(extension) = runtime.os_extension.as_ref() else {
118 panic!(
119 "runtime policy forwarding lost OS extension for thread {:#x}",
120 thread.as_u64()
121 );
122 };
123 if !unsafe { extension.forward_running_policy_applied(thread, base_policy, observed_ns) } {
124 panic!(
125 "runtime policy forwarding lost callback for thread {:#x}",
126 thread.as_u64()
127 );
128 }
129}
130
131unsafe extern "Rust" fn runtime_thread_scheduler_tick_hook(
132 data: usize,
133 thread: ThreadId,
134 observed_ns: u64,
135) -> SchedulerTickWorkDisposition {
136 let runtime = unsafe { runtime_thread_data_from_raw(data) };
137 let Some(extension) = runtime.os_extension.as_ref() else {
138 panic!(
139 "runtime scheduler-tick forwarding lost OS extension for thread {:#x}",
140 thread.as_u64()
141 );
142 };
143 unsafe { extension.forward_scheduler_tick_work(thread, observed_ns) }.unwrap_or_else(|| {
144 panic!(
145 "runtime scheduler-tick forwarding lost callback for thread {:#x}",
146 thread.as_u64()
147 )
148 })
149}
150
151unsafe extern "Rust" fn runtime_thread_drop_hook(data: usize) {
152 drop(unsafe { Box::from_raw(ptr::with_exposed_provenance_mut::<RuntimeThreadData>(data)) });
155}
156
157unsafe fn runtime_thread_data_from_raw(data: usize) -> &'static RuntimeThreadData {
158 unsafe { &*ptr::with_exposed_provenance::<RuntimeThreadData>(data) }
161}
162
163pub fn thread_os_extension(
165 thread: &ThreadHandle,
166) -> Result<Option<ThreadOsExtensionBorrow<'_>>, TaskError> {
167 let runtime = task_system()
168 .ok_or(TaskError::NotInitialized)?
169 .thread_extension(thread)?;
170 let RuntimeExtensionKind::Runtime = classify_runtime_extension(
171 runtime.as_ref().map(|extension| extension.ops()),
172 runtime.as_ref().map_or(0, |extension| extension.data()),
173 )?
174 else {
175 return Ok(None);
176 };
177 let Some(runtime) = runtime else {
178 unreachable!("classified runtime extension must be present")
179 };
180 let data = unsafe { runtime_thread_data_from_raw(runtime.data()) };
183 Ok(data
184 .os_extension
185 .as_ref()
186 .map(|extension| ThreadOsExtensionBorrow {
187 data: extension.data(),
188 ops: extension.ops(),
189 _runtime: runtime,
190 }))
191}
192
193pub fn current_os_extension() -> Result<Option<ThreadOsExtensionLease>, TaskError> {
195 let runtime = current_thread_extension()?;
196 let RuntimeExtensionKind::Runtime = classify_runtime_extension(
197 runtime.as_ref().map(|extension| extension.ops()),
198 runtime.as_ref().map_or(0, |extension| extension.data()),
199 )?
200 else {
201 return Ok(None);
202 };
203 let Some(runtime) = runtime else {
204 unreachable!("classified runtime extension must be present")
205 };
206 let data = unsafe { runtime_thread_data_from_raw(runtime.data()) };
209 Ok(data
210 .os_extension
211 .as_ref()
212 .map(|extension| ThreadOsExtensionLease {
213 data: extension.data(),
214 ops: extension.ops(),
215 _runtime: runtime,
216 }))
217}
218
219#[derive(Clone, Copy, Debug, Eq, PartialEq)]
220pub(in crate::thread) enum RuntimeExtensionKind {
221 Missing,
222 Runtime,
223}
224
225pub(in crate::thread) fn classify_runtime_extension(
226 ops: Option<&ThreadExtensionOps>,
227 data: usize,
228) -> Result<RuntimeExtensionKind, TaskError> {
229 let Some(ops) = ops else {
230 return Ok(RuntimeExtensionKind::Missing);
231 };
232 if !core::ptr::eq(ops, &RUNTIME_THREAD_EXTENSION_OPS) {
233 return Err(TaskError::InvalidConfiguration);
234 }
235 if data == 0 || !data.is_multiple_of(core::mem::align_of::<RuntimeThreadData>()) {
236 return Err(TaskError::InvalidRuntimeHandle);
237 }
238 Ok(RuntimeExtensionKind::Runtime)
239}