ax_task/runtime/interface.rs
1//! Operating-system capability table consumed by ax-task.
2
3use trait_ffi::def_extern_trait;
4
5use crate::{
6 runtime::{
7 RuntimeHandleResult, RuntimeStatus, TaskSystemHandle,
8 cpu::{
9 CpuRemoteHandle, CurrentCpuOwnerHandles, IrqGuardToken, LocalIrqState,
10 PreemptGuardToken, RqClockSample, RuntimeCpuId, SchedulerDeadlineUpdate,
11 SchedulerRuntimeDeadline,
12 },
13 resource::{
14 AddressSpaceDestroyOutcome, AddressSpaceHandle, AddressSpaceMembarrierState,
15 AddressSpaceReclaimArmOutcome, ExecutionContextHandle, KernelContextRequest,
16 MembarrierRegistration, MembarrierRegistrationPhase, RuntimeMembarrierAction,
17 StackHandle, StackRequest, TlsHandle, UserContextRequest,
18 },
19 switch::{
20 ContextThreadBinding, CurrentThreadPublication, RuntimeScheduleOrigin,
21 RuntimeSchedulerEntry, RuntimeSchedulerFrameEnterResult, RuntimeSchedulerReturn,
22 RuntimeSwitchPlan, SchedSwitchRecord, ThreadIdentityV1,
23 },
24 },
25 time::MonotonicInstant,
26};
27
28/// OS capabilities needed by the scheduling core.
29///
30/// Implementations must keep task-system and CPU-local handles valid until
31/// shutdown. All IRQ-path methods must be allocation-free and non-blocking.
32#[def_extern_trait(mod_path = "runtime", abi = "rust")]
33pub trait TaskRuntime {
34 /// Returns the runtime-owned task-system handle, or `NONE` before setup.
35 ///
36 /// # Safety
37 ///
38 /// A non-`NONE` result must identify a pinned [`crate::runtime::TaskSystem`] that
39 /// remains live until shutdown. The linked runtime provider is the trust
40 /// root for this raw handle; callers cannot validate it dynamically.
41 unsafe fn task_system_handle() -> TaskSystemHandle;
42
43 /// Captures the complete pinned scheduler capability for the calling CPU.
44 ///
45 /// This is a CPU-owned capability, not a migration-stable task handle. The
46 /// caller must retain an IRQ guard or scheduler-frame baton from before this
47 /// query until every dereference of the returned object has completed.
48 ///
49 /// # Safety
50 ///
51 /// The returned identity, owner-only [`crate::runtime::cpu::CpuLocal`] handle and
52 /// Arc-backed [`crate::runtime::cpu::CpuRemote`] handle must all describe the same calling
53 /// CPU and remain live until shutdown. The local address must originate
54 /// from the allocation's mutable owner capability, not from a shared
55 /// `CpuLocal` borrow. Before reconstructing a reference, the caller must
56 /// claim the returned remote endpoint's owner gate and retain both that
57 /// claim and its CPU pin for the complete derived-borrow lifetime.
58 unsafe fn current_cpu_owner_handles() -> CurrentCpuOwnerHandles;
59
60 /// Returns the Arc-backed [`crate::runtime::cpu::CpuRemote`] endpoint for the calling CPU.
61 ///
62 /// This is the scheduler-adjacent current-CPU fast path. Unlike
63 /// [`Self::cpu_remote_handle`], it must not derive a CPU identifier and
64 /// resolve that identifier through the global task-system registry.
65 ///
66 /// # Safety
67 ///
68 /// The caller must prevent migration until it has finished every read
69 /// through the returned endpoint. A non-`NONE` result must identify the
70 /// calling CPU's Arc-backed [`crate::runtime::cpu::CpuRemote`] and remain live until
71 /// shutdown. It must not identify a [`crate::runtime::cpu::CpuLocal`] or any other
72 /// allocation.
73 unsafe fn current_cpu_remote_handle() -> CpuRemoteHandle;
74
75 /// Returns the generation-bearing scheduler identity bound to the calling
76 /// execution context.
77 ///
78 /// This is the identity-only equivalent of Linux's direct `current`
79 /// pointer for fast paths that do not need an owner reference. Providers
80 /// must read the task-owned runtime context selected by the architecture
81 /// current-thread register. [`ThreadIdentityV1::NONE`] denotes an unbound
82 /// bootstrap context.
83 ///
84 /// A bound result must equal the identity in
85 /// [`Self::current_thread_publication`] and remain immutable for the
86 /// complete lifetime of that runtime context.
87 fn current_thread_identity() -> ThreadIdentityV1;
88
89 /// Returns the scheduler publication bound to the calling execution context.
90 ///
91 /// This is the local equivalent of Linux's direct `current` task pointer.
92 /// Providers must read the task-owned runtime context selected by the
93 /// architecture current-thread register; they must not resolve the local
94 /// publication through a remote runqueue endpoint.
95 /// [`CurrentThreadPublication::NONE`] denotes an unbound bootstrap context.
96 ///
97 /// A bound result must match the scheduler core retained by the current
98 /// task and remain immutable for the complete lifetime of that runtime
99 /// context. Preemption and migration must not change this task identity.
100 fn current_thread_publication() -> CurrentThreadPublication;
101
102 /// Tests the current execution context's advisory preemption-pending state.
103 ///
104 /// This is the runtime equivalent of Linux's `need_resched()` safe-point
105 /// query. It must read the architecture-selected current state without
106 /// disabling preemption or claiming scheduler work. A `false` result is
107 /// only a snapshot.
108 fn current_preemption_pending() -> bool;
109
110 /// Returns the Arc-backed [`crate::runtime::cpu::CpuRemote`] endpoint for `cpu`.
111 ///
112 /// Unlike [`Self::current_cpu_local_handle`], this handle must never point
113 /// at [`crate::runtime::cpu::CpuLocal`]. Remote producers may retain and dereference the
114 /// endpoint without aliasing the owner CPU's mutable runqueue borrow.
115 ///
116 /// # Safety
117 ///
118 /// A non-`NONE` result must identify the Arc-backed [`crate::runtime::cpu::CpuRemote`]
119 /// endpoint for `cpu` and remain live until shutdown. It must not identify
120 /// a [`crate::runtime::cpu::CpuLocal`] or any other allocation.
121 unsafe fn cpu_remote_handle(cpu: RuntimeCpuId) -> CpuRemoteHandle;
122
123 /// Returns the calling CPU's logical identifier under an existing pin.
124 ///
125 /// # Safety
126 ///
127 /// The caller must prevent migration until it has finished the local
128 /// operation associated with the returned identity.
129 unsafe fn current_cpu_id() -> RuntimeCpuId;
130
131 /// Prepares one owner CPU's runtime facilities for scheduler publication.
132 ///
133 /// The caller holds local IRQ exclusion and has validated that the CPU is
134 /// currently offline. The implementation must prepare every CPU-local
135 /// wake source needed by the scheduler, including its physical
136 /// clockevent, before returning success. It must not allocate, block,
137 /// invoke callbacks, or re-enter ax-task. Failure must leave the runtime
138 /// offline and retryable.
139 fn prepare_cpu_online(cpu: RuntimeCpuId) -> RuntimeStatus;
140
141 /// Stops one owner CPU's runtime facilities before final offline publication.
142 ///
143 /// The scheduler has already closed remote admission and proved the CPU
144 /// quiescent, but still reports it online while this hook runs. The
145 /// implementation must stop every CPU-local wake source, including its
146 /// physical clockevent, before returning success. Failure must leave the
147 /// runtime retryable. The hook must not allocate, block, invoke callbacks,
148 /// or re-enter ax-task.
149 fn prepare_cpu_offline(cpu: RuntimeCpuId) -> RuntimeStatus;
150
151 /// Saves the raw local-interrupt state and disables local interrupts.
152 ///
153 /// This operation does not enter the scheduler's nested IRQ-guard owner
154 /// scope. Synchronization guards need that narrower capability so an IRQ
155 /// return can still own and consume its explicit preemption depth.
156 fn local_irq_save_and_disable() -> LocalIrqState;
157
158 /// Restores a raw local-interrupt state.
159 ///
160 /// # Safety
161 ///
162 /// `state` must have been returned by
163 /// [`Self::local_irq_save_and_disable`] on this CPU and must be restored
164 /// exactly once in properly nested order.
165 unsafe fn local_irq_restore(state: LocalIrqState);
166
167 /// Saves raw interrupt state, disables local IRQs and enters nested guards.
168 /// A hard interrupt that already owns the runtime's IRQ-return pin may
169 /// return [`IrqGuardToken::NONE`]; nested users then borrow that single
170 /// outer CPU owner instead of creating per-handler guard transactions.
171 fn irq_guard_enter() -> IrqGuardToken;
172
173 /// Leaves one nested IRQ guard and restores the outer raw state if needed.
174 ///
175 /// # Safety
176 ///
177 /// `token` must have been returned by `irq_guard_enter` on this CPU and
178 /// must be exited exactly once. [`IrqGuardToken::NONE`] is a no-op borrowed
179 /// owner. Non-empty tokens may be exited in non-LIFO order.
180 unsafe fn irq_guard_exit(token: IrqGuardToken);
181
182 /// Prevents the current task context from being preempted or migrated.
183 ///
184 /// This capability does not disable hardware interrupts. It is valid only
185 /// in task context or inside an active scheduler frame; scheduler state
186 /// shared with hard-IRQ producers uses the separate IRQ-safe lock domain.
187 ///
188 /// When an enclosing scheduler frame or runtime IRQ guard already owns the
189 /// CPU for the complete lock scope, the runtime returns
190 /// [`PreemptGuardToken::NONE`]. The matching lock guard then releases only
191 /// its raw lock; it must not manufacture another ordinary preemption depth
192 /// inside the existing owner transaction.
193 fn preempt_guard_enter() -> PreemptGuardToken;
194
195 /// Leaves one nested task-preemption guard.
196 ///
197 /// The final exit may enter the scheduler when work is pending. When the
198 /// caller already owns a scheduler frame, it must consume only this nested
199 /// depth and preserve the scheduler baton and raw IRQ state.
200 ///
201 /// # Safety
202 ///
203 /// A non-`NONE` `token` must have been returned by
204 /// `preempt_guard_enter` on this task execution context and must be exited
205 /// exactly once. Tokens may be exited in non-LIFO order.
206 unsafe fn preempt_guard_exit(token: PreemptGuardToken);
207
208 /// Leaves one nested task-preemption guard at a hard-IRQ return boundary.
209 ///
210 /// Unlike [`Self::preempt_guard_exit`], the final exit may enter the
211 /// scheduler while hardware IRQs remain disabled and must return with IRQs
212 /// disabled for the architecture exception epilogue.
213 ///
214 /// # Safety
215 ///
216 /// A non-`NONE` `token` must have been returned by
217 /// [`Self::preempt_guard_enter`] on this task execution context and must be
218 /// exited exactly once.
219 unsafe fn preempt_guard_exit_irq_return(token: PreemptGuardToken);
220
221 /// Publishes entry into the runtime's hard-interrupt lifecycle.
222 fn hardirq_enter();
223
224 /// Publishes exit from the runtime's hard-interrupt lifecycle.
225 fn hardirq_exit();
226
227 /// Publishes sticky scheduler work to the current CPU's architecture
228 /// preemption state and reports whether a local safe point makes a self-IPI
229 /// unnecessary.
230 ///
231 /// The caller owns an IRQ guard and has already published the scheduler
232 /// payload. Before returning, the runtime must set the architecture-owned
233 /// `need_resched` state observed by preemption and IRQ return. It may return
234 /// `true` only when that state is guaranteed to reach the scheduler before
235 /// the CPU can sleep: through hard-IRQ return, an active scheduler/preemption
236 /// guard, or atomic conversion of the final task-context IRQ guard into a
237 /// scheduler baton.
238 fn publish_local_scheduler_work() -> bool;
239
240 /// Withdraws the outgoing runtime context's CPU binding after raw switch.
241 ///
242 /// The incoming context calls this exactly once while local IRQs remain
243 /// disabled and before the scheduler clears the outgoing thread's
244 /// `on_cpu` publication. The implementation must not allocate, block,
245 /// invoke callbacks, consume the scheduler baton, or re-enter ax-task. It
246 /// returns the deferred resource-release edge. CPU-time accounting remains
247 /// owned by the runqueue clock and must not open a second time domain in
248 /// the architecture switch tail.
249 /// Any failure is an unrecoverable runtime invariant: the raw switch has
250 /// already committed, so there is no compatibility retry path.
251 fn finish_context_switch_tail() -> bool;
252
253 /// Consumes the CPU-local scheduler switch baton on a fresh context.
254 ///
255 /// The baton is not an [`IrqGuardToken`] and never belongs to a task. A
256 /// resumed scheduler frame consumes the current CPU's baton after the raw
257 /// switch returns; a fresh trampoline calls this hook exactly once after
258 /// completing the switch tail.
259 fn finish_initial_context_switch();
260
261 /// Enters the current CPU's exact scheduler switch phase.
262 ///
263 /// The runtime validates `entry`, disables hardware IRQs, and atomically
264 /// creates one CPU-local baton. For [`RuntimeSchedulerEntry::PreemptExit`]
265 /// and [`RuntimeSchedulerEntry::IrqReturn`], it must transform the exact
266 /// final lock-preemption depth into the scheduler depth. For
267 /// [`RuntimeSchedulerEntry::IrqGuardExit`], it must instead transform the
268 /// final task-context IRQ-publication depth. Neither path may expose a
269 /// fully preemptible intermediate state. The runtime must not save this
270 /// phase in an execution context or migrate ordinary IRQ tokens with tasks.
271 /// [`RuntimeSchedulerEntry::IrqReturnContinuation`] must reproduce Linux's
272 /// IRQ-return pass boundary: establish one preemption depth, enable local
273 /// IRQs without a live scheduler baton, disable them again, then convert
274 /// that exact depth into the next scheduler baton. A successful result
275 /// must carry a non-empty task-system capability; a rejected entry returns
276 /// the unsafe-context sentinel without live capabilities.
277 fn scheduler_frame_guard_enter(
278 origin: RuntimeScheduleOrigin,
279 entry: RuntimeSchedulerEntry,
280 ) -> RuntimeSchedulerFrameEnterResult;
281
282 /// Consumes the current CPU's scheduler switch baton after switch tail.
283 ///
284 /// This hook restores task-context hardware IRQ state and must not schedule
285 /// recursively. It returns `true` only when deferred callbacks may run with
286 /// IRQs enabled and every ordinary guard clear.
287 fn scheduler_frame_guard_exit(
288 return_to: RuntimeSchedulerReturn,
289 needs_reschedule: bool,
290 ) -> bool;
291
292 /// Returns whether execution is currently inside a hard interrupt.
293 fn in_hard_irq() -> bool;
294
295 /// Validates an entry before it publishes task state or creates a baton.
296 ///
297 /// This is the runtime equivalent of Linux `might_sleep()` plus the final
298 /// scheduler-entry context check. It must return [`RuntimeStatus::UnsafeContext`]
299 /// while any ordinary IRQ/preemption guard is live or hardware execution is
300 /// still in hard IRQ context.
301 fn validate_schedule_context(origin: RuntimeScheduleOrigin) -> RuntimeStatus;
302
303 /// Validates one owner-CPU scheduler-state access.
304 ///
305 /// This is the runtime equivalent of Linux's `lockdep_assert_rq_held()`.
306 /// It must return [`RuntimeStatus::Success`] only while the current CPU is
307 /// pinned by an ordinary IRQ guard or owns an active scheduler baton.
308 /// Unlike [`Self::validate_schedule_context`], a completely unguarded task
309 /// context is invalid here: an interrupt-return scheduler entry could
310 /// otherwise re-enter over a live mutable [`crate::runtime::cpu::CpuLocal`] borrow.
311 fn validate_owner_cpu_context() -> RuntimeStatus;
312
313 /// Returns one sample from the finite monotonic `ktime` domain.
314 fn monotonic_now() -> MonotonicInstant;
315
316 /// Returns one coherent source sample for the current owner runqueue.
317 ///
318 /// Runqueue mutation is owner-only: remote operations publish into the
319 /// owner inbox instead of locking another CPU's runqueue. Like Linux
320 /// `update_rq_clock()` on the local rq, the runtime therefore samples the
321 /// pinned current CPU and must not repeat a remote CPU lookup.
322 /// Scheduler absolute values must never be compared directly with
323 /// monotonic deadlines.
324 fn rq_clock_sample() -> RqClockSample;
325
326 /// Commits the current CPU's complete scheduler-deadline state.
327 ///
328 /// The runtime owns the physical clockevent. It must ignore generations
329 /// older than the most recently accepted update and merge the accepted
330 /// scheduler deadline with non-scheduler sources before programming hardware. This
331 /// hook is callable from ordinary task context, so the runtime must hold
332 /// local IRQ exclusion across both state publication and hardware
333 /// programming instead of relying on an implicit caller-side guard.
334 ///
335 /// This is an infallible ownership boundary, like Linux's hrtimer-to-
336 /// clockevent rearm path. A runtime must absorb an expired hardware
337 /// deadline by clamping it to the device's minimum nonzero delta and treat a device that cannot
338 /// retain a wakeup source as a runtime-fatal invariant. Returning a
339 /// recoverable error here would leave the scheduler queue and physical
340 /// clockevent in an unknowable half-committed state.
341 fn publish_scheduler_deadline(update: SchedulerDeadlineUpdate);
342
343 /// Commits the owner CPU's current scheduling-class hrtick.
344 ///
345 /// The caller already owns an IRQ-off scheduler or CPU-owner scope. Unlike
346 /// task and kernel timers, this state is rq-local and must not enter the
347 /// remotely reachable deadline base.
348 fn publish_scheduler_runtime_deadline(update: SchedulerRuntimeDeadline);
349
350 /// Notifies `cpu` after the scheduler has published owner work.
351 ///
352 /// Sticky scheduler flags and owner inbox membership remain owned by
353 /// ax-task. The runtime transports only a coalescible physical edge,
354 /// matching Linux's split between `TIF_NEED_RESCHED`/wake-list state and
355 /// the reschedule or call-function IPI. Success
356 /// means either a fresh edge was sent or an in-flight edge already covers
357 /// this publication; every other status is an unrecoverable lifecycle
358 /// violation.
359 fn notify_scheduler_cpu(cpu: RuntimeCpuId) -> RuntimeStatus;
360
361 /// Restarts the periodic scheduler tick while leaving the idle thread.
362 ///
363 /// Mirrors Linux `tick_nohz_idle_exit()` before `schedule_idle()`: the idle
364 /// loop's own IRQ-off checkpoints cannot cover a reschedule request that
365 /// becomes visible only after IRQs are re-enabled, so the owner schedule
366 /// that switches away from the idle thread owns the restart. The caller is
367 /// the owner CPU inside its scheduler frame with local IRQs disabled. The
368 /// implementation must not allocate, block, invoke callbacks, or re-enter
369 /// ax-task; an already running tick is a no-op.
370 fn idle_exit_restart_scheduler_tick();
371
372 /// Commits one local interrupt wait after the scheduler publishes polling.
373 ///
374 /// The implementation must disable local interrupts, call
375 /// `finish_current_cpu_idle_polling`, and immediately recheck sticky task
376 /// work and physical clockevent state before stopping the periodic tick or
377 /// sleeping. It may use the architecture's atomic IRQ-enable-and-wait
378 /// primitive only when all sources remain idle. Task deadlines stay armed
379 /// while the scheduler tick is stopped. Work published before polling was
380 /// cleared is observed by the final recheck; work published afterwards
381 /// owns a physical interrupt edge. The tick must restart before runnable
382 /// work can leave the idle loop, but may remain stopped across
383 /// non-scheduling IRQs.
384 fn wait_for_interrupt();
385
386 /// Allocates a guarded stack satisfying `request`.
387 ///
388 /// On success, `handle` must be non-zero and uniquely identify a live stack
389 /// accepted by [`Self::deallocate_stack`] until ownership is transferred.
390 fn allocate_stack(request: StackRequest) -> RuntimeHandleResult;
391
392 /// Releases a stack after the reaper proves no context can reference it.
393 ///
394 /// Ownership transfers exactly once. The scheduler has already crossed the
395 /// switch-tail lifetime boundary, so a provider must treat an inability to
396 /// release this handle as a fatal runtime invariant rather than inventing a
397 /// polling retry protocol.
398 fn deallocate_stack(stack: StackHandle);
399
400 /// Allocates TLS for the runtime's fixed kernel-image template.
401 ///
402 /// Template layout, initialization, and alignment belong to the runtime.
403 /// This operation does not allocate arbitrary application TLS.
404 ///
405 /// On success, `handle` must be non-zero and uniquely identify a live TLS
406 /// allocation accepted by [`Self::deallocate_tls`] until ownership moves.
407 fn allocate_kernel_tls() -> RuntimeHandleResult;
408
409 /// Releases a TLS area after its execution context has been destroyed.
410 ///
411 /// Ownership transfers exactly once after no context can reference the
412 /// allocation. Failure is a fatal runtime invariant.
413 fn deallocate_tls(tls: TlsHandle);
414
415 /// Creates a kernel execution context.
416 ///
417 /// On success, `handle` must be non-zero and uniquely identify a live
418 /// context accepted by [`Self::destroy_context`].
419 fn create_kernel_context(request: KernelContextRequest) -> RuntimeHandleResult;
420
421 /// Creates a user-capable execution context with a mandatory address space.
422 ///
423 /// On success, `handle` must follow the same ownership contract as
424 /// [`Self::create_kernel_context`].
425 fn create_user_context(request: UserContextRequest) -> RuntimeHandleResult;
426
427 /// Binds a created context to its final generation-bearing thread ID.
428 ///
429 /// The runtime must validate the context handle and install the association
430 /// atomically. A failed call must leave the context unbound so construction
431 /// can destroy it. This hook runs under the task registry's preempt-only
432 /// lock; it must not allocate, block, invoke callbacks, or re-enter
433 /// ax-task.
434 ///
435 /// Providers without execution contexts still export this capability and
436 /// return `Unsupported`, keeping trait-FFI symbol completeness explicit.
437 fn bind_context_thread(binding: ContextThreadBinding) -> RuntimeStatus;
438
439 /// Destroys an execution context that cannot be scheduled again.
440 ///
441 /// The registry makes a record reclaimable only after switch tail clears
442 /// physical CPU ownership. Destruction is therefore a single, infallible
443 /// ownership transfer; a provider must report an impossible live-context
444 /// state through its own fatal invariant path rather than return `Busy`.
445 fn destroy_context(context: ExecutionContextHandle);
446
447 /// Releases an address-space object after no CPU retains it as active mm.
448 ///
449 /// [`AddressSpaceDestroyOutcome::Active`] leaves the object live and
450 /// accepted by a later retry. This task-context operation may drop the OS
451 /// ownership lease; it is never invoked from the IRQ-off context-switch
452 /// path. The runtime must treat an invalid handle as a fatal provider
453 /// invariant rather than expose a compatibility status.
454 fn destroy_address_space(address_space: AddressSpaceHandle) -> AddressSpaceDestroyOutcome;
455
456 /// Arms one deferred retry after destruction observed an active CPU lease.
457 ///
458 /// The token is already queued in ax-task before this call. The runtime
459 /// returns [`AddressSpaceReclaimArmOutcome::Ready`] if no CPU lease remains,
460 /// or records an allocation-free notification obligation and returns
461 /// [`AddressSpaceReclaimArmOutcome::Armed`]. The CPU that drops the last
462 /// lease must then call [`crate::runtime::resource::notify_address_space_reclaim`]. An invalid
463 /// handle is a fatal provider invariant.
464 fn arm_address_space_reclaim(
465 address_space: AddressSpaceHandle,
466 ) -> AddressSpaceReclaimArmOutcome;
467
468 /// Loads the shared `mm` identity and membarrier registration state.
469 ///
470 /// This operation runs while rq locks or local IRQ exclusion may be held.
471 /// It must be a fixed, allocation-free atomic lookup and must not acquire
472 /// an OS lock or re-enter ax-task. An invalid handle is a fatal provider
473 /// invariant.
474 fn address_space_membarrier_state(
475 address_space: AddressSpaceHandle,
476 ) -> AddressSpaceMembarrierState;
477
478 /// Advances one irreversible per-`mm` membarrier registration phase.
479 ///
480 /// `Begin` publishes the requested bit before ax-task inspects runqueues;
481 /// `Complete` publishes the ready bit only after synchronous target-rq
482 /// refresh. The operation must be allocation-free and atomic.
483 fn update_address_space_membarrier_state(
484 address_space: AddressSpaceHandle,
485 registration: MembarrierRegistration,
486 phase: MembarrierRegistrationPhase,
487 ) -> AddressSpaceMembarrierState;
488
489 /// Executes one bounded membarrier action synchronously on `cpu`.
490 ///
491 /// Providers must not return success until the target callback completes.
492 /// The remote callback runs in hard-IRQ context and may only execute the
493 /// selected full barrier or the ax-task rq refresh entry; it must not
494 /// allocate, sleep, or invoke arbitrary OS callbacks.
495 fn synchronize_membarrier_cpu(
496 cpu: RuntimeCpuId,
497 action: RuntimeMembarrierAction,
498 ) -> RuntimeStatus;
499
500 /// Consumes one committed scheduler-switch transaction with local
501 /// interrupts disabled.
502 ///
503 /// # Safety
504 ///
505 /// Both execution-context handles and every non-empty address-space handle
506 /// in `plan` must identify live runtime objects. The caller must have
507 /// committed scheduler state and released runqueue locks. The provider
508 /// must validate and prepare both transitions before committing either one,
509 /// then consume the plan exactly once.
510 unsafe fn switch_context(plan: RuntimeSwitchPlan);
511
512 /// Flushes the current address space's local translation cache.
513 fn flush_tlb_local(start: usize, size: usize);
514
515 /// Captures a context-switch trace record without allocation or rq reentry.
516 ///
517 /// The outgoing rq lock may remain held. An optional returned notification
518 /// runs exactly once in incoming switch completion, after scheduler locks
519 /// and CPU-local borrows are released, with local IRQs still disabled.
520 fn trace_sched_switch(record: SchedSwitchRecord) -> Option<fn()>;
521
522 /// Writes directly to the runtime's emergency console without taking an
523 /// OS lock or re-entering the scheduler.
524 fn emergency_console_write(message: &str);
525
526 /// Reports an unrecoverable scheduler invariant and terminates execution.
527 fn fatal_invariant(code: u32, argument: usize) -> !;
528}