Skip to main content

ax_task/thread/
current.rs

1//! Operations bound to the calling scheduler thread.
2
3use alloc::sync::Arc;
4use core::marker::PhantomData;
5
6pub use crate::{
7    runtime::switch::dispatch::{
8        ExitPermit, commit_current_exit, exit_current_thread, prepare_current_exit,
9        yield_current_cpu,
10    },
11    sync::wait_queue::{sleep, sleep_until},
12    thread::{
13        current::park::{
14            CurrentParkDisposition, CurrentParkResume, CurrentParkStart, PreparedCurrentPark,
15            begin_current_park,
16        },
17        execution::exit_current,
18    },
19};
20use crate::{
21    runtime::{
22        context::{
23            RuntimeSchedulerFrameGuard, runtime_current_cpu_mut, runtime_task_system,
24            validate_schedule_context,
25        },
26        switch::{RuntimeScheduleOrigin, RuntimeSchedulerEntry, dispatch::execute_switch_plan},
27        task_runtime,
28    },
29    sched::CpuSet,
30    thread::{
31        CurrentThreadToken, TaskError, ThreadCore, ThreadExtensionLease, ThreadHandle, ThreadId,
32    },
33};
34
35/// Returns a strong handle for the calling scheduler thread.
36///
37/// # Errors
38///
39/// Returns [`TaskError::NotInitialized`] before runtime CPU publication,
40/// [`TaskError::CpuOwnerBorrowed`] for a reentrant owner query, or
41/// [`TaskError::NoRunnableThread`] before a current thread is installed.
42pub fn current_thread_handle() -> Result<ThreadHandle, TaskError> {
43    #[cfg(feature = "qperf-metrics")]
44    crate::diagnostics::counters::record_current_thread_handle_query();
45    let publication = current_thread_publication()?;
46    // SAFETY: the scheduler retains the executing task's owner-side Arc across
47    // preemption and migration until this synchronous operation returns.
48    unsafe { publication.acquire_handle() }
49}
50
51/// Returns the generation-bearing identity of the calling scheduler thread.
52#[inline(always)]
53pub fn current_thread_id() -> Result<ThreadId, TaskError> {
54    let identity = current_thread_identity()?;
55    Ok(ThreadId::from_parts(identity.slot, identity.generation))
56}
57
58/// Captures the scheduler thread executing this task context.
59#[inline(always)]
60pub fn current_thread_token() -> Result<CurrentThreadToken, TaskError> {
61    Ok(CurrentThreadToken::new(current_thread_id()?))
62}
63
64#[inline(always)]
65pub(crate) fn current_thread_identity()
66-> Result<crate::runtime::switch::ThreadIdentityV1, TaskError> {
67    let identity = task_runtime::current_thread_identity();
68    if identity.is_bound() {
69        return Ok(identity);
70    }
71
72    let publication = task_runtime::current_thread_publication();
73    if publication.identity() != identity || !publication.owner().is_none() {
74        return Err(TaskError::InvalidRuntimeHandle);
75    }
76    // Preserve the public distinction between a runtime that has not installed
77    // its task system and an initialized bootstrap context without a current
78    // scheduler thread. Bound task contexts never enter this cold path.
79    let _system = runtime_task_system()?;
80    Err(TaskError::NoRunnableThread)
81}
82
83pub(crate) fn current_thread_publication()
84-> Result<crate::runtime::switch::CurrentThreadPublication, TaskError> {
85    let publication = task_runtime::current_thread_publication();
86    let identity = publication.identity();
87    if !identity.is_bound() {
88        if !publication.owner().is_none() {
89            return Err(TaskError::InvalidRuntimeHandle);
90        }
91        // Preserve the public distinction between a runtime that has not
92        // installed its task system and an initialized bootstrap context that
93        // has not published a scheduler thread. This cold error path does not
94        // add a handle lookup to the bound-current fast path.
95        let _system = runtime_task_system()?;
96        return Err(TaskError::NoRunnableThread);
97    }
98    if publication.owner().is_none() {
99        return Err(TaskError::InvalidRuntimeHandle);
100    }
101    Ok(publication)
102}
103
104pub(crate) fn current_thread_core_arc() -> Result<Arc<ThreadCore>, TaskError> {
105    let publication = current_thread_publication()?;
106    // SAFETY: the runtime publication belongs to this architecture context.
107    // The returned Arc is scheduler-internal and remains in the synchronous
108    // current-thread operation; it does not acquire an external lease.
109    unsafe { publication.acquire_scheduler_core() }
110}
111
112/// Validates that the caller may publish a waiter or block its current thread.
113///
114/// Sleeping synchronization primitives should call this before changing any
115/// waiter, owner, donation, or thread-lifecycle state.
116pub fn validate_blocking_context() -> Result<(), TaskError> {
117    acquire_blocking_permit().map(|_| ())
118}
119
120/// RT-lock contention may schedule inside another RT critical section.
121pub(crate) fn validate_rt_lock_context() -> Result<(), TaskError> {
122    validate_schedule_context(RuntimeScheduleOrigin::Block)
123}
124
125pub(crate) fn validate_sleeping_lock_context() -> Result<(), TaskError> {
126    if crate::runtime::task_runtime::in_hard_irq() || current_thread_core_arc()?.holds_rt_lock() {
127        return Err(TaskError::UnsafeContext);
128    }
129    Ok(())
130}
131
132/// One validated opportunity to publish a blocking handshake.
133pub(crate) struct BlockingPermit {
134    _not_send: PhantomData<*mut ()>,
135}
136
137pub(crate) fn acquire_blocking_permit() -> Result<BlockingPermit, TaskError> {
138    validate_schedule_context(RuntimeScheduleOrigin::Block)?;
139    let current = current_thread_core_arc()?;
140    if current.holds_rt_lock() && !current.in_rt_lock_wait() {
141        return Err(TaskError::UnsafeContext);
142    }
143    Ok(BlockingPermit {
144        _not_send: PhantomData,
145    })
146}
147
148/// Returns the opaque extension of the calling scheduler thread.
149///
150/// Runtime entry trampolines use the callback-table address as a type identity
151/// before recovering an OS-owned closure or process object from `data`.
152pub fn current_thread_extension() -> Result<Option<ThreadExtensionLease>, TaskError> {
153    let handle = current_thread_handle()?;
154    Ok(handle
155        .extension_view()
156        .map(|view| ThreadExtensionLease::new(view, handle)))
157}
158
159/// Updates the calling thread's affinity and completes a required migration.
160///
161/// A successful return guarantees that the caller is executing on a CPU in
162/// the new mask. Generic remote-thread affinity updates remain asynchronous and
163/// are completed by the remote owner's next scheduler safe point.
164pub fn set_current_thread_affinity(affinity: CpuSet) -> Result<(), TaskError> {
165    let mut scheduler_frame = RuntimeSchedulerFrameGuard::enter(
166        RuntimeScheduleOrigin::Yield,
167        RuntimeSchedulerEntry::Task,
168    )?;
169    let current = scheduler_frame.current_thread_ref()?;
170    let system = scheduler_frame.task_system();
171    let mut outcome = {
172        let mut cpu = runtime_current_cpu_mut(&mut scheduler_frame)?;
173        let must_migrate = system.set_current_affinity(cpu.as_mut(), affinity)?;
174        if !must_migrate {
175            return Ok(());
176        }
177
178        // The new mask is now visible and excludes this CPU. Keep the scheduler
179        // baton and raw IRQ mask continuously owned until this context has moved;
180        // exposing an IRQ-enabled validation window here could let IRQ-return
181        // scheduling migrate the caller between publishing the mask and yielding.
182        // SAFETY: `scheduler_frame` owns the IRQ-off scheduler baton.
183        unsafe { system.yield_current_in_scheduler_frame(cpu.as_mut()) }.unwrap_or_else(|_| {
184            // Affinity publication cannot be rolled back safely after another CPU
185            // may have observed the migration target. Scheduler commit failures are
186            // therefore runtime invariants, like failures after exit publication.
187            task_runtime::fatal_invariant(0x4558_0021, current.id().as_u64() as usize);
188        })
189    };
190    let decision = outcome.decision_mut().unwrap_or_else(|| {
191        task_runtime::fatal_invariant(0x4558_0022, current.id().as_u64() as usize)
192    });
193    execute_switch_plan(&mut scheduler_frame, decision);
194    Ok(())
195}
196pub(crate) mod park;