Add SIGINT handler

This commit is contained in:
Alex Shelkovnykov 2023-09-25 00:08:51 -06:00
parent 0072e09a85
commit ae674c1372
7 changed files with 185 additions and 83 deletions

View File

@ -35,7 +35,7 @@
:: %mean hint :: %mean hint
~_ [%leaf "I am a %mean hint via ~_ from +wack"] ~_ [%leaf "I am a %mean hint via ~_ from +wack"]
:: %hela hint :: %hela hint
:: ~> %hela ~> %hela
:: %memo hint :: %memo hint
~+ ~+
?~ m +(n) ?~ m +(n)
@ -47,13 +47,23 @@
!. !.
|= [m=@ud n=@ud] |= [m=@ud n=@ud]
:: %hela hint :: %hela hint
:: ~> %hela ~> %hela
:: %memo hint :: %memo hint
~+ ~+
?~ m +(n) ?~ m +(n)
?~ n ?~ n
(wack (dec m) 1) (wack (dec m) 1)
(wack (dec m) $(n (dec n))) (wack (dec m) $(n (dec n)))
++ slow
|= x=@ud
!:
(slow-help x 0)
++ slow-help
|= [x=@ud y=@ud]
!.
?: .= x y
x
$(y .+(y))
-- => -- =>
:: ::
|% |%
@ -65,19 +75,23 @@
^- ^ ^- ^
:: ::
?. ?=(?(%crud %wack %wyrd) p.card.ovo) ?. ?=(?(%crud %wack %wyrd) p.card.ovo)
~> %slog.[0 leaf+(scow %ud (wack 1 1))] ~> %slog.[0 leaf+(scow %ud (slow (bex 23)))]
[~ ..poke] [~ ..poke]
:: ::
=/ buz =/ buz
~> %mean.'pith: bad wasp' ~> %mean.'pith: bad wasp'
;;(wasp card.ovo) ;;(wasp card.ovo)
?+ -.buz ?+ -.buz
~> %slog.[0 leaf+"default $wasp"] ~> %slog.[0 leaf+(scow %ud (wack 1 1))]
[~ ..poke] [~ ..poke]
:: ::
%crud %crud
~> %slog.[0 goof.buz] =/ tang tang.goof.buz
[~ ..poke] |-
?~ tang
[~ ..poke]
~> %slog.[0 -.tang]
$(tang +.tang)
== ==
-- --
:: ::

21
rust/ares/Cargo.lock generated
View File

@ -20,11 +20,13 @@ dependencies = [
"either", "either",
"ibig", "ibig",
"intmap", "intmap",
"lazy_static",
"libc", "libc",
"memmap", "memmap",
"murmur3", "murmur3",
"num-derive", "num-derive",
"num-traits", "num-traits",
"signal-hook",
"static_assertions", "static_assertions",
] ]
@ -569,6 +571,25 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "signal-hook"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "static_assertions" name = "static_assertions"
version = "1.1.0" version = "1.1.0"

View File

@ -11,18 +11,20 @@ edition = "2018"
[dependencies] [dependencies]
ares_macros = { path = "../ares_macros" } ares_macros = { path = "../ares_macros" }
bitvec = "1.0.0"
either = "1.9.0"
libc = "0.2.126"
murmur3 = { git = "https://github.com/tloncorp/murmur3", rev = "7878a0f" }
memmap = "0.7.0"
intmap = "1.1.0"
num-traits = "0.2"
num-derive = "0.3"
criterion = "0.4"
static_assertions = "1.1.0"
ibig = { path = "../ibig-rs" }
assert_no_alloc = "1.1.2" assert_no_alloc = "1.1.2"
bitvec = "1.0.0"
criterion = "0.4"
either = "1.9.0"
ibig = { path = "../ibig-rs" }
intmap = "1.1.0"
lazy_static = "1.4.0"
libc = "0.2.126"
memmap = "0.7.0"
murmur3 = { git = "https://github.com/tloncorp/murmur3", rev = "7878a0f" }
num-derive = "0.3"
num-traits = "0.2"
signal-hook = "0.3"
static_assertions = "1.1.0"
[build-dependencies] [build-dependencies]
cc = "1.0.79" cc = "1.0.79"

