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