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
~_ [%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)
==
--
::

21
rust/ares/Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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<Noun, Tone> {
let terminator = Arc::clone(&TERMINATOR);
let mut res: Noun = D(0);
let mut cache = Hamt::<Noun>::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::<NockWork>();
} else {
// Axis invalid for input Noun
break Err(NockErr::Deterministic);
}
}
@ -330,37 +336,43 @@ pub fn interpret(
res = once.noun;
stack.pop::<NockWork>();
}
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::<NockWork>();
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::<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 {
Todo3::ComputeChild => {
thee.todo = Todo3::ComputeType;
@ -481,37 +493,43 @@ pub fn interpret(
stack.pop::<NockWork>();
}
},
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::<NockWork>();
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::<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 {
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::<NockWork>();
}
}
Todo9::RestoreSubject => {
subject = kale.core;
stack.pop::<NockWork>();
}
},
}
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<Noun>,
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)

View File

@ -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() {

View File

@ -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;

View File

@ -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<AtomicBool> = 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> {
noun.slot(axis)
.map_err(|_e| io::Error::new(io::ErrorKind::InvalidInput, "Bad axis"))
}
fn clear_interrupt() {
(*TERMINATOR).store(false, Ordering::Relaxed);
}