Skip to main content

ax_task/thread/handle/
mod.rs

1//! Strong, weak, and direct IRQ-wake handles.
2
3use alloc::sync::{Arc, Weak};
4#[cfg(feature = "lockdep")]
5use core::cell::UnsafeCell;
6use core::{
7    marker::PhantomData,
8    mem::ManuallyDrop,
9    sync::atomic::{AtomicBool, AtomicPtr, AtomicU8, AtomicU32, AtomicU64, AtomicUsize, Ordering},
10};
11
12mod wake_batch;
13pub use wake_batch::ThreadWakeBatch;
14
15use crate::{
16    runtime::{
17        cpu::PreemptGuardToken,
18        delivery::{
19            inbox::{InboxKind, InboxNode},
20            work::TaskWorkDoorbell,
21        },
22        resource::AddressSpaceMembarrierId,
23        service::SchedulerTickCpuTime,
24        task_runtime,
25    },
26    sched::{
27        CpuId, DeadlineFlags, DeadlinePolicy, FairMode, Nice, RtPriority, SchedulePolicy,
28        algorithm::RunQueueNodeStorage, system::ThreadSchedCell,
29    },
30    thread::{
31        ParkPublication, PiWaitNodeStorage, PiWaitState, SchedulerTickWork, SchedulerTickWorkClaim,
32        SchedulingKey, SchedulingUrgency, TaskError, ThreadAffinityCompletion, ThreadExtensionView,
33        ThreadId, ThreadLifecycle, ThreadState, WakePublication,
34    },
35    time::queue::TaskDeadlineNode,
36};
37
38const REAP_CLAIMED: usize = 1 << (usize::BITS - 1);
39const REAP_MAX_UPGRADE_READERS: usize = REAP_CLAIMED - 1;
40const SCHEDULER_ACTIVITY_CLOSED: usize = 1 << (usize::BITS - 1);
41const SCHEDULER_ACTIVITY_MAX_READERS: usize = SCHEDULER_ACTIVITY_CLOSED - 1;
42
43#[cfg(feature = "lockdep")]
44struct ThreadHeldLocks {
45    stack: UnsafeCell<crate::sync::lockdep::HeldLockStack>,
46}
47
48#[cfg(feature = "lockdep")]
49impl ThreadHeldLocks {
50    const fn new() -> Self {
51        Self {
52            stack: UnsafeCell::new(crate::sync::lockdep::HeldLockStack::new()),
53        }
54    }
55
56    unsafe fn with_mut<R>(
57        &self,
58        operation: impl FnOnce(&mut crate::sync::lockdep::HeldLockStack) -> R,
59    ) -> R {
60        // SAFETY: the caller owns the current-task and migration-exclusion
61        // contract documented on `ThreadCore::with_held_locks`.
62        unsafe { operation(&mut *self.stack.get()) }
63    }
64}
65
66#[cfg(feature = "lockdep")]
67impl core::fmt::Debug for ThreadHeldLocks {
68    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69        formatter.write_str("ThreadHeldLocks(..)")
70    }
71}
72
73// SAFETY: the stack is accessed only by its currently executing task while
74// local IRQ exclusion prevents migration and scheduler replacement. Other
75// threads may retain `ThreadCore` references but cannot access this field.
76#[cfg(feature = "lockdep")]
77unsafe impl Sync for ThreadHeldLocks {}
78
79/// A strong reference used to inspect and control a live thread.
80#[derive(Debug)]
81pub struct ThreadHandle {
82    pub(crate) core: ManuallyDrop<Arc<ThreadCore>>,
83    reap_signal: Arc<ThreadReapSignal>,
84}
85
86impl Drop for ThreadHandle {
87    fn drop(&mut self) {
88        unsafe {
89            // SAFETY: `core` is wrapped solely so this destructor can release
90            // the strong count before publishing the reaper retry. It is
91            // dropped exactly once here and never accessed afterwards.
92            ManuallyDrop::drop(&mut self.core);
93        }
94        self.reap_signal.release_external_lease();
95    }
96}
97
98impl Clone for ThreadHandle {
99    fn clone(&self) -> Self {
100        let core = Arc::clone(&self.core);
101        let reap_signal = Arc::clone(&self.reap_signal);
102        reap_signal.acquire_external_lease();
103        Self {
104            core: ManuallyDrop::new(core),
105            reap_signal,
106        }
107    }
108}
109
110impl ThreadHandle {
111    pub(crate) fn from_core(core: Arc<ThreadCore>) -> Self {
112        let reap_signal = Arc::clone(&core.reap_signal);
113        reap_signal.acquire_external_lease();
114        Self {
115            core: ManuallyDrop::new(core),
116            reap_signal,
117        }
118    }
119
120    /// Returns the immutable runtime publication for this live scheduler
121    /// thread.
122    #[doc(hidden)]
123    pub fn runtime_publication(&self) -> crate::runtime::switch::CurrentThreadPublication {
124        crate::runtime::switch::CurrentThreadPublication::from_core(self.id(), &self.core)
125    }
126
127    pub(crate) fn runtime_core_arc(&self) -> &Arc<ThreadCore> {
128        &self.core
129    }
130
131    /// Returns the generation-checked registry identity.
132    pub fn id(&self) -> ThreadId {
133        self.core.id
134    }
135
136    /// Returns the thread's base scheduling policy.
137    pub fn base_policy(&self) -> SchedulePolicy {
138        self.core.base_policy.load()
139    }
140
141    /// Returns the policy after priority-inheritance donation is applied.
142    pub fn effective_policy(&self) -> SchedulePolicy {
143        self.core.effective_policy.load()
144    }
145
146    /// Reports physical execution-resource reclamation independently of logical exit.
147    pub fn execution_reclaimed(&self) -> bool {
148        self.core.execution_reclaimed.load(Ordering::Acquire)
149    }
150
151    /// Returns the most recently published lifecycle state.
152    pub fn state(&self) -> ThreadState {
153        self.core.state()
154    }
155
156    /// Creates a non-owning lifecycle observer.
157    pub fn downgrade(&self) -> WeakThreadHandle {
158        WeakThreadHandle {
159            core: Arc::downgrade(&self.core),
160        }
161    }
162
163    /// Creates a direct wake handle that does not consult the thread registry.
164    pub fn wake_handle(&self) -> ThreadWakeHandle {
165        ThreadWakeHandle::from_core(Arc::clone(&self.core))
166    }
167
168    /// Transfers this handle into a direct wake handle without cloning it.
169    ///
170    /// The returned handle retains the same thread and external-lifetime
171    /// lease. Conversion does not change thread state or publish a wake.
172    pub fn into_wake_handle(self) -> ThreadWakeHandle {
173        let handle = ManuallyDrop::new(self);
174        // SAFETY: the consumed handle cannot run Drop or be used again. Move
175        // each owning field exactly once; the wake handle assumes the same
176        // core-before-lease release order without changing either Arc count
177        // or the external lease count.
178        unsafe {
179            ThreadWakeHandle {
180                core: core::ptr::read(&handle.core),
181                reap_signal: core::ptr::read(&handle.reap_signal),
182            }
183        }
184    }
185
186    /// Returns the physical CPU that must cross a scheduler boundary.
187    ///
188    /// Unlike direct wake placement, this snapshot remains on the source CPU
189    /// until switch tail releases the outgoing context. A task-context caller
190    /// can therefore publish state, take this snapshot, and rendezvous with the
191    /// returned CPU; either the thread is still active there or it crossed a
192    /// scheduler boundary after the publication.
193    pub fn scheduler_fence_cpu(&self) -> Option<CpuId> {
194        self.core.sched().scheduler_fence_cpu()
195    }
196
197    /// Returns Linux `task_cpu()`: the last committed runqueue assignment.
198    ///
199    /// This remains the previous CPU while a task sleeps and changes to the
200    /// destination when an rq-to-rq migration commits. Physical execution
201    /// ownership is exposed separately by [`Self::scheduler_fence_cpu`].
202    /// A wake-placement hint is never reported as scheduler placement.
203    pub fn assigned_cpu(&self) -> Option<CpuId> {
204        self.core.assigned_cpu()
205    }
206
207    pub(crate) fn extension_view(&self) -> Option<crate::thread::ThreadExtensionView> {
208        self.core.extension_view()
209    }
210}
211
212impl Eq for ThreadHandle {}
213
214impl PartialEq for ThreadHandle {
215    fn eq(&self, other: &Self) -> bool {
216        self.id() == other.id()
217    }
218}
219
220/// A non-owning thread observer for ordinary task context.
221#[derive(Clone, Debug)]
222pub struct WeakThreadHandle {
223    core: Weak<ThreadCore>,
224}
225
226impl WeakThreadHandle {
227    /// Attempts to acquire a strong reference while the thread header is alive.
228    pub fn upgrade(&self) -> Option<ThreadHandle> {
229        let core = self.core.upgrade()?;
230        if !core.try_enter_weak_upgrade() {
231            return None;
232        }
233        let handle = ThreadHandle::from_core(core);
234        handle.core.exit_weak_upgrade();
235        Some(handle)
236    }
237}
238
239/// A stable direct wake header reference.
240///
241/// [`Self::wake`] uses non-sleeping scheduler transactions and is safe in hard
242/// IRQ context. Creating, cloning, and dropping this owning reference are task-context
243/// operations. A coroutine whose last raw-waker reference is released in hard IRQ
244/// defers only that zero-reference allocation to the typed task-system reaper.
245#[derive(Debug)]
246pub struct ThreadWakeHandle {
247    pub(crate) core: ManuallyDrop<Arc<ThreadCore>>,
248    reap_signal: Arc<ThreadReapSignal>,
249}
250
251impl Drop for ThreadWakeHandle {
252    fn drop(&mut self) {
253        unsafe {
254            // SAFETY: identical ownership rule to ThreadHandle::drop above.
255            ManuallyDrop::drop(&mut self.core);
256        }
257        self.reap_signal.release_external_lease();
258    }
259}
260
261impl Clone for ThreadWakeHandle {
262    fn clone(&self) -> Self {
263        let core = Arc::clone(&self.core);
264        let reap_signal = Arc::clone(&self.reap_signal);
265        reap_signal.acquire_external_lease();
266        Self {
267            core: ManuallyDrop::new(core),
268            reap_signal,
269        }
270    }
271}
272
273impl ThreadWakeHandle {
274    pub(crate) fn from_core(core: Arc<ThreadCore>) -> Self {
275        let reap_signal = Arc::clone(&core.reap_signal);
276        reap_signal.acquire_external_lease();
277        Self {
278            core: ManuallyDrop::new(core),
279            reap_signal,
280        }
281    }
282
283    /// Directly wakes the thread without allocating, sleeping, or invoking callbacks.
284    ///
285    /// This IRQ-safe operation may acquire the thread scheduler lock and the
286    /// selected CPU's raw runqueue lock.
287    pub fn wake(&self) -> WakeResult {
288        self.core.wake(WakeIntent::Normal)
289    }
290
291    /// Wakes from task context when the caller expects to block shortly.
292    ///
293    /// This is Linux's `WF_SYNC` contract. It remains a scheduling hint: the
294    /// waker commits the destination runqueue activation before returning.
295    pub fn wake_sync(&self) -> WakeResult {
296        debug_assert!(!crate::runtime::task_runtime::in_hard_irq());
297        self.core.wake(WakeIntent::Sync)
298    }
299
300    pub(crate) fn deliver_wait_claim_from_task(
301        &self,
302        claim: &crate::thread::WaitWakeClaim,
303        intent: WakeIntent,
304    ) -> crate::thread::WaitWakeDelivery {
305        crate::runtime::context::wake_wait_claim_from_task(&self.core, claim, intent)
306    }
307
308    /// Returns the thread that owns this wake header.
309    pub fn thread_id(&self) -> ThreadId {
310        self.core.id
311    }
312}
313
314impl ThreadCore {
315    fn wake(self: &Arc<Self>, intent: WakeIntent) -> WakeResult {
316        crate::runtime::context::wake_thread_from_current_cpu(self, intent)
317    }
318}
319
320#[derive(Clone, Copy, Debug, Eq, PartialEq)]
321pub(crate) enum WakeIntent {
322    Normal,
323    Sync,
324}
325
326impl WakeIntent {
327    pub(crate) const fn is_sync(self) -> bool {
328        matches!(self, Self::Sync)
329    }
330}
331
332/// Result of an IRQ-safe wake publication.
333#[derive(Clone, Copy, Debug, Eq, PartialEq)]
334pub enum WakeResult {
335    /// This call completed a logical wake transaction. The thread is runnable,
336    /// retained a park notification, or has owner-CPU activation committed.
337    Notified,
338    /// An unresolved park-transition notification already represents this event.
339    AlreadyPending,
340    /// The destination thread has exited, so the late wake is ignored.
341    Exited,
342    /// No scheduler-ready CPU is currently reachable for wake delivery.
343    Unavailable,
344}
345
346/// Runqueue-coherent snapshot of one thread's charged CPU runtime.
347#[derive(Clone, Copy, Debug, Eq, PartialEq)]
348pub struct ThreadRuntimeSnapshot {
349    charged_runtime_ns: u64,
350    running: bool,
351}
352
353impl ThreadRuntimeSnapshot {
354    /// Returns cumulative CPU runtime, including the current running residual.
355    pub const fn charged_runtime_ns(self) -> u64 {
356        self.charged_runtime_ns
357    }
358
359    /// Returns whether the snapshot included a live running residual.
360    pub const fn is_running(self) -> bool {
361        self.running
362    }
363}
364
365#[derive(Debug)]
366struct ThreadReapSignal {
367    exited: AtomicBool,
368    external_leases: AtomicUsize,
369    task_work: Option<Arc<TaskWorkDoorbell>>,
370}
371
372#[must_use = "the scheduler activity guard serializes owner delivery against exit"]
373pub(crate) struct ThreadSchedulerActivity<'thread> {
374    core: &'thread ThreadCore,
375    preempt: PreemptGuardToken,
376    _not_send: PhantomData<*mut ()>,
377}
378
379impl Drop for ThreadSchedulerActivity<'_> {
380    fn drop(&mut self) {
381        self.core.finish_scheduler_activity();
382        release_scheduler_preempt(self.preempt);
383    }
384}
385
386#[must_use = "the owned scheduler exit guard closes new activity until exit commits"]
387pub(crate) struct OwnedThreadSchedulerExit {
388    core: Arc<ThreadCore>,
389    preempt: PreemptGuardToken,
390    sealed: bool,
391    _not_send: PhantomData<*mut ()>,
392}
393
394impl OwnedThreadSchedulerExit {
395    pub(crate) fn seal(&mut self) {
396        self.sealed = true;
397    }
398}
399
400impl Drop for OwnedThreadSchedulerExit {
401    fn drop(&mut self) {
402        if !self.sealed {
403            self.core.reopen_scheduler_activity();
404        }
405        release_scheduler_preempt(self.preempt);
406    }
407}
408
409fn release_scheduler_preempt(token: PreemptGuardToken) {
410    if token.is_none() {
411        return;
412    }
413    // SAFETY: scheduler activity and exit guards are !Send and consume the
414    // exact token returned on this execution context after publishing their
415    // final gate state.
416    unsafe { task_runtime::preempt_guard_exit(token) };
417}
418
419#[must_use = "dropping the delivery lease makes an exited thread reapable"]
420pub(crate) struct ThreadSchedulerInboxDelivery<'thread> {
421    core: &'thread ThreadCore,
422}
423
424impl Drop for ThreadSchedulerInboxDelivery<'_> {
425    fn drop(&mut self) {
426        self.core.finish_scheduler_inbox_delivery();
427    }
428}
429
430impl ThreadReapSignal {
431    fn new(task_work: Option<Arc<TaskWorkDoorbell>>) -> Self {
432        Self {
433            exited: AtomicBool::new(false),
434            external_leases: AtomicUsize::new(0),
435            task_work,
436        }
437    }
438
439    fn mark_exited(&self) {
440        self.exited.store(true, Ordering::Release);
441    }
442
443    fn publish(&self) {
444        if let Some(task_work) = &self.task_work {
445            task_work.publish();
446        }
447    }
448
449    fn acquire_external_lease(&self) {
450        self.external_leases
451            .try_update(Ordering::AcqRel, Ordering::Acquire, |leases| {
452                leases.checked_add(1)
453            })
454            .expect("thread external-lifetime lease count overflow");
455    }
456
457    fn release_external_lease(&self) {
458        let previous = self.external_leases.fetch_sub(1, Ordering::AcqRel);
459        assert!(previous != 0, "unbalanced thread external-lifetime lease");
460        if previous == 1 && self.exited.load(Ordering::Acquire) {
461            self.publish();
462        }
463    }
464
465    fn external_lease_count(&self) -> usize {
466        self.external_leases.load(Ordering::Acquire)
467    }
468}
469
470#[derive(Debug)]
471pub(crate) struct ThreadCore {
472    id: ThreadId,
473    sched: Arc<ThreadSchedCell>,
474    // Stable `mm` identity. Registration bits remain rq-owned and are
475    // refreshed explicitly by the membarrier synchronization protocol.
476    membarrier_identity: AtomicUsize,
477    runqueue_nodes: RunQueueNodeStorage,
478    pi_wait_nodes: PiWaitNodeStorage,
479    // Immutable after publication. Every handle retaining this copy also pins
480    // the registry-owned extension destructor through the reaper Arc contract.
481    extension: Option<ThreadExtensionView>,
482    pub(crate) execution: Option<Arc<crate::thread::execution::ThreadExecution>>,
483    pub(crate) execution_reclaimed: AtomicBool,
484    scheduler_tick_cpu_time: Option<Arc<SchedulerTickCpuTime>>,
485    scheduler_tick_work: Option<SchedulerTickWork>,
486    scheduler_tick_work_generation: AtomicU64,
487    scheduler_tick_observed_ns: AtomicU64,
488    scheduler_tick_work_node: InboxNode,
489    deadline_callback_node: InboxNode,
490    base_policy: AtomicPolicy,
491    effective_policy: AtomicPolicy,
492    effective_key_sequence: AtomicUsize,
493    effective_deadline_active: AtomicBool,
494    effective_deadline_ns: AtomicU64,
495    state: Arc<ThreadLifecycle>,
496    reap_signal: Arc<ThreadReapSignal>,
497    reap_gate: AtomicUsize,
498    scheduler_activity_gate: AtomicUsize,
499    scheduler_inbox_deliveries: AtomicUsize,
500    pub(super) affinity_completion: ThreadAffinityCompletion,
501    park_generation: AtomicU64,
502    park_sequence: AtomicU64,
503    rt_lock_depth: AtomicUsize,
504    ordinary_park_generation: AtomicU64,
505    wake_cpu_hint: AtomicU32,
506    wake_affinity: WakeAffinityState,
507    affinity_update_node: InboxNode,
508    deadline_refresh_node: InboxNode,
509    wake_batch_next: AtomicPtr<ThreadCore>,
510    wake_batch_linked: AtomicBool,
511    sleep_timer: TaskDeadlineNode,
512    deadline_cbs_timer: TaskDeadlineNode,
513    deadline_zero_lag_timer: TaskDeadlineNode,
514    sleep_timer_cpu: AtomicU32,
515    sleep_timer_generation: AtomicU64,
516    migration_node: InboxNode,
517    committed_runtime_ns: AtomicU64,
518    #[cfg(feature = "lockdep")]
519    held_locks: ThreadHeldLocks,
520    pi_wait_state: PiWaitState,
521}
522
523pub(crate) struct ThreadCoreInit {
524    pub(crate) id: ThreadId,
525    pub(crate) policy: SchedulePolicy,
526    pub(crate) sched: Arc<ThreadSchedCell>,
527    pub(crate) extension: Option<ThreadExtensionView>,
528    pub(crate) execution: Option<Arc<crate::thread::execution::ThreadExecution>>,
529    pub(crate) scheduler_tick_cpu_time: Option<Arc<SchedulerTickCpuTime>>,
530    pub(crate) scheduler_tick_work: Option<SchedulerTickWork>,
531    pub(crate) membarrier_identity: AddressSpaceMembarrierId,
532    pub(crate) task_work: Option<Arc<TaskWorkDoorbell>>,
533}
534
535impl ThreadCore {
536    pub(crate) fn new(init: ThreadCoreInit) -> Result<Self, TaskError> {
537        let ThreadCoreInit {
538            id,
539            policy,
540            sched,
541            extension,
542            execution,
543            scheduler_tick_cpu_time,
544            scheduler_tick_work,
545            membarrier_identity,
546            task_work,
547        } = init;
548        debug_assert_eq!(id, sched.id());
549        let lifecycle = Arc::clone(sched.lifecycle());
550        let reap_signal = crate::thread::allocation::try_arc(ThreadReapSignal::new(task_work))?;
551        Ok(Self {
552            id,
553            sched,
554            membarrier_identity: AtomicUsize::new(membarrier_identity.into_raw()),
555            runqueue_nodes: RunQueueNodeStorage::new()?,
556            pi_wait_nodes: PiWaitNodeStorage::new()?,
557            extension,
558            execution,
559            execution_reclaimed: AtomicBool::new(false),
560            scheduler_tick_cpu_time,
561            scheduler_tick_work,
562            scheduler_tick_work_generation: AtomicU64::new(0),
563            scheduler_tick_observed_ns: AtomicU64::new(0),
564            scheduler_tick_work_node: InboxNode::new(InboxKind::TaskWork),
565            deadline_callback_node: InboxNode::new(InboxKind::TaskWork),
566            base_policy: AtomicPolicy::new(policy),
567            effective_policy: AtomicPolicy::new(policy),
568            effective_key_sequence: AtomicUsize::new(0),
569            effective_deadline_active: AtomicBool::new(false),
570            effective_deadline_ns: AtomicU64::new(0),
571            state: lifecycle,
572            reap_signal,
573            reap_gate: AtomicUsize::new(0),
574            scheduler_activity_gate: AtomicUsize::new(0),
575            scheduler_inbox_deliveries: AtomicUsize::new(0),
576            affinity_completion: ThreadAffinityCompletion::new(1),
577            park_generation: AtomicU64::new(0),
578            park_sequence: AtomicU64::new(0),
579            rt_lock_depth: AtomicUsize::new(0),
580            ordinary_park_generation: AtomicU64::new(0),
581            wake_cpu_hint: AtomicU32::new(u32::MAX),
582            wake_affinity: WakeAffinityState::new(),
583            affinity_update_node: InboxNode::new(InboxKind::OwnerControl),
584            deadline_refresh_node: InboxNode::new(InboxKind::OwnerControl),
585            wake_batch_next: AtomicPtr::new(core::ptr::null_mut()),
586            wake_batch_linked: AtomicBool::new(false),
587            sleep_timer: TaskDeadlineNode::for_thread(id),
588            deadline_cbs_timer: TaskDeadlineNode::deadline_cbs_for_thread(id),
589            deadline_zero_lag_timer: TaskDeadlineNode::deadline_zero_lag_for_thread(id),
590            sleep_timer_cpu: AtomicU32::new(u32::MAX),
591            sleep_timer_generation: AtomicU64::new(0),
592            migration_node: InboxNode::new(InboxKind::OwnerControl),
593            committed_runtime_ns: AtomicU64::new(0),
594            #[cfg(feature = "lockdep")]
595            held_locks: ThreadHeldLocks::new(),
596            pi_wait_state: PiWaitState::new(),
597        })
598    }
599
600    pub(crate) const fn runqueue_nodes(&self) -> &RunQueueNodeStorage {
601        &self.runqueue_nodes
602    }
603
604    pub(crate) const fn pi_wait_nodes(&self) -> &PiWaitNodeStorage {
605        &self.pi_wait_nodes
606    }
607
608    pub(crate) fn membarrier_identity(&self) -> AddressSpaceMembarrierId {
609        let raw = self.membarrier_identity.load(Ordering::Acquire);
610        // SAFETY: the thread's runtime resources keep the corresponding
611        // address-space generation alive until this value is replaced inside
612        // the owner-rq address-space transition.
613        unsafe { AddressSpaceMembarrierId::from_raw(raw) }
614    }
615
616    pub(crate) fn publish_membarrier_identity(&self, identity: AddressSpaceMembarrierId) {
617        self.membarrier_identity
618            .store(identity.into_raw(), Ordering::Release);
619    }
620
621    /// Mutates lockdep state owned by this currently executing thread.
622    ///
623    /// # Safety
624    ///
625    /// The caller must prove that this is the current thread and prevent local
626    /// IRQ entry, migration, and scheduler replacement for the complete call.
627    #[cfg(feature = "lockdep")]
628    pub(crate) unsafe fn with_held_locks<R>(
629        &self,
630        operation: impl FnOnce(&mut crate::sync::lockdep::HeldLockStack) -> R,
631    ) -> R {
632        unsafe { self.held_locks.with_mut(operation) }
633    }
634}
635
636mod lifecycle;
637mod policy;
638mod runtime_accounting;
639mod wake_affinity;
640mod wake_state;
641
642use policy::AtomicPolicy;
643use wake_affinity::WakeAffinityState;
644
645mod control;