From ae674c1372adbd42919d7df55e082f7c9ed1679d Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Mon, 25 Sep 2023 00:08:51 -0600 Subject: [PATCH] Add SIGINT handler --- hoon/scaffolding/toddler.hoon | 26 +++++-- rust/ares/Cargo.lock | 21 +++++ rust/ares/Cargo.toml | 24 +++--- rust/ares/src/interpreter.rs | 141 +++++++++++++++++++++------------- rust/ares/src/jets/nock.rs | 23 ++++-- rust/ares/src/lib.rs | 2 + rust/ares/src/serf.rs | 31 ++++++-- 7 files changed, 185 insertions(+), 83 deletions(-) diff --git a/hoon/scaffolding/toddler.hoon b/hoon/scaffolding/toddler.hoon index 79be6f9..9eb8d31 100644 --- a/hoon/scaffolding/toddler.hoon +++ b/hoon/scaffolding/toddler.hoon @@ -35,7 +35,7 @@ :: %mean hint ~_ [%leaf "I am a %mean hint via ~_ from +wack"] :: %hela hint - :: ~> %hela + ~> %hela :: %memo hint ~+ ?~ m +(n) @@ -47,13 +47,23 @@ !. |= [m=@ud n=@ud] :: %hela hint - :: ~> %hela + ~> %hela :: %memo hint ~+ ?~ m +(n) ?~ n (wack (dec m) 1) (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) - ~> %slog.[0 leaf+(scow %ud (wack 1 1))] + ~> %slog.[0 leaf+(scow %ud (slow (bex 23)))] [~ ..poke] :: =/ buz ~> %mean.'pith: bad wasp' ;;(wasp card.ovo) ?+ -.buz - ~> %slog.[0 leaf+"default $wasp"] + ~> %slog.[0 leaf+(scow %ud (wack 1 1))] [~ ..poke] :: %crud - ~> %slog.[0 goof.buz] - [~ ..poke] + =/ tang tang.goof.buz + |- + ?~ tang + [~ ..poke] + ~> %slog.[0 -.tang] + $(tang +.tang) == -- :: diff --git a/rust/ares/Cargo.lock b/rust/ares/Cargo.lock index fb69b32..a99638f 100644 --- a/rust/ares/Cargo.lock +++ b/rust/ares/Cargo.lock @@ -20,11 +20,13 @@ dependencies = [ "either", "ibig", "intmap", + "lazy_static", "libc", "memmap", "murmur3", "num-derive", "num-traits", + "signal-hook", "static_assertions", ] @@ -569,6 +571,25 @@ dependencies = [ "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]] name = "static_assertions" version = "1.1.0" diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 88bd51a..23f7368 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -11,18 +11,20 @@ edition = "2018" [dependencies] 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" +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] cc = "1.0.79" diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 7f8c9f8..984ece0 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -7,10 +7,13 @@ use crate::mem::NockStack; use crate::newt::Newt; use crate::noun; use crate::noun::{tape, Atom, Cell, IndirectAtom, Noun, Slots, D, T}; +use crate::serf::TERMINATOR; use ares_macros::tas; use assert_no_alloc::assert_no_alloc; use bitvec::prelude::{BitSlice, Lsb0}; use either::Either::*; +use std::sync::atomic::Ordering; +use std::sync::Arc; crate::gdb!(); @@ -265,10 +268,12 @@ pub fn interpret( mut subject: Noun, formula: Noun, ) -> Result { + let terminator = Arc::clone(&TERMINATOR); let mut res: Noun = D(0); let mut cache = Hamt::::new(); // XX: Should this come after initial frame_push()? let virtual_frame = stack.get_frame_pointer(); + let virtual_trace = stack.get_mean_stack(); stack.frame_push(0); unsafe { @@ -323,6 +328,7 @@ pub fn interpret( res = noun; stack.pop::(); } else { + // Axis invalid for input Noun break Err(NockErr::Deterministic); } } @@ -330,37 +336,43 @@ pub fn interpret( res = once.noun; stack.pop::(); } - NockWork::Work2(mut vale) => match vale.todo { - Todo2::ComputeSubject => { - vale.todo = Todo2::ComputeFormula; - *stack.top() = NockWork::Work2(vale); - push_formula(stack, vale.subject, false)?; + NockWork::Work2(mut vale) => { + if (*terminator).load(Ordering::Relaxed) { + break Err(NockErr::NonDeterministic); } - 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::(); - subject = vale.subject; - push_formula(stack, res, true)?; - } else { - vale.todo = Todo2::RestoreSubject; - std::mem::swap(&mut vale.subject, &mut subject); + + match vale.todo { + Todo2::ComputeSubject => { + vale.todo = Todo2::ComputeFormula; *stack.top() = NockWork::Work2(vale); - stack.frame_push(0); - *stack.push() = NockWork::Ret; - push_formula(stack, res, true)?; + push_formula(stack, vale.subject, false)?; + } + 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::(); + 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::(); } } - Todo2::RestoreSubject => { - subject = vale.subject; - stack.pop::(); - } - }, + } NockWork::Work3(mut thee) => match thee.todo { Todo3::ComputeChild => { thee.todo = Todo3::ComputeType; @@ -481,37 +493,43 @@ pub fn interpret( stack.pop::(); } }, - NockWork::Work9(mut kale) => match kale.todo { - Todo9::ComputeCore => { - kale.todo = Todo9::ComputeResult; - *stack.top() = NockWork::Work9(kale); - push_formula(stack, kale.core, false)?; + NockWork::Work9(mut kale) => { + if (*terminator).load(Ordering::Relaxed) { + break Err(NockErr::NonDeterministic); } - Todo9::ComputeResult => { - if let Ok(formula) = res.slot_atom(kale.axis) { - if kale.tail { - stack.pop::(); - subject = res; - push_formula(stack, formula, true)?; + + match kale.todo { + Todo9::ComputeCore => { + kale.todo = Todo9::ComputeResult; + *stack.top() = NockWork::Work9(kale); + push_formula(stack, kale.core, false)?; + } + Todo9::ComputeResult => { + if let Ok(formula) = res.slot_atom(kale.axis) { + if kale.tail { + stack.pop::(); + 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 { - 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)?; + // Axis into core must be atom + break Err(NockErr::Deterministic); } - } else { - // Axis into core must be atom - break Err(NockErr::Deterministic); + } + Todo9::RestoreSubject => { + subject = kale.core; + stack.pop::(); } } - Todo9::RestoreSubject => { - subject = kale.core; - stack.pop::(); - } - }, + } NockWork::Work10(mut diet) => { match diet.todo { Todo10::ComputeTree => { @@ -623,7 +641,13 @@ pub fn interpret( match nock { 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, cache: &mut Hamt, virtual_frame: *const u64, + virtual_trace: Noun, error: NockErr, ) -> Tone { let mut trace = stack.get_mean_stack(); @@ -839,6 +864,9 @@ pub fn exit_early( stack.preserve(cache); stack.frame_pop(); } + while !stack.get_mean_stack().raw_equals(virtual_trace) { + stack.trace_pop(); + } }; Tone::Error(error, trace) } @@ -1026,6 +1054,11 @@ fn match_hint_pre_nock( Ok(None) } 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)?]); stack.trace_push(trace); Ok(None) diff --git a/rust/ares/src/jets/nock.rs b/rust/ares/src/jets/nock.rs index 4650b98..322b7e7 100644 --- a/rust/ares/src/jets/nock.rs +++ b/rust/ares/src/jets/nock.rs @@ -10,7 +10,7 @@ use crate::noun::{Noun, D, T}; 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( stack: &mut NockStack, newt: &mut Option<&mut Newt>, @@ -23,6 +23,7 @@ pub fn jet_mink( let v_formula = slot(arg, 5)?; 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) { Ok(res) => Ok(T(stack, &[D(0), res])), Err(err) => match err { @@ -61,11 +62,6 @@ pub mod util { let tag = tone.head().as_direct()?; let original_list = tone.tail(); - // if tag.data() < 2 { - // return Ok(tone); - // } else if tag.data() > 2 { - // return Err(JetErr::Deterministic); - // } match tag.data() { x if x < 2 => return Ok(tone), x if x > 2 => return Err(JetErr::Deterministic), @@ -260,6 +256,21 @@ pub mod util { mod tests { use super::*; 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] fn test_mink_success() { diff --git a/rust/ares/src/lib.rs b/rust/ares/src/lib.rs index 5ae5ba2..23e67e1 100644 --- a/rust/ares/src/lib.rs +++ b/rust/ares/src/lib.rs @@ -1,5 +1,7 @@ extern crate num_derive; #[macro_use] +extern crate lazy_static; +#[macro_use] extern crate static_assertions; pub mod interpreter; pub mod jets; diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index dc76449..92dfe68 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -7,12 +7,14 @@ use crate::newt::Newt; use crate::noun::{Cell, Noun, Slots, D, T}; use crate::snapshot::{self, Snapshot}; use ares_macros::tas; +use signal_hook; +use signal_hook::consts::SIGINT; use std::fs::create_dir_all; use std::io; use std::path::PathBuf; use std::result::Result; -use std::thread::sleep; -use std::time; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; crate::gdb!(); @@ -23,12 +25,20 @@ const POKE_AXIS: u64 = 23; #[allow(dead_code)] const WISH_AXIS: u64 = 10; +// Necessary because Arc::new is not const +lazy_static! { + pub static ref TERMINATOR: Arc = Arc::new(AtomicBool::new(false)); +} + /** * 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. */ 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() .nth(2) .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 // possible, without rendering stack trace or injecting crud event. See // c3__evil in vere. - // + + clear_interrupt(); + // crud = [+(now) [%$ %arvo ~] [%crud goof ovo]] let job_cell = job.as_cell().expect("serf: work: job not a cell"); let now = inc( @@ -197,6 +209,8 @@ pub fn serf() -> io::Result<()> { } _ => panic!("got message with unknown tag {}", tag), }; + + clear_interrupt(); } Ok(()) @@ -231,8 +245,9 @@ pub fn soft( let tang = mook(stack, &mut Some(newt), tone, false) .expect("serf: soft: +mook crashed on bail") .tail(); - // TODO: shouldn't always be exit, look at the comment by u3m_bail to see how vere - // handles this + // XX: noun::Tone or noun::NockErr should use a bail enum system similar to u3m_bail motes; + // 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]); Err(goof) } @@ -244,3 +259,7 @@ fn slot(noun: Noun, axis: u64) -> io::Result { noun.slot(axis) .map_err(|_e| io::Error::new(io::ErrorKind::InvalidInput, "Bad axis")) } + +fn clear_interrupt() { + (*TERMINATOR).store(false, Ordering::Relaxed); +}