1use core::sync::atomic::{AtomicUsize, Ordering};
2
3#[derive(Debug)]
5#[repr(C, align(4096))]
6pub struct SigProcControl {
7 pub pending: AtomicU64,
8 pub actions: [RawAction; 64],
9 pub sender_infos: [AtomicU64; 32],
10 }
13#[derive(Debug, Default)]
19#[repr(C, align(16))]
20pub struct RawAction {
21 pub first: AtomicU64,
25 pub user_data: AtomicU64,
28}
29
30#[derive(Debug, Default)]
32#[repr(C)]
33pub struct Sigcontrol {
34 pub word: [AtomicU64; 2],
36
37 pub sender_infos: [AtomicU64; 32],
39
40 pub control_flags: SigatomicUsize,
41
42 pub saved_ip: NonatomicUsize, pub saved_archdep_reg: NonatomicUsize, pub saved_excp_code: NonatomicUsize, pub saved_excp_addr: NonatomicUsize, }
47#[derive(Clone, Copy, Debug)]
48pub struct SenderInfo {
49 pub pid: u32,
50 pub ruid: u32,
51}
52impl SenderInfo {
53 #[inline]
54 pub fn raw(self) -> u64 {
55 u64::from(self.pid) | (u64::from(self.ruid) << 32)
56 }
57 #[inline]
58 pub const fn from_raw(raw: u64) -> Self {
59 Self {
60 pid: raw as u32,
61 ruid: (raw >> 32) as u32,
62 }
63 }
64}
65
66impl Sigcontrol {
67 pub fn currently_pending_unblocked(&self, proc: &SigProcControl) -> u64 {
68 let proc_pending = proc.pending.load(Ordering::Relaxed);
69 let [w0, w1] = core::array::from_fn(|i| {
70 let w = self.word[i].load(Ordering::Relaxed);
71 ((w | (proc_pending >> (i * 32))) & 0xffff_ffff) & (w >> 32)
72 });
73 w0 | (w1 << 32)
75 }
76 pub fn set_allowset(&self, new_allowset: u64) -> u64 {
77 let [w0, w1] = self.word.each_ref().map(|w| w.load(Ordering::Relaxed));
79 let old_a0 = w0 & 0xffff_ffff_0000_0000;
80 let old_a1 = w1 & 0xffff_ffff_0000_0000;
81 let new_a0 = (new_allowset & 0xffff_ffff) << 32;
82 let new_a1 = new_allowset & 0xffff_ffff_0000_0000;
83
84 let prev_w0 = self.word[0].fetch_add(new_a0.wrapping_sub(old_a0), Ordering::Relaxed);
85 let prev_w1 = self.word[1].fetch_add(new_a1.wrapping_sub(old_a1), Ordering::Relaxed);
86 let up0 = prev_w0 & (prev_w0 >> 32);
88 let up1 = prev_w1 & (prev_w1 >> 32);
89
90 up0 | (up1 << 32)
91 }
92}
93
94#[derive(Debug, Default)]
95#[repr(transparent)]
96pub struct SigatomicUsize(AtomicUsize);
97
98impl SigatomicUsize {
99 #[inline]
100 pub fn load(&self, ordering: Ordering) -> usize {
101 let value = self.0.load(Ordering::Relaxed);
102 if ordering != Ordering::Relaxed {
103 core::sync::atomic::compiler_fence(ordering);
104 }
105 value
106 }
107 #[inline]
108 pub fn store(&self, value: usize, ordering: Ordering) {
109 if ordering != Ordering::Relaxed {
110 core::sync::atomic::compiler_fence(ordering);
111 }
112 self.0.store(value, Ordering::Relaxed);
113 }
114}
115#[derive(Debug, Default)]
116#[repr(transparent)]
117pub struct NonatomicUsize(AtomicUsize);
118
119impl NonatomicUsize {
120 #[inline]
121 pub const fn new(a: usize) -> Self {
122 Self(AtomicUsize::new(a))
123 }
124
125 #[inline]
126 pub fn get(&self) -> usize {
127 self.0.load(Ordering::Relaxed)
128 }
129 #[inline]
130 pub fn set(&self, value: usize) {
131 self.0.store(value, Ordering::Relaxed);
132 }
133}
134
135pub fn sig_bit(sig: usize) -> u64 {
136 1 << (sig - 1)
137}
138
139impl SigProcControl {
141 pub fn signal_will_ign(&self, sig: usize, stop_or_continue: bool) -> bool {
150 let flags = self.actions[sig - 1].first.load(Ordering::Relaxed);
151 let will_ign = flags & (1 << 63) != 0;
152 let sig_specific = flags & (1 << 62) != 0; will_ign || (sig == SIGCHLD && stop_or_continue && sig_specific)
155 }
156 pub fn signal_will_stop(&self, sig: usize) -> bool {
158 use crate::flag::*;
159 matches!(sig, SIGTSTP | SIGTTIN | SIGTTOU)
160 && self.actions[sig - 1].first.load(Ordering::Relaxed) & (1 << 62) != 0
161 }
162}
163
164#[cfg(not(target_arch = "x86"))]
165pub use core::sync::atomic::AtomicU64;
166
167use crate::SIGCHLD;
168
169#[cfg(target_arch = "x86")]
170pub use self::atomic::AtomicU64;
171
172#[cfg(target_arch = "x86")]
173mod atomic {
174 use core::{cell::UnsafeCell, sync::atomic::Ordering};
175
176 #[derive(Debug, Default)]
177 pub struct AtomicU64(UnsafeCell<u64>);
178
179 unsafe impl Send for AtomicU64 {}
180 unsafe impl Sync for AtomicU64 {}
181
182 impl AtomicU64 {
183 pub const fn new(inner: u64) -> Self {
184 Self(UnsafeCell::new(inner))
185 }
186 pub fn compare_exchange(
187 &self,
188 old: u64,
189 new: u64,
190 _success: Ordering,
191 _failure: Ordering,
192 ) -> Result<u64, u64> {
193 let old_hi = (old >> 32) as u32;
194 let old_lo = old as u32;
195 let new_hi = (new >> 32) as u32;
196 let new_lo = new as u32;
197 let mut out_hi;
198 let mut out_lo;
199
200 unsafe {
201 core::arch::asm!("lock cmpxchg8b [{}]", in(reg) self.0.get(), inout("edx") old_hi => out_hi, inout("eax") old_lo => out_lo, in("ecx") new_hi, in("ebx") new_lo);
202 }
203
204 if old_hi == out_hi && old_lo == out_lo {
205 Ok(old)
206 } else {
207 Err(u64::from(out_lo) | (u64::from(out_hi) << 32))
208 }
209 }
210 pub fn load(&self, ordering: Ordering) -> u64 {
211 match self.compare_exchange(0, 0, ordering, ordering) {
212 Ok(new) => new,
213 Err(new) => new,
214 }
215 }
216 pub fn store(&self, new: u64, ordering: Ordering) {
217 let mut old = 0;
218
219 loop {
220 match self.compare_exchange(old, new, ordering, Ordering::Relaxed) {
221 Ok(_) => break,
222 Err(new) => {
223 old = new;
224 core::hint::spin_loop();
225 }
226 }
227 }
228 }
229 pub fn fetch_update(
230 &self,
231 set_order: Ordering,
232 fetch_order: Ordering,
233 mut f: impl FnMut(u64) -> Option<u64>,
234 ) -> Result<u64, u64> {
235 let mut old = self.load(fetch_order);
236
237 loop {
238 let new = f(old).ok_or(old)?;
239 match self.compare_exchange(old, new, set_order, Ordering::Relaxed) {
240 Ok(_) => return Ok(new),
241 Err(changed) => {
242 old = changed;
243 core::hint::spin_loop();
244 }
245 }
246 }
247 }
248 pub fn fetch_or(&self, bits: u64, order: Ordering) -> u64 {
249 self.fetch_update(order, Ordering::Relaxed, |b| Some(b | bits))
250 .unwrap()
251 }
252 pub fn fetch_and(&self, bits: u64, order: Ordering) -> u64 {
253 self.fetch_update(order, Ordering::Relaxed, |b| Some(b & bits))
254 .unwrap()
255 }
256 pub fn fetch_add(&self, term: u64, order: Ordering) -> u64 {
257 self.fetch_update(order, Ordering::Relaxed, |b| Some(b.wrapping_add(term)))
258 .unwrap()
259 }
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use std::sync::{
266 atomic::{AtomicU64, Ordering},
267 Arc,
268 };
269
270 #[cfg(not(loom))]
271 use std::{sync::Mutex, thread};
272 #[cfg(not(loom))]
273 fn model(f: impl FnOnce()) {
274 f()
275 }
276
277 #[cfg(loom)]
278 use loom::{model, sync::Mutex, thread};
279
280 use crate::{RawAction, SigProcControl, Sigcontrol};
281
282 struct FakeThread {
283 ctl: Sigcontrol,
284 pctl: SigProcControl,
285 ctxt: Mutex<()>,
286 }
287 impl Default for FakeThread {
288 fn default() -> Self {
289 Self {
290 ctl: Sigcontrol::default(),
291 pctl: SigProcControl {
292 pending: AtomicU64::new(0),
293 actions: core::array::from_fn(|_| RawAction::default()),
294 sender_infos: Default::default(),
295 },
296 ctxt: Default::default(),
297 }
298 }
299 }
300
301 #[test]
302 fn singlethread_mask() {
303 model(|| {
304 let fake_thread = Arc::new(FakeThread::default());
305
306 let thread = {
307 let fake_thread = Arc::clone(&fake_thread);
308
309 thread::spawn(move || {
310 fake_thread.ctl.set_allowset(!0);
311 {
312 let _g = fake_thread.ctxt.lock();
313 if fake_thread
314 .ctl
315 .currently_pending_unblocked(&fake_thread.pctl)
316 == 0
317 {
318 drop(_g);
319 thread::park();
320 }
321 }
322 })
323 };
324
325 for sig in 1..=64 {
326 let _g = fake_thread.ctxt.lock();
327
328 let idx = sig - 1;
329 let bit = 1 << (idx % 32);
330
331 fake_thread.ctl.word[idx / 32].fetch_or(bit, Ordering::Relaxed);
332 let w = fake_thread.ctl.word[idx / 32].load(Ordering::Relaxed);
333
334 if w & (w >> 32) != 0 {
335 thread.thread().unpark();
336 }
337 }
338
339 thread.join().unwrap();
340 });
341 }
342}