mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 12:18:19 +00:00
do expect/dbg with atomics
This commit is contained in:
parent
224dffec04
commit
3bae4145d9
3 changed files with 98 additions and 29 deletions
|
@ -1042,12 +1042,9 @@ fn roc_dev_native(
|
|||
envp: bumpalo::collections::Vec<*const c_char>,
|
||||
expect_metadata: ExpectMetadata,
|
||||
) -> ! {
|
||||
use roc_repl_expect::run::ExpectMemory;
|
||||
use signal_hook::{
|
||||
consts::signal::SIGCHLD,
|
||||
consts::signal::{SIGUSR1, SIGUSR2},
|
||||
iterator::Signals,
|
||||
};
|
||||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
|
||||
use roc_repl_expect::run::{ChildProcessMsg, ExpectMemory};
|
||||
|
||||
let ExpectMetadata {
|
||||
mut expectations,
|
||||
|
@ -1055,11 +1052,9 @@ fn roc_dev_native(
|
|||
layout_interner,
|
||||
} = expect_metadata;
|
||||
|
||||
let mut signals = Signals::new(&[SIGCHLD, SIGUSR1, SIGUSR2]).unwrap();
|
||||
|
||||
// let shm_name =
|
||||
let shm_name = format!("/roc_expect_buffer_{}", std::process::id());
|
||||
let memory = ExpectMemory::create_or_reuse_mmap(&shm_name);
|
||||
let mut memory = ExpectMemory::create_or_reuse_mmap(&shm_name);
|
||||
|
||||
let layout_interner = layout_interner.into_global();
|
||||
|
||||
|
@ -1085,12 +1080,14 @@ fn roc_dev_native(
|
|||
std::process::exit(1)
|
||||
}
|
||||
1.. => {
|
||||
for sig in &mut signals {
|
||||
match sig {
|
||||
SIGCHLD => break,
|
||||
SIGUSR1 => {
|
||||
// this is the signal we use for an expect failure. Let's see what the child told us
|
||||
let sigchld = Arc::new(AtomicBool::new(false));
|
||||
signal_hook::flag::register(signal_hook::consts::SIGCHLD, Arc::clone(&sigchld))
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
match memory.wait_for_child(sigchld.clone()) {
|
||||
ChildProcessMsg::Terminate => break,
|
||||
ChildProcessMsg::Expect => {
|
||||
roc_repl_expect::run::render_expects_in_memory(
|
||||
&mut writer,
|
||||
arena,
|
||||
|
@ -1100,10 +1097,10 @@ fn roc_dev_native(
|
|||
&memory,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
SIGUSR2 => {
|
||||
// this is the signal we use for a dbg
|
||||
|
||||
memory.reset();
|
||||
}
|
||||
ChildProcessMsg::Dbg => {
|
||||
roc_repl_expect::run::render_dbgs_in_memory(
|
||||
&mut writer,
|
||||
arena,
|
||||
|
@ -1113,8 +1110,9 @@ fn roc_dev_native(
|
|||
&memory,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
memory.reset();
|
||||
}
|
||||
_ => println!("received signal {}", sig),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const SIGUSR1: c_int = if (builtin.os.tag.isDarwin()) 30 else 10;
|
||||
const SIGUSR2: c_int = if (builtin.os.tag.isDarwin()) 31 else 12;
|
||||
const Atomic = std.atomic.Atomic;
|
||||
|
||||
const O_RDWR: c_int = 2;
|
||||
const O_CREAT: c_int = 64;
|
||||
|
@ -45,13 +44,14 @@ pub fn expectFailedStartSharedFile() callconv(.C) [*]u8 {
|
|||
|
||||
const ptr = @ptrCast([*]u8, shared_ptr);
|
||||
|
||||
SHARED_BUFFER = ptr[0..length];
|
||||
|
||||
return ptr;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
extern fn roc_send_signal(pid: c_int, sig: c_int) c_int;
|
||||
extern fn roc_shm_open(name: *const i8, oflag: c_int, mode: c_uint) c_int;
|
||||
extern fn roc_mmap(addr: ?*anyopaque, length: c_uint, prot: c_int, flags: c_int, fd: c_int, offset: c_uint) *anyopaque;
|
||||
extern fn roc_getppid() c_int;
|
||||
|
@ -81,18 +81,35 @@ pub fn readSharedBufferEnv() callconv(.C) void {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_atomic_ptr() *Atomic(u32) {
|
||||
const usize_ptr = @ptrCast([*]u32, @alignCast(@alignOf(usize), SHARED_BUFFER.ptr));
|
||||
const atomic_ptr = @ptrCast(*Atomic(u32), &usize_ptr[5]);
|
||||
|
||||
return atomic_ptr;
|
||||
}
|
||||
|
||||
pub fn expectFailedFinalize() callconv(.C) void {
|
||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||
const parent_pid = roc_getppid();
|
||||
const atomic_ptr = get_atomic_ptr();
|
||||
atomic_ptr.storeUnchecked(1);
|
||||
|
||||
_ = roc_send_signal(parent_pid, SIGUSR1);
|
||||
// wait till the parent is done before proceeding
|
||||
const Ordering = std.atomic.Ordering;
|
||||
while (atomic_ptr.load(Ordering.Acquire) != 0) {
|
||||
std.atomic.spinLoopHint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sendDbg() callconv(.C) void {
|
||||
if (builtin.os.tag == .macos or builtin.os.tag == .linux) {
|
||||
const parent_pid = roc_getppid();
|
||||
const atomic_ptr = get_atomic_ptr();
|
||||
atomic_ptr.storeUnchecked(2);
|
||||
|
||||
_ = roc_send_signal(parent_pid, SIGUSR2);
|
||||
// wait till the parent is done before proceeding
|
||||
const Ordering = std.atomic.Ordering;
|
||||
while (atomic_ptr.load(Ordering.Acquire) != 0) {
|
||||
std.atomic.spinLoopHint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
use std::{os::unix::process::parent_id, sync::Arc};
|
||||
use std::{
|
||||
os::unix::process::parent_id,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicU32},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use bumpalo::collections::Vec as BumpVec;
|
||||
use bumpalo::Bump;
|
||||
|
@ -105,6 +111,16 @@ impl<'a> ExpectMemory<'a> {
|
|||
let mut result = RocCallResult::default();
|
||||
unsafe { set_shared_buffer((self.ptr, self.length), &mut result) };
|
||||
}
|
||||
|
||||
pub fn wait_for_child(&self, sigchld: Arc<AtomicBool>) -> ChildProcessMsg {
|
||||
let sequence = ExpectSequence { ptr: self.ptr };
|
||||
sequence.wait_for_child(sigchld)
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
let mut sequence = ExpectSequence { ptr: self.ptr };
|
||||
sequence.reset();
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -591,16 +607,18 @@ struct ExpectSequence {
|
|||
}
|
||||
|
||||
impl ExpectSequence {
|
||||
const START_OFFSET: usize = 16;
|
||||
const START_OFFSET: usize = 8 + 8 + 8;
|
||||
|
||||
const COUNT_INDEX: usize = 0;
|
||||
const OFFSET_INDEX: usize = 1;
|
||||
const LOCK_INDEX: usize = 2;
|
||||
|
||||
fn new(ptr: *mut u8) -> Self {
|
||||
unsafe {
|
||||
let ptr = ptr as *mut usize;
|
||||
std::ptr::write_unaligned(ptr.add(Self::COUNT_INDEX), 0);
|
||||
std::ptr::write_unaligned(ptr.add(Self::OFFSET_INDEX), Self::START_OFFSET);
|
||||
std::ptr::write_unaligned(ptr.add(Self::LOCK_INDEX), 0);
|
||||
}
|
||||
|
||||
Self {
|
||||
|
@ -611,11 +629,47 @@ impl ExpectSequence {
|
|||
fn count_failures(&self) -> usize {
|
||||
unsafe { *(self.ptr as *const usize).add(Self::COUNT_INDEX) }
|
||||
}
|
||||
|
||||
fn wait_for_child(&self, sigchld: Arc<AtomicBool>) -> ChildProcessMsg {
|
||||
use std::sync::atomic::Ordering;
|
||||
let ptr = self.ptr as *const u32;
|
||||
let atomic_ptr: *const AtomicU32 = unsafe { ptr.add(5).cast() };
|
||||
let atomic = unsafe { &*atomic_ptr };
|
||||
|
||||
loop {
|
||||
if sigchld.load(Ordering::Relaxed) {
|
||||
break ChildProcessMsg::Terminate;
|
||||
}
|
||||
|
||||
match atomic.load(Ordering::Acquire) {
|
||||
0 => std::hint::spin_loop(),
|
||||
1 => break ChildProcessMsg::Expect,
|
||||
2 => break ChildProcessMsg::Dbg,
|
||||
n => panic!("invalid atomic value set by the child: {:#x}", n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
unsafe {
|
||||
let ptr = self.ptr as *mut usize;
|
||||
std::ptr::write_unaligned(ptr.add(Self::COUNT_INDEX), 0);
|
||||
std::ptr::write_unaligned(ptr.add(Self::OFFSET_INDEX), Self::START_OFFSET);
|
||||
std::ptr::write_unaligned(ptr.add(Self::LOCK_INDEX), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ChildProcessMsg {
|
||||
Expect = 1,
|
||||
Dbg = 2,
|
||||
Terminate = 3,
|
||||
}
|
||||
|
||||
struct ExpectFrame {
|
||||
region: Region,
|
||||
module_id: ModuleId,
|
||||
|
||||
start_offset: usize,
|
||||
}
|
||||
|
||||
|
@ -627,8 +681,8 @@ impl ExpectFrame {
|
|||
let module_id_bytes: [u8; 4] = unsafe { *(start.add(offset + 8).cast()) };
|
||||
let module_id: ModuleId = unsafe { std::mem::transmute(module_id_bytes) };
|
||||
|
||||
// skip to frame, 8 bytes for region, 4 for module id
|
||||
let start_offset = offset + 12;
|
||||
// skip to frame
|
||||
let start_offset = offset + 8 + 4;
|
||||
|
||||
Self {
|
||||
region,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue