Add loom test, and move signal ABI to separate mod.
This commit is contained in:
@@ -18,3 +18,6 @@ std = []
|
||||
[dependencies]
|
||||
bitflags = "2.4"
|
||||
core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" }
|
||||
|
||||
[target.'cfg(loom)'.dev-dependencies]
|
||||
loom = "0.7"
|
||||
|
||||
+2
-186
@@ -2,10 +2,9 @@ use core::{
|
||||
mem,
|
||||
ops::{Deref, DerefMut},
|
||||
slice,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
use crate::flag::{EventFlags, MapFlags, PtraceFlags, SigcontrolFlags};
|
||||
use crate::flag::{EventFlags, MapFlags, PtraceFlags};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
#[repr(C)]
|
||||
@@ -358,187 +357,4 @@ impl DerefMut for SetSighandlerData {
|
||||
unsafe { slice::from_raw_parts_mut(self as *mut Self as *mut u8, mem::size_of::<Self>()) }
|
||||
}
|
||||
}
|
||||
/// Signal runtime struct for the entire process
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(4096))]
|
||||
pub struct SigProcControl {
|
||||
pub pending: AtomicU64,
|
||||
pub actions: [RawAction; 64],
|
||||
}
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(16))]
|
||||
pub struct RawAction {
|
||||
/// Only two MSBs are interesting for the kernel. If bit 63 is set, signal is ignored. If bit
|
||||
/// 62 is set and the signal is SIGTSTP/SIGTTIN/SIGTTOU, it's equivalent to the action of
|
||||
/// SIGSTOP.
|
||||
pub first: AtomicU64,
|
||||
/// Completely ignored by the kernel, but exists so userspace can (when 16-byte atomics exist)
|
||||
/// atomically set both the handler, sigaction flags, and sigaction mask.
|
||||
pub user_data: AtomicU64,
|
||||
}
|
||||
|
||||
/// Signal runtime struct for a thread
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct Sigcontrol {
|
||||
// composed of [lo "pending" | lo "unmasked", hi "pending" | hi "unmasked"]
|
||||
pub word: [AtomicU64; 2],
|
||||
|
||||
pub control_flags: SigatomicUsize,
|
||||
|
||||
pub saved_ip: NonatomicUsize, // rip/eip/pc
|
||||
pub saved_archdep_reg: NonatomicUsize, // rflags/eflags/x0
|
||||
}
|
||||
|
||||
impl Sigcontrol {
|
||||
pub fn currently_pending_unblocked(&self) -> u64 {
|
||||
let [w0, w1] = self.word.each_ref().map(|w| w.load(Ordering::Relaxed));
|
||||
(w0 & 0xffff_ffff) | ((w1 & 0xffff_ffff) << 32)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct SigatomicUsize(AtomicUsize);
|
||||
|
||||
impl SigatomicUsize {
|
||||
#[inline]
|
||||
pub fn load(&self, ordering: Ordering) -> usize {
|
||||
let value = self.0.load(Ordering::Relaxed);
|
||||
if ordering != Ordering::Relaxed {
|
||||
core::sync::atomic::compiler_fence(ordering);
|
||||
}
|
||||
value
|
||||
}
|
||||
#[inline]
|
||||
pub fn store(&self, value: usize, ordering: Ordering) {
|
||||
if ordering != Ordering::Relaxed {
|
||||
core::sync::atomic::compiler_fence(ordering);
|
||||
}
|
||||
self.0.store(value, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct NonatomicUsize(AtomicUsize);
|
||||
|
||||
impl NonatomicUsize {
|
||||
#[inline]
|
||||
pub fn get(&self) -> usize {
|
||||
self.0.load(Ordering::Relaxed)
|
||||
}
|
||||
#[inline]
|
||||
pub fn set(&self, value: usize) {
|
||||
self.0.store(value, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sig_bit(sig: usize) -> u64 {
|
||||
1 << (sig - 1)
|
||||
}
|
||||
impl SigProcControl {
|
||||
pub fn signal_will_ign(&self, sig: usize) -> bool {
|
||||
self.actions[sig - 1].first.load(Ordering::Relaxed) & (1 << 63) != 0
|
||||
}
|
||||
pub fn signal_will_stop(&self, sig: usize) -> bool {
|
||||
use crate::flag::*;
|
||||
matches!(sig, SIGTSTP | SIGTTIN | SIGTTOU) && self.actions[sig - 1].first.load(Ordering::Relaxed) & (1 << 62) != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86"))]
|
||||
pub use core::sync::atomic::AtomicU64;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
pub use self::atomic::AtomicU64;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
mod atomic {
|
||||
use core::{cell::UnsafeCell, sync::atomic::Ordering};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AtomicU64(UnsafeCell<u64>);
|
||||
|
||||
unsafe impl Send for AtomicU64 {}
|
||||
unsafe impl Sync for AtomicU64 {}
|
||||
|
||||
impl AtomicU64 {
|
||||
pub const fn new(inner: u64) -> Self {
|
||||
Self(UnsafeCell::new(inner))
|
||||
}
|
||||
pub fn compare_exchange(
|
||||
&self,
|
||||
old: u64,
|
||||
new: u64,
|
||||
_success: Ordering,
|
||||
_failure: Ordering,
|
||||
) -> Result<u64, u64> {
|
||||
let old_hi = (old >> 32) as u32;
|
||||
let old_lo = old as u32;
|
||||
let new_hi = (new >> 32) as u32;
|
||||
let new_lo = new as u32;
|
||||
let mut out_hi;
|
||||
let mut out_lo;
|
||||
|
||||
unsafe {
|
||||
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);
|
||||
}
|
||||
|
||||
if old_hi == out_hi && old_lo == out_lo {
|
||||
Ok(old)
|
||||
} else {
|
||||
Ok(u64::from(out_lo) | (u64::from(out_hi) << 32))
|
||||
}
|
||||
}
|
||||
pub fn load(&self, ordering: Ordering) -> u64 {
|
||||
match self.compare_exchange(0, 0, ordering, ordering) {
|
||||
Ok(new) => new,
|
||||
Err(new) => new,
|
||||
}
|
||||
}
|
||||
pub fn store(&self, new: u64, ordering: Ordering) {
|
||||
let mut old = 0;
|
||||
|
||||
loop {
|
||||
match self.compare_exchange(old, new, ordering, Ordering::Relaxed) {
|
||||
Ok(_) => break,
|
||||
Err(new) => {
|
||||
old = new;
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn fetch_update(
|
||||
&self,
|
||||
set_order: Ordering,
|
||||
fetch_order: Ordering,
|
||||
mut f: impl FnMut(u64) -> Option<u64>,
|
||||
) -> Result<u64, u64> {
|
||||
let mut old = self.load(fetch_order);
|
||||
|
||||
loop {
|
||||
let new = f(old).ok_or(old)?;
|
||||
match self.compare_exchange(old, new, set_order, Ordering::Relaxed) {
|
||||
Ok(_) => return Ok(new),
|
||||
Err(changed) => {
|
||||
old = changed;
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn fetch_or(&self, bits: u64, order: Ordering) -> u64 {
|
||||
self.fetch_update(order, Ordering::Relaxed, |b| Some(b | bits))
|
||||
.unwrap()
|
||||
}
|
||||
pub fn fetch_and(&self, bits: u64, order: Ordering) -> u64 {
|
||||
self.fetch_update(order, Ordering::Relaxed, |b| Some(b & bits))
|
||||
.unwrap()
|
||||
}
|
||||
pub fn fetch_add(&self, term: u64, order: Ordering) -> u64 {
|
||||
self.fetch_update(order, Ordering::Relaxed, |b| Some(b.wrapping_add(term)))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
pub use crate::sigabi::*;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![cfg_attr(not(any(feature = "std", test)), no_std)]
|
||||
#![allow(unexpected_cfgs)] // why does this even exist?
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate core;
|
||||
@@ -53,6 +54,9 @@ pub mod io;
|
||||
/// Call numbers used by each system call
|
||||
pub mod number;
|
||||
|
||||
/// ABI for shared memory based signals
|
||||
pub mod sigabi;
|
||||
|
||||
/// V2 scheme format
|
||||
pub mod schemev2;
|
||||
|
||||
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
/// Signal runtime struct for the entire process
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(4096))]
|
||||
pub struct SigProcControl {
|
||||
pub pending: AtomicU64,
|
||||
pub actions: [RawAction; 64],
|
||||
}
|
||||
#[derive(Debug)]
|
||||
#[repr(C, align(16))]
|
||||
pub struct RawAction {
|
||||
/// Only two MSBs are interesting for the kernel. If bit 63 is set, signal is ignored. If bit
|
||||
/// 62 is set and the signal is SIGTSTP/SIGTTIN/SIGTTOU, it's equivalent to the action of
|
||||
/// SIGSTOP.
|
||||
pub first: AtomicU64,
|
||||
/// Completely ignored by the kernel, but exists so userspace can (when 16-byte atomics exist)
|
||||
/// atomically set both the handler, sigaction flags, and sigaction mask.
|
||||
pub user_data: AtomicU64,
|
||||
}
|
||||
|
||||
/// Signal runtime struct for a thread
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct Sigcontrol {
|
||||
// composed of [lo "pending" | lo "unmasked", hi "pending" | hi "unmasked"]
|
||||
pub word: [AtomicU64; 2],
|
||||
|
||||
pub control_flags: SigatomicUsize,
|
||||
|
||||
pub saved_ip: NonatomicUsize, // rip/eip/pc
|
||||
pub saved_archdep_reg: NonatomicUsize, // rflags/eflags/x0
|
||||
}
|
||||
|
||||
impl Sigcontrol {
|
||||
pub fn currently_pending_unblocked(&self) -> u64 {
|
||||
let [w0, w1] = self.word.each_ref().map(|w| w.load(Ordering::Relaxed)).map(|w| (w & 0xffff_ffff) & (w >> 32));
|
||||
//core::sync::atomic::fence(Ordering::Acquire);
|
||||
w0 | (w1 << 32)
|
||||
}
|
||||
pub fn set_allowset(&self, new_allowset: u64) -> u64 {
|
||||
//core::sync::atomic::fence(Ordering::Release);
|
||||
let [w0, w1] = self.word.each_ref().map(|w| w.load(Ordering::Relaxed));
|
||||
let old_a0 = w0 & 0xffff_ffff_0000_0000;
|
||||
let old_a1 = w1 & 0xffff_ffff_0000_0000;
|
||||
let new_a0 = (new_allowset & 0xffff_ffff) << 32;
|
||||
let new_a1 = new_allowset & 0xffff_ffff_0000_0000;
|
||||
|
||||
let prev_w0 = self.word[0].fetch_add(new_a0.wrapping_sub(old_a0), Ordering::Relaxed);
|
||||
let prev_w1 = self.word[0].fetch_add(new_a1.wrapping_sub(old_a1), Ordering::Relaxed);
|
||||
//core::sync::atomic::fence(Ordering::Acquire);
|
||||
let up0 = prev_w0 & (prev_w0 >> 32);
|
||||
let up1 = prev_w1 & (prev_w1 >> 32);
|
||||
|
||||
up0 | (up1 << 32)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct SigatomicUsize(AtomicUsize);
|
||||
|
||||
impl SigatomicUsize {
|
||||
#[inline]
|
||||
pub fn load(&self, ordering: Ordering) -> usize {
|
||||
let value = self.0.load(Ordering::Relaxed);
|
||||
if ordering != Ordering::Relaxed {
|
||||
core::sync::atomic::compiler_fence(ordering);
|
||||
}
|
||||
value
|
||||
}
|
||||
#[inline]
|
||||
pub fn store(&self, value: usize, ordering: Ordering) {
|
||||
if ordering != Ordering::Relaxed {
|
||||
core::sync::atomic::compiler_fence(ordering);
|
||||
}
|
||||
self.0.store(value, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct NonatomicUsize(AtomicUsize);
|
||||
|
||||
impl NonatomicUsize {
|
||||
#[inline]
|
||||
pub fn get(&self) -> usize {
|
||||
self.0.load(Ordering::Relaxed)
|
||||
}
|
||||
#[inline]
|
||||
pub fn set(&self, value: usize) {
|
||||
self.0.store(value, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sig_bit(sig: usize) -> u64 {
|
||||
1 << (sig - 1)
|
||||
}
|
||||
impl SigProcControl {
|
||||
pub fn signal_will_ign(&self, sig: usize) -> bool {
|
||||
self.actions[sig - 1].first.load(Ordering::Relaxed) & (1 << 63) != 0
|
||||
}
|
||||
pub fn signal_will_stop(&self, sig: usize) -> bool {
|
||||
use crate::flag::*;
|
||||
matches!(sig, SIGTSTP | SIGTTIN | SIGTTOU) && self.actions[sig - 1].first.load(Ordering::Relaxed) & (1 << 62) != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86"))]
|
||||
pub use core::sync::atomic::AtomicU64;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
pub use self::atomic::AtomicU64;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
mod atomic {
|
||||
use core::{cell::UnsafeCell, sync::atomic::Ordering};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct AtomicU64(UnsafeCell<u64>);
|
||||
|
||||
unsafe impl Send for AtomicU64 {}
|
||||
unsafe impl Sync for AtomicU64 {}
|
||||
|
||||
impl AtomicU64 {
|
||||
pub const fn new(inner: u64) -> Self {
|
||||
Self(UnsafeCell::new(inner))
|
||||
}
|
||||
pub fn compare_exchange(
|
||||
&self,
|
||||
old: u64,
|
||||
new: u64,
|
||||
_success: Ordering,
|
||||
_failure: Ordering,
|
||||
) -> Result<u64, u64> {
|
||||
let old_hi = (old >> 32) as u32;
|
||||
let old_lo = old as u32;
|
||||
let new_hi = (new >> 32) as u32;
|
||||
let new_lo = new as u32;
|
||||
let mut out_hi;
|
||||
let mut out_lo;
|
||||
|
||||
unsafe {
|
||||
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);
|
||||
}
|
||||
|
||||
if old_hi == out_hi && old_lo == out_lo {
|
||||
Ok(old)
|
||||
} else {
|
||||
Ok(u64::from(out_lo) | (u64::from(out_hi) << 32))
|
||||
}
|
||||
}
|
||||
pub fn load(&self, ordering: Ordering) -> u64 {
|
||||
match self.compare_exchange(0, 0, ordering, ordering) {
|
||||
Ok(new) => new,
|
||||
Err(new) => new,
|
||||
}
|
||||
}
|
||||
pub fn store(&self, new: u64, ordering: Ordering) {
|
||||
let mut old = 0;
|
||||
|
||||
loop {
|
||||
match self.compare_exchange(old, new, ordering, Ordering::Relaxed) {
|
||||
Ok(_) => break,
|
||||
Err(new) => {
|
||||
old = new;
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn fetch_update(
|
||||
&self,
|
||||
set_order: Ordering,
|
||||
fetch_order: Ordering,
|
||||
mut f: impl FnMut(u64) -> Option<u64>,
|
||||
) -> Result<u64, u64> {
|
||||
let mut old = self.load(fetch_order);
|
||||
|
||||
loop {
|
||||
let new = f(old).ok_or(old)?;
|
||||
match self.compare_exchange(old, new, set_order, Ordering::Relaxed) {
|
||||
Ok(_) => return Ok(new),
|
||||
Err(changed) => {
|
||||
old = changed;
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn fetch_or(&self, bits: u64, order: Ordering) -> u64 {
|
||||
self.fetch_update(order, Ordering::Relaxed, |b| Some(b | bits))
|
||||
.unwrap()
|
||||
}
|
||||
pub fn fetch_and(&self, bits: u64, order: Ordering) -> u64 {
|
||||
self.fetch_update(order, Ordering::Relaxed, |b| Some(b & bits))
|
||||
.unwrap()
|
||||
}
|
||||
pub fn fetch_add(&self, term: u64, order: Ordering) -> u64 {
|
||||
self.fetch_update(order, Ordering::Relaxed, |b| Some(b.wrapping_add(term)))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
#[cfg(not(loom))]
|
||||
use std::{thread, sync::Mutex};
|
||||
#[cfg(not(loom))]
|
||||
fn model(f: impl FnOnce()) { f() }
|
||||
|
||||
#[cfg(loom)]
|
||||
use loom::{model, thread, sync::Mutex};
|
||||
|
||||
use crate::Sigcontrol;
|
||||
|
||||
#[test]
|
||||
fn singlethread_mask() {
|
||||
model(|| {
|
||||
let tctl = Arc::new(Sigcontrol::default());
|
||||
let mutex = Arc::new(Mutex::new(()));
|
||||
|
||||
let thread = {
|
||||
let tctl = Arc::clone(&tctl);
|
||||
let mutex = Arc::clone(&mutex);
|
||||
|
||||
thread::spawn(move || {
|
||||
tctl.set_allowset(!0);
|
||||
{
|
||||
let _g = mutex.lock();
|
||||
if tctl.currently_pending_unblocked() == 0 {
|
||||
drop(_g);
|
||||
thread::park();
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
for sig in 1..=64 {
|
||||
let _g = mutex.lock();
|
||||
|
||||
let idx = sig - 1;
|
||||
let bit = 1 << (idx % 32);
|
||||
|
||||
tctl.word[idx / 32].fetch_or(bit, Ordering::Relaxed);
|
||||
let w = tctl.word[idx / 32].load(Ordering::Relaxed);
|
||||
|
||||
if w & (w >> 32) != 0 {
|
||||
thread.thread().unpark();
|
||||
}
|
||||
}
|
||||
|
||||
thread.join().unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user