1use core::{
2 mem::{offset_of, size_of},
3 pin::Pin,
4 ptr::NonNull,
5 sync::atomic::{AtomicUsize, Ordering},
6};
7
8use crate::{CpuAreaRef, ThreadSwitchError};
9
10#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
12#[repr(transparent)]
13pub struct CurrentContext(usize);
14
15impl CurrentContext {
16 pub const fn from_raw(raw: usize) -> Option<Self> {
18 if raw == 0 { None } else { Some(Self(raw)) }
19 }
20
21 pub const fn as_usize(self) -> usize {
23 self.0
24 }
25}
26
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
28pub(crate) struct CpuBindingEpoch(usize);
29
30#[derive(Clone, Copy, Debug)]
31pub(crate) struct CurrentCpuBinding {
32 pub(crate) area: CpuAreaRef,
33 pub(crate) epoch: CpuBindingEpoch,
34}
35
36const CPU_PHASE_MASK: usize = 0b11;
37const CPU_UNBOUND: usize = 0b00;
38const CPU_BINDING: usize = 0b01;
39const CPU_BOUND: usize = 0b10;
40const CPU_UNBINDING: usize = 0b11;
41
42const fn current_thread_reserved_size() -> usize {
43 64 - 5 * size_of::<usize>()
44}
45
46#[repr(C, align(64))]
52pub struct CurrentThreadHeader {
53 context: usize,
54 cpu_area: AtomicUsize,
55 binding_epoch: AtomicUsize,
56 architecture_state: [AtomicUsize; 2],
57 reserved: [u8; current_thread_reserved_size()],
58}
59
60impl CurrentThreadHeader {
61 pub const fn new(context: CurrentContext) -> Self {
63 Self {
64 context: context.0,
65 cpu_area: AtomicUsize::new(0),
66 binding_epoch: AtomicUsize::new(CPU_UNBOUND),
67 architecture_state: [const { AtomicUsize::new(0) }; 2],
68 reserved: [0; current_thread_reserved_size()],
69 }
70 }
71
72 pub(crate) const fn boot(area_base: usize) -> Self {
73 Self {
74 context: 0,
75 cpu_area: AtomicUsize::new(area_base),
76 binding_epoch: AtomicUsize::new(CPU_BOUND),
77 architecture_state: [const { AtomicUsize::new(0) }; 2],
78 reserved: [0; current_thread_reserved_size()],
79 }
80 }
81
82 pub const fn current_context(&self) -> Option<CurrentContext> {
84 CurrentContext::from_raw(self.context)
85 }
86
87 pub fn cpu_area(&self) -> Option<CpuAreaRef> {
89 self.cpu_binding().map(|binding| binding.area)
90 }
91
92 pub fn cpu_area_base(&self) -> Option<usize> {
94 self.cpu_binding().map(|binding| binding.area.base())
95 }
96
97 pub(crate) unsafe fn bind_cpu(
98 self: Pin<&Self>,
99 area: CpuAreaRef,
100 ) -> Result<CpuBindingEpoch, ThreadSwitchError> {
101 let this = self.get_ref();
102 let unbound = this.binding_epoch.load(Ordering::Acquire);
103 if unbound & CPU_PHASE_MASK != CPU_UNBOUND {
104 return Err(ThreadSwitchError::NextThreadAlreadyBound);
105 }
106 this.binding_epoch
107 .compare_exchange(
108 unbound,
109 unbound | CPU_BINDING,
110 Ordering::AcqRel,
111 Ordering::Acquire,
112 )
113 .map_err(|_| ThreadSwitchError::NextThreadAlreadyBound)?;
114 this.cpu_area.store(area.base(), Ordering::Relaxed);
115 let bound = (unbound & !CPU_PHASE_MASK) | CPU_BOUND;
116 this.binding_epoch.store(bound, Ordering::Release);
117 Ok(CpuBindingEpoch(bound))
118 }
119
120 pub(crate) unsafe fn unbind_cpu(
121 self: Pin<&Self>,
122 expected: CpuBindingEpoch,
123 ) -> Result<(), ThreadSwitchError> {
124 if expected.0 & CPU_PHASE_MASK != CPU_BOUND {
125 return Err(ThreadSwitchError::StalePreviousBinding);
126 }
127 let this = self.get_ref();
128 let unbinding = (expected.0 & !CPU_PHASE_MASK) | CPU_UNBINDING;
129 this.binding_epoch
130 .compare_exchange(expected.0, unbinding, Ordering::AcqRel, Ordering::Acquire)
131 .map_err(|_| ThreadSwitchError::StalePreviousBinding)?;
132 this.cpu_area.store(0, Ordering::Relaxed);
133 let next_unbound = (expected.0 & !CPU_PHASE_MASK).wrapping_add(4);
134 this.binding_epoch.store(next_unbound, Ordering::Release);
135 Ok(())
136 }
137
138 pub(crate) fn cpu_binding(&self) -> Option<CurrentCpuBinding> {
139 let (area_base, epoch) = self.raw_cpu_binding()?;
140 let area = unsafe { CpuAreaRef::from_initialized_base(area_base) }.ok()?;
143 Some(CurrentCpuBinding { area, epoch })
144 }
145
146 pub(crate) fn raw_cpu_binding(&self) -> Option<(usize, CpuBindingEpoch)> {
147 loop {
148 let before = self.binding_epoch.load(Ordering::Acquire);
149 if before & CPU_PHASE_MASK != CPU_BOUND {
150 return None;
151 }
152 let area_base = self.cpu_area.load(Ordering::Relaxed);
153 let after = self.binding_epoch.load(Ordering::Acquire);
154 if before == after {
155 return Some((area_base, CpuBindingEpoch(after)));
156 }
157 core::hint::spin_loop();
158 }
159 }
160
161 pub fn as_non_null(self: Pin<&Self>) -> NonNull<Self> {
163 NonNull::from(self.get_ref())
164 }
165}
166
167pub const CURRENT_THREAD_CPU_BASE_OFFSET: usize = offset_of!(CurrentThreadHeader, cpu_area);
169pub const CURRENT_THREAD_ARCH_STATE_OFFSET: usize =
171 offset_of!(CurrentThreadHeader, architecture_state);
172pub const CURRENT_THREAD_ARCH_STATE_SIZE: usize = 2 * size_of::<usize>();
174
175const _: () = {
176 assert!(size_of::<CurrentThreadHeader>() == 64);
177 assert!(core::mem::align_of::<CurrentThreadHeader>() == 64);
178};