View File

@ -7,10 +7,13 @@ use crate::mem::NockStack;
use crate::newt::Newt; use crate::newt::Newt;
use crate::noun; use crate::noun;
use crate::noun::{tape, Atom, Cell, IndirectAtom, Noun, Slots, D, T}; use crate::noun::{tape, Atom, Cell, IndirectAtom, Noun, Slots, D, T};
use crate::serf::TERMINATOR;
use ares_macros::tas; use ares_macros::tas;
use assert_no_alloc::assert_no_alloc; use assert_no_alloc::assert_no_alloc;
use bitvec::prelude::{BitSlice, Lsb0}; use bitvec::prelude::{BitSlice, Lsb0};
use either::Either::*; use either::Either::*;
use std::sync::atomic::Ordering;
use std::sync::Arc;
crate::gdb!(); crate::gdb!();
@ -265,10 +268,12 @@ pub fn interpret(
mut subject: Noun, mut subject: Noun,
formula: Noun, formula: Noun,
) -> Result<Noun, Tone> { ) -> Result<Noun, Tone> {
let terminator = Arc::clone(&TERMINATOR);
let mut res: Noun = D(0); let mut res: Noun = D(0);
let mut cache = Hamt::<Noun>::new(); let mut cache = Hamt::<Noun>::new();
// XX: Should this come after initial frame_push()? // XX: Should this come after initial frame_push()?
let virtual_frame = stack.get_frame_pointer(); let virtual_frame = stack.get_frame_pointer();
let virtual_trace = stack.get_mean_stack();
stack.frame_push(0); stack.frame_push(0);
unsafe { unsafe {
@ -323,6 +328,7 @@ pub fn interpret(
res = noun; res = noun;
stack.pop::<NockWork>(); stack.pop::<NockWork>();
} else { } else {
// Axis invalid for input Noun
break Err(NockErr::Deterministic); break Err(NockErr::Deterministic);
} }
} }
@ -330,37 +336,43 @@ pub fn interpret(
res = once.noun; res = once.noun;
stack.pop::<NockWork>(); stack.pop::<NockWork>();
} }
NockWork::Work2(mut vale) => match vale.todo { NockWork::Work2(mut vale) => {
Todo2::ComputeSubject => { if (*terminator).load(Ordering::Relaxed) {
vale.todo = Todo2::ComputeFormula; break Err(NockErr::NonDeterministic);
*stack.top() = NockWork::Work2(vale);
push_formula(stack, vale.subject, false)?;
} }
Todo2::ComputeFormula => {
vale.todo = Todo2::ComputeResult; match vale.todo {
vale.subject = res; Todo2::ComputeSubject => {
*stack.top() = NockWork::Work2(vale); vale.todo = Todo2::ComputeFormula;
push_formula(stack, vale.formula, false)?;
}
Todo2::ComputeResult => {
if vale.tail {
stack.pop::<NockWork>();
subject = vale.subject;
push_formula(stack, res, true)?;
} else {
vale.todo = Todo2::RestoreSubject;
std::mem::swap(&mut vale.subject, &mut subject);
*stack.top() = NockWork::Work2(vale); *stack.top() = NockWork::Work2(vale);
stack.frame_push(0); push_formula(stack, vale.subject, false)?;
*stack.push() = NockWork::Ret; }
push_formula(stack, res, true)?; Todo2::ComputeFormula => {
vale.todo = Todo2::ComputeResult;
vale.subject = res;
*stack.top() = NockWork::Work2(vale);
push_formula(stack, vale.formula, false)?;
}
Todo2::ComputeResult => {
if vale.tail {
stack.pop::<NockWork>();
subject = vale.subject;
push_formula(stack, res, true)?;
} else {
vale.todo = Todo2::RestoreSubject;
std::mem::swap(&mut vale.subject, &mut subject);
*stack.top() = NockWork::Work2(vale);
stack.frame_push(0);
*stack.push() = NockWork::Ret;
push_formula(stack, res, true)?;
}
}
Todo2::RestoreSubject => {
subject = vale.subject;
stack.pop::<NockWork>();
} }
} }
Todo2::RestoreSubject => { }
subject = vale.subject;
stack.pop::<NockWork>();
}
},
NockWork::Work3(mut thee) => match thee.todo { NockWork::Work3(mut thee) => match thee.todo {
Todo3::ComputeChild => { Todo3::ComputeChild => {
thee.todo = Todo3::ComputeType; thee.todo = Todo3::ComputeType;
@ -481,37 +493,43 @@ pub fn interpret(
stack.pop::<NockWork>(); stack.pop::<NockWork>();
} }
}, },
NockWork::Work9(mut kale) => match kale.todo { NockWork::Work9(mut kale) => {
Todo9::ComputeCore => { if (*terminator).load(Ordering::Relaxed) {
kale.todo = Todo9::ComputeResult; break Err(NockErr::NonDeterministic);
*stack.top() = NockWork::Work9(kale);
push_formula(stack, kale.core, false)?;
} }
Todo9::ComputeResult => {
if let Ok(formula) = res.slot_atom(kale.axis) { match kale.todo {
if kale.tail { Todo9::ComputeCore => {
stack.pop::<NockWork>(); kale.todo = Todo9::ComputeResult;
subject = res; *stack.top() = NockWork::Work9(kale);
push_formula(stack, formula, true)?; push_formula(stack, kale.core, false)?;
}
Todo9::ComputeResult => {
if let Ok(formula) = res.slot_atom(kale.axis) {
if kale.tail {
stack.pop::<NockWork>();
subject = res;
push_formula(stack, formula, true)?;
} else {
kale.todo = Todo9::RestoreSubject;
kale.core = subject;
*stack.top() = NockWork::Work9(kale);
subject = res;
stack.frame_push(0);
*stack.push() = NockWork::Ret;
push_formula(stack, formula, true)?;
}
} else { } else {
kale.todo = Todo9::RestoreSubject; // Axis into core must be atom
kale.core = subject; break Err(NockErr::Deterministic);
*stack.top() = NockWork::Work9(kale);
subject = res;
stack.frame_push(0);
*stack.push() = NockWork::Ret;
push_formula(stack, formula, true)?;
} }
} else { }
// Axis into core must be atom Todo9::RestoreSubject => {
break Err(NockErr::Deterministic); subject = kale.core;
stack.pop::<NockWork>();
} }
} }
Todo9::RestoreSubject => { }
subject = kale.core;
stack.pop::<NockWork>();
}
},
NockWork::Work10(mut diet) => { NockWork::Work10(mut diet) => {
match diet.todo { match diet.todo {
Todo10::ComputeTree => { Todo10::ComputeTree => {
@ -623,7 +641,13 @@ pub fn interpret(
match nock { match nock {
Ok(res) => Ok(res), Ok(res) => Ok(res),
Err(err) => Err(exit_early(stack, &mut cache, virtual_frame, err)), Err(err) => Err(exit_early(
stack,
&mut cache,
virtual_frame,
virtual_trace,
err,
)),
} }
} }
@ -830,6 +854,7 @@ pub fn exit_early(
stack: &mut NockStack, stack: &mut NockStack,
cache: &mut Hamt<Noun>, cache: &mut Hamt<Noun>,
virtual_frame: *const u64, virtual_frame: *const u64,
virtual_trace: Noun,
error: NockErr, error: NockErr,
) -> Tone { ) -> Tone {
let mut trace = stack.get_mean_stack(); let mut trace = stack.get_mean_stack();
@ -839,6 +864,9 @@ pub fn exit_early(
stack.preserve(cache); stack.preserve(cache);
stack.frame_pop(); stack.frame_pop();
} }
while !stack.get_mean_stack().raw_equals(virtual_trace) {
stack.trace_pop();
}
}; };
Tone::Error(error, trace) Tone::Error(error, trace)
} }
@ -1026,6 +1054,11 @@ fn match_hint_pre_nock(
Ok(None) Ok(None)
} }
tas!(b"hand") | tas!(b"hunk") | tas!(b"lose") | tas!(b"mean") | tas!(b"spot") => { tas!(b"hand") | tas!(b"hunk") | tas!(b"lose") | tas!(b"mean") | tas!(b"spot") => {
let terminator = Arc::clone(&TERMINATOR);
if (*terminator).load(Ordering::Relaxed) {
return Err(NockErr::NonDeterministic);
}
let trace = T(stack, &[tag.as_noun(), res.ok_or(NockErr::Deterministic)?]); let trace = T(stack, &[tag.as_noun(), res.ok_or(NockErr::Deterministic)?]);
stack.trace_push(trace); stack.trace_push(trace);
Ok(None) Ok(None)

View File

@ -10,7 +10,7 @@ use crate::noun::{Noun, D, T};
crate::gdb!(); crate::gdb!();
// XX: interpret should accept optional scry function and potentially produce blocked // XX: interpret should accept optional scry function and potentially produce blocked
pub fn jet_mink( pub fn jet_mink(
stack: &mut NockStack, stack: &mut NockStack,
newt: &mut Option<&mut Newt>, newt: &mut Option<&mut Newt>,
@ -23,6 +23,7 @@ pub fn jet_mink(
let v_formula = slot(arg, 5)?; let v_formula = slot(arg, 5)?;
let _scry = slot(arg, 3)?; let _scry = slot(arg, 3)?;
// XX: no partial traces; all of our traces go down to the "home road"
match interpret(stack, newt, v_subject, v_formula) { match interpret(stack, newt, v_subject, v_formula) {
Ok(res) => Ok(T(stack, &[D(0), res])), Ok(res) => Ok(T(stack, &[D(0), res])),
Err(err) => match err { Err(err) => match err {
@ -61,11 +62,6 @@ pub mod util {
let tag = tone.head().as_direct()?; let tag = tone.head().as_direct()?;
let original_list = tone.tail(); let original_list = tone.tail();
// if tag.data() < 2 {
// return Ok(tone);
// } else if tag.data() > 2 {
// return Err(JetErr::Deterministic);
// }
match tag.data() { match tag.data() {
x if x < 2 => return Ok(tone), x if x < 2 => return Ok(tone),
x if x > 2 => return Err(JetErr::Deterministic), x if x > 2 => return Err(JetErr::Deterministic),
@ -260,6 +256,21 @@ pub mod util {
mod tests { mod tests {
use super::*; use super::*;
use crate::jets::util::test::{assert_jet, init_stack}; use crate::jets::util::test::{assert_jet, init_stack};
use crate::serf::TERMINATOR;
use std::sync::Arc;
#[test]
fn init() {
// This needs to be done because TERMINATOR is lazy allocated, and if you don't
// do it before you call the unit tests it'll get allocated on the Rust heap
// inside an assert_no_alloc block.
//
// Also Rust has no primitive for pre-test setup / post-test teardown, so we
// do it in a test that we rely on being called before any other in this file,
// since we're already using single-threaded test mode to avoid race conditions
// (because Rust doesn't support test order dependencies either).
let _ = Arc::clone(&TERMINATOR);
}
#[test] #[test]
fn test_mink_success() { fn test_mink_success() {

View File

@ -1,5 +1,7 @@
extern crate num_derive; extern crate num_derive;
#[macro_use] #[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate static_assertions; extern crate static_assertions;
pub mod interpreter; pub mod interpreter;
pub mod jets; pub mod jets;

View File

@ -7,12 +7,14 @@ use crate::newt::Newt;
use crate::noun::{Cell, Noun, Slots, D, T}; use crate::noun::{Cell, Noun, Slots, D, T};
use crate::snapshot::{self, Snapshot}; use crate::snapshot::{self, Snapshot};
use ares_macros::tas; use ares_macros::tas;
use signal_hook;
use signal_hook::consts::SIGINT;
use std::fs::create_dir_all; use std::fs::create_dir_all;
use std::io; use std::io;
use std::path::PathBuf; use std::path::PathBuf;
use std::result::Result; use std::result::Result;
use std::thread::sleep; use std::sync::atomic::{AtomicBool, Ordering};
use std::time; use std::sync::Arc;
crate::gdb!(); crate::gdb!();
@ -23,12 +25,20 @@ const POKE_AXIS: u64 = 23;
#[allow(dead_code)] #[allow(dead_code)]
const WISH_AXIS: u64 = 10; const WISH_AXIS: u64 = 10;
// Necessary because Arc::new is not const
lazy_static! {
pub static ref TERMINATOR: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
}
/** /**
* This is suitable for talking to the king process. To test, change the arg_c[0] line in * This is suitable for talking to the king process. To test, change the arg_c[0] line in
* u3_lord_init in vere to point at this binary and start vere like normal. * u3_lord_init in vere to point at this binary and start vere like normal.
*/ */
pub fn serf() -> io::Result<()> { pub fn serf() -> io::Result<()> {
sleep(time::Duration::from_secs(0)); // Register SIGTERM signal hook to set flag first time, shutdown second time
signal_hook::flag::register_conditional_shutdown(SIGINT, 1, Arc::clone(&TERMINATOR))?;
signal_hook::flag::register(SIGINT, Arc::clone(&TERMINATOR))?;
let snap_path_string = std::env::args() let snap_path_string = std::env::args()
.nth(2) .nth(2)
.ok_or(io::Error::new(io::ErrorKind::Other, "no pier path"))?; .ok_or(io::Error::new(io::ErrorKind::Other, "no pier path"))?;
@ -157,7 +167,9 @@ pub fn serf() -> io::Result<()> {
// TODO: on decryption failure in aes_siv, should bail as fast as // TODO: on decryption failure in aes_siv, should bail as fast as
// possible, without rendering stack trace or injecting crud event. See // possible, without rendering stack trace or injecting crud event. See
// c3__evil in vere. // c3__evil in vere.
//
clear_interrupt();
// crud = [+(now) [%$ %arvo ~] [%crud goof ovo]] // crud = [+(now) [%$ %arvo ~] [%crud goof ovo]]
let job_cell = job.as_cell().expect("serf: work: job not a cell"); let job_cell = job.as_cell().expect("serf: work: job not a cell");
let now = inc( let now = inc(
@ -197,6 +209,8 @@ pub fn serf() -> io::Result<()> {
} }
_ => panic!("got message with unknown tag {}", tag), _ => panic!("got message with unknown tag {}", tag),
}; };
clear_interrupt();
} }
Ok(()) Ok(())
@ -231,8 +245,9 @@ pub fn soft(
let tang = mook(stack, &mut Some(newt), tone, false) let tang = mook(stack, &mut Some(newt), tone, false)
.expect("serf: soft: +mook crashed on bail") .expect("serf: soft: +mook crashed on bail")
.tail(); .tail();
// TODO: shouldn't always be exit, look at the comment by u3m_bail to see how vere // XX: noun::Tone or noun::NockErr should use a bail enum system similar to u3m_bail motes;
// handles this // might be able to replace NockErr with mote and map determinism to individual motes;
// for, always set to %exit
let goof = T(stack, &[D(tas!(b"exit")), tang]); let goof = T(stack, &[D(tas!(b"exit")), tang]);
Err(goof) Err(goof)
} }
@ -244,3 +259,7 @@ fn slot(noun: Noun, axis: u64) -> io::Result<Noun> {
noun.slot(axis) noun.slot(axis)
.map_err(|_e| io::Error::new(io::ErrorKind::InvalidInput, "Bad axis")) .map_err(|_e| io::Error::new(io::ErrorKind::InvalidInput, "Bad axis"))
} }
fn clear_interrupt() {
(*TERMINATOR).store(false, Ordering::Relaxed);
}