From e9e97fe82a059f4f22133aa638b71509b35a93bb Mon Sep 17 00:00:00 2001 From: Skgland Date: Wed, 19 Nov 2025 22:08:42 +0100 Subject: [PATCH] handle machine heap/stack allocation error --- src/arena.rs | 11 +++--- src/atom_table.rs | 16 ++++++--- src/functor_macro.rs | 2 +- src/machine/attributed_variables.rs | 14 +++++--- src/machine/copier.rs | 30 +++++++++------- src/machine/dispatch.rs | 27 +++++++++++---- src/machine/heap.rs | 45 ++++++++++++++++-------- src/machine/lib_machine/mod.rs | 15 +++++--- src/machine/loader.rs | 4 +-- src/machine/machine_errors.rs | 9 ++--- src/machine/machine_state.rs | 17 ++++----- src/machine/machine_state_impl.rs | 15 ++++---- src/machine/mock_wam.rs | 6 ++-- src/machine/mod.rs | 17 ++++++--- src/machine/stack.rs | 54 ++++++++++++++--------------- src/machine/system_calls.rs | 50 ++++++++++++++++---------- src/macros.rs | 8 ++--- src/offset_table.rs | 25 +++++++------ src/raw_block.rs | 44 ++++++++++------------- src/read.rs | 6 ++-- 20 files changed, 245 insertions(+), 170 deletions(-) diff --git a/src/arena.rs b/src/arena.rs index 09c52728..c27ed5f4 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -3,6 +3,7 @@ #[cfg(feature = "http")] use crate::http::{HttpListener, HttpResponse}; +use crate::machine::heap::AllocError; use crate::machine::loader::LiveLoadState; use crate::machine::streams::*; use crate::offset_table::*; @@ -486,12 +487,12 @@ unsafe impl Sync for Arena {} #[allow(clippy::new_without_default)] impl Arena { #[inline] - pub fn new() -> Self { - Arena { + pub fn new() -> Result { + Ok(Arena { base: None, - f64_tbl: F64Table::new(), - code_index_tbl: CodeIndexTable::new(), - } + f64_tbl: F64Table::new()?, + code_index_tbl: CodeIndexTable::new()?, + }) } } diff --git a/src/atom_table.rs b/src/atom_table.rs index cb4e5230..d9e970db 100644 --- a/src/atom_table.rs +++ b/src/atom_table.rs @@ -1,6 +1,7 @@ #![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work #![allow(unused_parens)] // see mthom/scryer-prolog#3092 and rust-lang/rust#147126 +use crate::machine::heap::AllocError; use crate::parser::ast::MAX_ARITY; use crate::raw_block::*; use crate::types::*; @@ -437,21 +438,21 @@ impl InnerAtomTable { impl AtomTable { #[inline] - pub fn new() -> Arc { + pub fn new() -> Result, AllocError> { let upgraded = global_atom_table().read().unwrap().upgrade(); // don't inline upgraded, otherwise temporary will be dropped too late in case of None if let Some(atom_table) = upgraded { - atom_table + Ok(atom_table) } else { let mut guard = global_atom_table().write().unwrap(); // try to upgrade again in case we lost the race on the write lock if let Some(atom_table) = guard.upgrade() { - atom_table + Ok(atom_table) } else { let atom_table = Arc::new(Self { inner: Arcu::new( InnerAtomTable { - block: RawBlock::new(), + block: RawBlock::new()?, table: Arcu::new(IndexSet::new(), GlobalEpochCounterPool), }, GlobalEpochCounterPool, @@ -459,11 +460,16 @@ impl AtomTable { update: Mutex::new(()), }); *guard = Arc::downgrade(&atom_table); - atom_table + Ok(atom_table) } } } + #[inline] + pub fn retrieve() -> Arc { + global_atom_table().read().unwrap().upgrade().unwrap() + } + pub fn active_table(&self) -> RcuRef, IndexSet> { self.inner.read().table.read() } diff --git a/src/functor_macro.rs b/src/functor_macro.rs index 88e5a5a2..82d6d98e 100644 --- a/src/functor_macro.rs +++ b/src/functor_macro.rs @@ -551,7 +551,7 @@ mod tests { #[test] fn inlined_atoms() { - let atom_table = AtomTable::new(); + let atom_table = AtomTable::new().unwrap(); let inlined = AtomTable::build_with(&atom_table, "inline"); assert!(inlined.is_inlined()); diff --git a/src/machine/attributed_variables.rs b/src/machine/attributed_variables.rs index 4aaf45ee..f1b5ba6c 100644 --- a/src/machine/attributed_variables.rs +++ b/src/machine/attributed_variables.rs @@ -54,7 +54,9 @@ impl MachineState { self.attr_var_init.bindings.push((h, addr)); } - fn populate_var_and_value_lists(&mut self) -> Result<(HeapCellValue, HeapCellValue), usize> { + fn populate_var_and_value_lists( + &mut self, + ) -> Result<(HeapCellValue, HeapCellValue), AllocError> { let size = self.attr_var_init.bindings.len(); let iter = self @@ -70,7 +72,7 @@ impl MachineState { Ok((var_list_addr, value_list_addr)) } - fn verify_attributes(&mut self) -> Result<(), usize> { + fn verify_attributes(&mut self) -> Result<(), AllocError> { for (h, _) in &self.attr_var_init.bindings { self.heap[*h] = attr_var_as_cell!(*h); } @@ -110,8 +112,12 @@ impl MachineState { attr_vars } - pub(super) fn verify_attr_interrupt(&mut self, p: usize, arity: usize) -> Result<(), usize> { - self.allocate(arity + 3); + pub(super) fn verify_attr_interrupt( + &mut self, + p: usize, + arity: usize, + ) -> Result<(), AllocError> { + self.allocate(arity + 3)?; let e = self.e; let and_frame = self.stack.index_and_frame_mut(e); diff --git a/src/machine/copier.rs b/src/machine/copier.rs index 965902df..42359806 100644 --- a/src/machine/copier.rs +++ b/src/machine/copier.rs @@ -83,16 +83,16 @@ pub trait CopierTarget: IndexMut { fn threshold(&self) -> usize; // returns the tail location of the pstr on success fn as_slice_from<'a>(&'a self, from: usize) -> Box + 'a>; - fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result; - fn reserve(&mut self, num_cells: usize) -> Result, usize>; - fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), usize>; + fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result; + fn reserve(&mut self, num_cells: usize) -> Result, AllocError>; + fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), AllocError>; } pub(crate) fn copy_term( target: T, addr: HeapCellValue, attr_var_policy: AttrVarPolicy, -) -> Result { +) -> Result { let mut copy_term_state = CopyTermState::new(target, attr_var_policy); let old_threshold = copy_term_state.target.threshold(); @@ -149,7 +149,7 @@ impl CopyTermState { self.trail.push((TrailRef::heap_cell(addr), trail_item)); } - fn copy_list(&mut self, addr: usize) -> Result<(), usize> { + fn copy_list(&mut self, addr: usize) -> Result<(), AllocError> { for offset in 0..2 { read_heap_cell!(self.target[addr + offset], (HeapCellValueTag::Lis, h) => { @@ -194,7 +194,7 @@ impl CopyTermState { Ok(()) } - fn copy_partial_string(&mut self, pstr_loc: usize) -> Result<(), usize> { + fn copy_partial_string(&mut self, pstr_loc: usize) -> Result<(), AllocError> { match self.pstr_loc_locs.range_mut(..=pstr_loc).next_back() { Some(( _prev_pstr_loc, @@ -281,7 +281,7 @@ impl CopyTermState { Ok(()) } - fn copy_attr_var_lists(&mut self) -> Result<(), usize> { + fn copy_attr_var_lists(&mut self) -> Result<(), AllocError> { while !self.attr_var_list_locs.is_empty() { let mut list_loc_vec = std::mem::take(&mut self.attr_var_list_locs); @@ -300,7 +300,7 @@ impl CopyTermState { * structure which is ensured by this function and not at all by * the vanilla copier. */ - fn copy_attr_var_list(&mut self, mut list_addr: HeapCellValue) -> Result<(), usize> { + fn copy_attr_var_list(&mut self, mut list_addr: HeapCellValue) -> Result<(), AllocError> { while let HeapCellValueTag::Lis = list_addr.get_tag() { let threshold = self.target.threshold(); let heap_loc = list_addr.get_value() as usize; @@ -330,7 +330,11 @@ impl CopyTermState { Ok(()) } - fn reinstantiate_var(&mut self, addr: HeapCellValue, frontier: usize) -> Result<(), usize> { + fn reinstantiate_var( + &mut self, + addr: HeapCellValue, + frontier: usize, + ) -> Result<(), AllocError> { read_heap_cell!(addr, (HeapCellValueTag::Var, h) => { self.target[frontier] = heap_loc_as_cell!(frontier); @@ -381,7 +385,7 @@ impl CopyTermState { Ok(()) } - fn copy_var(&mut self, addr: HeapCellValue) -> Result<(), usize> { + fn copy_var(&mut self, addr: HeapCellValue) -> Result<(), AllocError> { let index = addr.get_value() as usize; let rd = self.target.deref(addr); let ra = self.target.store(rd); @@ -421,7 +425,7 @@ impl CopyTermState { Ok(()) } - fn copy_structure(&mut self, addr: usize) -> Result<(), usize> { + fn copy_structure(&mut self, addr: usize) -> Result<(), AllocError> { read_heap_cell!(self.target[addr], (HeapCellValueTag::Atom, (_name, arity)) => { let threshold = self.target.threshold(); @@ -464,7 +468,7 @@ impl CopyTermState { Ok(()) } - fn copy_term_impl(&mut self, addr: HeapCellValue) -> Result<(), usize> { + fn copy_term_impl(&mut self, addr: HeapCellValue) -> Result<(), AllocError> { self.scan = self.target.threshold(); let mut writer = self.target.reserve(1)?; @@ -503,7 +507,7 @@ impl CopyTermState { Ok(()) } - fn copy_pstrs(&mut self) -> Result<(), usize> { + fn copy_pstrs(&mut self) -> Result<(), AllocError> { while let Some((least_pstr_loc, pstr_data)) = self.pstr_loc_locs.pop_first() { let threshold = heap_index!(self.target.threshold()); diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index fc13d825..d593bedf 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -1051,7 +1051,10 @@ impl Machine { }); self.machine_st.num_of_args += 1; - self.try_me_else(next_i); + backtrack_on_resource_error!( + self.machine_st, + self.try_me_else(next_i) + ); self.machine_st.num_of_args -= 1; } None => { @@ -1130,7 +1133,10 @@ impl Machine { ); self.machine_st.num_of_args += 1; - self.try_me_else(next_i); + backtrack_on_resource_error!( + self.machine_st, + self.try_me_else(next_i) + ); self.machine_st.num_of_args -= 1; } None => { @@ -1183,7 +1189,7 @@ impl Machine { } } &Instruction::TryMeElse(offset) => { - self.try_me_else(offset); + backtrack_on_resource_error!(self.machine_st, self.try_me_else(offset)); } &Instruction::DefaultRetryMeElse(offset) => { self.retry_me_else(offset); @@ -1265,7 +1271,10 @@ impl Machine { self.machine_st.p += 1; } &Instruction::Allocate(num_cells) => { - self.machine_st.allocate(num_cells); + backtrack_on_resource_error!( + self.machine_st, + self.machine_st.allocate(num_cells) + ); } &Instruction::DefaultCallAcyclicTerm => { let addr = self.deref_register(1); @@ -3155,7 +3164,10 @@ impl Machine { IndexingLine::IndexedChoice(ref indexed_choice) => { match indexed_choice[self.machine_st.iip as usize] { IndexedChoiceInstruction::Try(offset) => { - self.indexed_try(offset); + backtrack_on_resource_error!( + self.machine_st, + self.indexed_try(offset) + ); } IndexedChoiceInstruction::Retry(l) => { self.retry(l); @@ -3208,7 +3220,10 @@ impl Machine { ); self.machine_st.num_of_args += 1; - self.indexed_try(offset); + backtrack_on_resource_error!( + self.machine_st, + self.indexed_try(offset) + ); self.machine_st.num_of_args -= 1; } None => { diff --git a/src/machine/heap.rs b/src/machine/heap.rs index c6ca9871..964d983e 100644 --- a/src/machine/heap.rs +++ b/src/machine/heap.rs @@ -1,5 +1,6 @@ use crate::atom_table::*; use crate::functor_macro::*; +use crate::machine::machine_errors::CompilationError; use crate::machine::{ArenaHeaderTag, Fixnum, Integer}; use crate::types::*; @@ -11,6 +12,19 @@ use std::sync::Once; const ALIGN: usize = Heap::heap_cell_alignment(); +#[derive(Debug, Clone)] +pub struct AllocError; + +impl AllocError { + pub(crate) fn to_compilation_error(&self, heap: &mut Heap) -> CompilationError { + CompilationError::FiniteMemoryInHeap(self.resource_error_offset(heap)) + } + + pub(crate) fn resource_error_offset(&self, heap: &mut Heap) -> usize { + heap.resource_error_offset() + } +} + #[derive(Debug)] pub struct Heap { inner: InnerHeap, @@ -612,7 +626,7 @@ impl Heap { } } - pub fn reserve(&mut self, num_cells: usize) -> Result, usize> { + pub fn reserve(&mut self, num_cells: usize) -> Result, AllocError> { let section; let len = heap_index!(num_cells); @@ -625,7 +639,7 @@ impl Heap { }; break; } else if !self.grow() { - return Err(self.resource_error_offset()); + return Err(AllocError); } } } @@ -649,7 +663,7 @@ impl Heap { } } - pub(crate) fn append(&mut self, other_heap: &impl SizedHeap) -> Result<(), usize> { + pub(crate) fn append(&mut self, other_heap: &impl SizedHeap) -> Result<(), AllocError> { let other_len = heap_index!(other_heap.cell_len()); loop { @@ -665,7 +679,7 @@ impl Heap { self.inner.byte_len += heap_index!(other_heap.cell_len()); break; } else if unsafe { !self.grow() } { - return Err(self.resource_error_offset()); + return Err(AllocError); } } @@ -742,10 +756,10 @@ impl Heap { // either succeed & return nothing or fail & return an offset into // the heap to a pre-allocated resource error - pub(crate) fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), usize> { + pub(crate) fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), AllocError> { unsafe { if self.inner.byte_len == self.inner.byte_cap && !self.grow() { - return Err(self.resource_error_offset()); + return Err(AllocError); } // SAFETY: @@ -779,7 +793,7 @@ impl Heap { Range { start, end } } - pub fn allocate_pstr(&mut self, src: &str) -> Result { + pub fn allocate_pstr(&mut self, src: &str) -> Result { let size_in_heap = Self::compute_pstr_size(src); let mut writer = self.reserve(size_in_heap)?; let HeapSectionWriteResult { result, .. } = @@ -794,7 +808,7 @@ impl Heap { // note that allocate_cstr emits a tail cell to the string (completing it with the empty list) // unlike any version of allocate_pstr. - pub fn allocate_cstr(&mut self, src: &str) -> Result { + pub fn allocate_cstr(&mut self, src: &str) -> Result { let size_in_heap = Self::compute_pstr_size(src); let mut writer = self.reserve(size_in_heap + 1)?; let HeapSectionWriteResult { result, .. } = @@ -849,7 +863,7 @@ impl Heap { // copies only the string, not its tail. returns the cell index of // the tail location - pub(crate) fn copy_pstr_within(&mut self, pstr_loc: usize) -> Result { + pub(crate) fn copy_pstr_within(&mut self, pstr_loc: usize) -> Result { let HeapStringScan { string, tail_idx } = self.scan_slice_to_str(pstr_loc); let s_len = string.len(); @@ -884,7 +898,7 @@ impl Heap { break; } else if !self.grow() { - return Err(self.resource_error_offset()); + return Err(AllocError); } } } @@ -893,7 +907,10 @@ impl Heap { } // src is a cell-indexed range. - pub(crate) fn copy_slice_to_end>(&mut self, src: R) -> Result<(), usize> { + pub(crate) fn copy_slice_to_end>( + &mut self, + src: R, + ) -> Result<(), AllocError> { let range = self.slice_range(src); let len = range.end - range.start; @@ -911,7 +928,7 @@ impl Heap { break; } else if !self.grow() { - return Err(self.resource_error_offset()); + return Err(AllocError); } } } @@ -970,7 +987,7 @@ impl Heap { pub(crate) fn functor_writer( functor: Vec, - ) -> impl FnMut(&mut Heap) -> Result { + ) -> impl FnMut(&mut Heap) -> Result { let size = Heap::compute_functor_byte_size(&functor); let mut functor_writer = ReservedHeapSection::functor_writer(functor); @@ -1133,7 +1150,7 @@ pub fn sized_iter_to_heap_list>( heap: &mut Heap, size: usize, values: impl Iterator, -) -> Result { +) -> Result { if size > 0 { let h = heap.cell_len(); let mut writer = heap.reserve(1 + 2 * size)?; diff --git a/src/machine/lib_machine/mod.rs b/src/machine/lib_machine/mod.rs index 810c607d..b4409aa5 100644 --- a/src/machine/lib_machine/mod.rs +++ b/src/machine/lib_machine/mod.rs @@ -4,6 +4,7 @@ use std::rc::Rc; use crate::atom_table; use crate::heap_iter::{stackful_post_order_iter, NonListElider}; +use crate::machine::heap::AllocError; use crate::machine::machine_indices::VarKey; use crate::machine::mock_wam::CompositeOpDir; use crate::machine::{ @@ -445,14 +446,15 @@ impl Iterator for QueryState<'_> { // contained in self.machine_st.ball. let h = machine.machine_st.heap.cell_len(); - if let Err(resource_err_loc) = machine + if let Err(err) = machine .machine_st .heap .append(&machine.machine_st.ball.stub) { + let resource_error_offset = err.resource_error_offset(&mut machine.machine_st.heap); return Some(Err(Term::from_heapcell( machine, - machine.machine_st.heap[resource_err_loc], + machine.machine_st.heap[resource_error_offset], &mut IndexMap::new(), ))); } @@ -553,11 +555,11 @@ impl Machine { self.run_module_predicate(atom!("loader"), (atom!("consult_stream"), 2)); } - pub(crate) fn allocate_stub_choice_point(&mut self) { + pub(crate) fn allocate_stub_choice_point(&mut self) -> Result<(), AllocError> { // NOTE: create a choice point to terminate the dispatch_loop // if an exception is thrown. - let stub_b = self.machine_st.stack.allocate_or_frame(0); + let stub_b = self.machine_st.stack.allocate_or_frame(0)?; let or_frame = self.machine_st.stack.index_or_frame_mut(stub_b); or_frame.prelude.num_cells = 0; @@ -575,6 +577,8 @@ impl Machine { self.machine_st.b = stub_b; self.machine_st.hb = self.machine_st.heap.cell_len(); self.machine_st.block = stub_b; + + Ok(()) } /// Runs a query. @@ -588,7 +592,8 @@ impl Machine { .read_term(&op_dir, Tokens::Default) .expect("Failed to parse query"); - self.allocate_stub_choice_point(); + self.allocate_stub_choice_point() + .expect("failed to allocate stub choice point"); // Write parsed term to heap let term_write_result = write_term_to_heap(&term, &mut self.machine_st.heap) diff --git a/src/machine/loader.rs b/src/machine/loader.rs index fded231b..1ad46c88 100644 --- a/src/machine/loader.rs +++ b/src/machine/loader.rs @@ -2359,8 +2359,8 @@ impl Machine { let mut writer = match self.machine_st.heap.reserve(3 + meta_specs.len()) { Ok(writer) => writer, - Err(err_loc) => { - self.machine_st.throw_resource_error(err_loc); + Err(err) => { + self.machine_st.throw_resource_error(err); return; } }; diff --git a/src/machine/machine_errors.rs b/src/machine/machine_errors.rs index 9b56b4fb..4df767bf 100644 --- a/src/machine/machine_errors.rs +++ b/src/machine/machine_errors.rs @@ -6,6 +6,7 @@ use crate::parser::ast::*; use crate::ffi::{self, FfiError}; use crate::forms::*; use crate::functor_macro::*; +use crate::machine::heap::AllocError; use crate::machine::heap::*; use crate::machine::loader::CompilationTarget; use crate::machine::machine_state::*; @@ -763,8 +764,8 @@ impl MachineState { } // throw an error pre-allocated in the heap - pub(super) fn throw_resource_error(&mut self, err_loc: usize) { - self.registers[1] = str_loc_as_cell!(err_loc); + pub(super) fn throw_resource_error(&mut self, err: AllocError) { + self.registers[1] = str_loc_as_cell!(err.resource_error_offset(&mut self.heap)); self.set_ball(); self.unwind_stack(); } @@ -777,8 +778,8 @@ impl MachineState { self.registers[1] = match writer(&mut self.heap) { Ok(loc) => loc, - Err(resource_err_loc) => { - self.throw_resource_error(resource_err_loc); + Err(err) => { + self.throw_resource_error(err); return; } }; diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index ca88a6ea..b915e3a1 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -5,6 +5,7 @@ use crate::heap_iter::*; use crate::heap_print::*; use crate::machine::attributed_variables::*; use crate::machine::copier::*; +use crate::machine::heap::AllocError; use crate::machine::heap::*; use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; @@ -192,7 +193,7 @@ fn push_var_eq_functors<'a>( size: usize, iter: impl Iterator, atom_tbl: &AtomTable, -) -> Result { +) -> Result { let src_h = heap.cell_len(); let true_size = if size > 0 { @@ -257,7 +258,7 @@ impl Ball { self.stub.clear(); } - pub(super) fn copy_and_align_to(&self, dest: &mut Heap) -> Result { + pub(super) fn copy_and_align_to(&self, dest: &mut Heap) -> Result { let h = dest.cell_len(); let diff = self.boundary as i64 - h as i64; @@ -346,17 +347,17 @@ impl<'a> CopierTarget for CopyTerm<'a> { } #[inline(always)] - fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { + fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { self.state.heap.copy_pstr_within(pstr_loc) } #[inline(always)] - fn reserve(&mut self, num_cells: usize) -> Result, usize> { + fn reserve(&mut self, num_cells: usize) -> Result, AllocError> { self.state.heap.reserve(num_cells) } #[inline(always)] - fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), usize> { + fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), AllocError> { self.state.heap.copy_slice_to_end(bounds) } } @@ -455,7 +456,7 @@ impl<'a> CopierTarget for CopyBallTerm<'a> { self.stack } - fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { + fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { debug_assert!(pstr_loc < self.heap.byte_len()); let HeapStringScan { string, tail_idx } = self.heap.scan_slice_to_str(pstr_loc); @@ -477,11 +478,11 @@ impl<'a> CopierTarget for CopyBallTerm<'a> { } #[inline] - fn reserve(&mut self, num_cells: usize) -> Result, usize> { + fn reserve(&mut self, num_cells: usize) -> Result, AllocError> { self.stub.reserve(num_cells) } - fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), usize> { + fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), AllocError> { let len = bounds.end - bounds.start; let mut stub_writer = self.stub.reserve(len)?; diff --git a/src/machine/machine_state_impl.rs b/src/machine/machine_state_impl.rs index 04fd143f..a4de9f93 100644 --- a/src/machine/machine_state_impl.rs +++ b/src/machine/machine_state_impl.rs @@ -4,6 +4,7 @@ use crate::forms::*; use crate::heap_iter::*; use crate::machine::attributed_variables::*; use crate::machine::copier::*; +use crate::machine::heap::AllocError; use crate::machine::heap::*; use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; @@ -30,8 +31,8 @@ impl MachineState { heap.store_resource_error(); MachineState { - arena: Arena::new(), - atom_tbl: AtomTable::new(), + arena: Arena::new().unwrap(), + atom_tbl: AtomTable::new().unwrap(), pdl: Vec::with_capacity(1024), s: HeapPtr::default(), s_offset: 0, @@ -47,7 +48,7 @@ impl MachineState { fail: false, heap, mode: MachineMode::Write, - stack: Stack::new(), + stack: Stack::new().unwrap(), registers: [heap_loc_as_cell!(0); MAX_ARITY + 1], // self.registers[0] is never used. trail: vec![], tr: 0, @@ -174,8 +175,8 @@ impl MachineState { } } - pub fn allocate(&mut self, num_cells: usize) { - let e = self.stack.allocate_and_frame(num_cells); + pub fn allocate(&mut self, num_cells: usize) -> Result<(), AllocError> { + let e = self.stack.allocate_and_frame(num_cells)?; let and_frame = self.stack.index_and_frame_mut(e); and_frame.prelude.e = self.e; @@ -183,6 +184,8 @@ impl MachineState { self.e = e; self.p += 1; + + Ok(()) } pub fn bind(&mut self, r1: Ref, a2: HeapCellValue) { @@ -922,7 +925,7 @@ impl MachineState { name: Atom, arity: usize, r: Ref, - ) -> Result<(), usize> { + ) -> Result<(), AllocError> { let h = self.heap.cell_len(); let mut writer = self.heap.reserve(arity + 1)?; diff --git a/src/machine/mock_wam.rs b/src/machine/mock_wam.rs index df9dbe75..7fcc5613 100644 --- a/src/machine/mock_wam.rs +++ b/src/machine/mock_wam.rs @@ -168,7 +168,7 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> { } #[inline(always)] - fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { + fn copy_pstr_to_threshold(&mut self, pstr_loc: usize) -> Result { self.wam.machine_st.heap.copy_pstr_within(pstr_loc) } @@ -178,12 +178,12 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> { } #[inline(always)] - fn reserve(&mut self, num_cells: usize) -> Result, usize> { + fn reserve(&mut self, num_cells: usize) -> Result, AllocError> { self.wam.machine_st.heap.reserve(num_cells) } #[inline(always)] - fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), usize> { + fn copy_slice_to_end(&mut self, bounds: Range) -> Result<(), AllocError> { self.wam.machine_st.heap.copy_slice_to_end(bounds) } } diff --git a/src/machine/mod.rs b/src/machine/mod.rs index db6dae7e..5f581900 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -38,6 +38,7 @@ use crate::instructions::*; use crate::machine::args::*; use crate::machine::compile::*; use crate::machine::copier::*; +use crate::machine::heap::AllocError; use crate::machine::heap::*; use crate::machine::loader::*; use crate::machine::machine_errors::*; @@ -60,6 +61,7 @@ use std::cmp::Ordering; use std::env; use std::io::Read; use std::path::PathBuf; +use std::process::ExitCode; use std::sync::atomic::AtomicBool; use std::sync::OnceLock; @@ -261,7 +263,9 @@ impl Machine { let p = index_ptr.local().unwrap(); // Leave a halting choice point to backtrack to in case the predicate fails or throws. - self.allocate_stub_choice_point(); + if self.allocate_stub_choice_point().is_err() { + return ExitCode::FAILURE; + } self.machine_st.cp = BREAK_FROM_DISPATCH_LOOP_LOC; self.machine_st.p = p; @@ -729,10 +733,10 @@ impl Machine { } #[inline(always)] - pub(super) fn try_me_else(&mut self, offset: usize) { + pub(super) fn try_me_else(&mut self, offset: usize) -> Result<(), AllocError> { if let Some(offset) = self.next_applicable_clause(offset) { let n = self.machine_st.num_of_args; - let b = self.machine_st.stack.allocate_or_frame(n); + let b = self.machine_st.stack.allocate_or_frame(n)?; let or_frame = self.machine_st.stack.index_or_frame_mut(b); or_frame.prelude.num_cells = n; @@ -758,13 +762,15 @@ impl Machine { } self.machine_st.p += 1; + + Ok(()) } #[inline(always)] - pub(super) fn indexed_try(&mut self, offset: usize) { + pub(super) fn indexed_try(&mut self, offset: usize) -> Result<(), AllocError> { if let Some(iip_offset) = self.next_inner_applicable_clause() { let n = self.machine_st.num_of_args; - let b = self.machine_st.stack.allocate_or_frame(n); + let b = self.machine_st.stack.allocate_or_frame(n)?; let or_frame = self.machine_st.stack.index_or_frame_mut(b); or_frame.prelude.num_cells = n; @@ -793,6 +799,7 @@ impl Machine { } self.machine_st.p += offset; + Ok(()) } #[inline(always)] diff --git a/src/machine/stack.rs b/src/machine/stack.rs index 4247149f..98e1dab8 100644 --- a/src/machine/stack.rs +++ b/src/machine/stack.rs @@ -1,5 +1,7 @@ use core::marker::PhantomData; +use std::ptr::NonNull; +use crate::machine::heap::AllocError; use crate::raw_block::*; use crate::types::*; @@ -159,45 +161,42 @@ impl OrFrame { } impl Stack { - pub(crate) fn new() -> Self { - Stack { - buf: RawBlock::new(), + pub(crate) fn new() -> Result { + Ok(Stack { + buf: RawBlock::new()?, _marker: PhantomData, - } + }) } #[inline(always)] - unsafe fn alloc(&mut self, frame_size: usize) -> *mut u8 { + unsafe fn alloc(&mut self, frame_size: usize) -> Result, AllocError> { loop { let ptr = self.buf.alloc(frame_size); - - if ptr.is_null() { - if !self.buf.grow() { - panic!("growing the stack failed") - } - } else { - return ptr; + if let Some(ptr) = NonNull::new(ptr) { + return Ok(ptr); } + self.buf.grow()?; } } - pub(crate) fn allocate_and_frame(&mut self, num_cells: usize) -> usize { + pub(crate) fn allocate_and_frame(&mut self, num_cells: usize) -> Result { let frame_size = AndFrame::size_of(num_cells); unsafe { let e = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr(); - let new_ptr = self.alloc(frame_size); + let new_ptr = self.alloc(frame_size)?; let mut offset = prelude_size::(); for idx in 0..num_cells { - let cell_ptr = new_ptr.add(offset) as *mut HeapCellValue; - ptr::write(cell_ptr, stack_loc_as_cell!(AndFrame, e, idx + 1)); + let cell_ptr = new_ptr.add(offset).cast::(); + ptr::write(cell_ptr.as_ptr(), stack_loc_as_cell!(AndFrame, e, idx + 1)); // Because in the Index and IndexMut inplementations we need to get this from // exposed provenance, we need to expose the provenance here, even though we don't // actually use the value for anything. This is a reminder that `expose_provenance` // isn't just a cast from a pointer to an integer but has actual side effects. - cell_ptr.expose_provenance(); + // FIXME(msrv) remove the as_ptr() call once MSRV reaches 1.89.0 + cell_ptr.as_ptr().expose_provenance(); offset += mem::size_of::(); } @@ -205,7 +204,7 @@ impl Stack { let and_frame = self.index_and_frame_mut(e); and_frame.prelude.num_cells = num_cells; - e + Ok(e) } } @@ -213,23 +212,24 @@ impl Stack { unsafe { (*self.buf.ptr.get()).addr() - self.buf.base.addr() } } - pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> usize { + pub(crate) fn allocate_or_frame(&mut self, num_cells: usize) -> Result { let frame_size = OrFrame::size_of(num_cells); unsafe { let b = (*self.buf.ptr.get_mut()).addr() - self.buf.base.addr(); - let new_ptr = self.alloc(frame_size); + let new_ptr = self.alloc(frame_size)?; let mut offset = prelude_size::(); for idx in 0..num_cells { - let cell_ptr = new_ptr.byte_add(offset) as *mut HeapCellValue; - ptr::write(cell_ptr, stack_loc_as_cell!(OrFrame, b, idx)); + let cell_ptr = new_ptr.byte_add(offset).cast::(); + ptr::write(cell_ptr.as_ptr(), stack_loc_as_cell!(OrFrame, b, idx)); // Because in the Index and IndexMut inplementations we need to get this from // exposed provenance, we need to expose the provenance here, even though we don't // actually use the value for anything. This is a reminder that `expose_provenance` // isn't just a cast from a pointer to an integer but has actual side effects. - cell_ptr.expose_provenance(); + // FIXME(msrv) remove as_ptr() call once msrv reaches 1.89.0 + cell_ptr.as_ptr().expose_provenance(); offset += mem::size_of::(); } @@ -237,7 +237,7 @@ impl Stack { let or_frame = self.index_or_frame_mut(b); or_frame.prelude.num_cells = num_cells; - b + Ok(b) } } @@ -285,7 +285,7 @@ mod tests { fn stack_tests() { let mut wam = MockWAM::new(); - let e = wam.machine_st.stack.allocate_and_frame(10); // create an AND frame! + let e = wam.machine_st.stack.allocate_and_frame(10).unwrap(); // create an AND frame! let and_frame = wam.machine_st.stack.index_and_frame_mut(e); assert_eq!( @@ -303,7 +303,7 @@ mod tests { assert_eq!(and_frame[5], empty_list_as_cell!()); - let b = wam.machine_st.stack.allocate_or_frame(5); + let b = wam.machine_st.stack.allocate_or_frame(5).unwrap(); let or_frame = wam.machine_st.stack.index_or_frame_mut(b); @@ -311,7 +311,7 @@ mod tests { assert_eq!(or_frame[idx], stack_loc_as_cell!(OrFrame, b, idx)); } - let next_e = wam.machine_st.stack.allocate_and_frame(9); // create an AND frame! + let next_e = wam.machine_st.stack.allocate_and_frame(9).unwrap(); // create an AND frame! let and_frame = wam.machine_st.stack.index_and_frame_mut(next_e); for idx in 0..9 { diff --git a/src/machine/system_calls.rs b/src/machine/system_calls.rs index 4e1797a9..8cc5f83b 100644 --- a/src/machine/system_calls.rs +++ b/src/machine/system_calls.rs @@ -17,6 +17,7 @@ use crate::instructions::*; use crate::machine; use crate::machine::code_walker::*; use crate::machine::copier::*; +use crate::machine::heap::AllocError; use crate::machine::heap::*; use crate::machine::machine_errors::*; use crate::machine::machine_indices::*; @@ -635,7 +636,7 @@ impl MachineState { pub(crate) fn get_attr_var_list( &mut self, attr_var: HeapCellValue, - ) -> Result, usize> { + ) -> Result, AllocError> { read_heap_cell!(attr_var, (HeapCellValueTag::AttrVar, h) => { Ok(Some(h + 1)) @@ -903,7 +904,7 @@ impl MachineState { &mut self, lh_offset: usize, copy_target: HeapCellValue, - ) -> Result { + ) -> Result { let threshold = self.lifted_heap.cell_len() - lh_offset; let mut writer = self.lifted_heap.reserve(5)?; @@ -1041,7 +1042,7 @@ impl MachineState { &mut self, chunk: HeapCellValue, return_p: usize, - ) -> usize { + ) -> Result { let chunk = self.store(self.deref(chunk)); let s = chunk.get_value() as usize; @@ -1053,7 +1054,7 @@ impl MachineState { let cp = to_local_code_ptr(&self.heap, p_functor).unwrap(); let prev_e = self.e; - let e = self.stack.allocate_and_frame(num_cells); + let e = self.stack.allocate_and_frame(num_cells)?; let and_frame = self.stack.index_and_frame_mut(e); and_frame.prelude.e = prev_e; @@ -1084,7 +1085,7 @@ impl MachineState { } self.e = e; - self.p + Ok(self.p) } pub fn value_to_str_like(&mut self, value: HeapCellValue) -> Option { @@ -2458,7 +2459,14 @@ impl Machine { self.machine_st.p = return_p; for chunk in cont_chunks.into_iter().rev() { - return_p = self.machine_st.call_continuation_chunk(chunk, return_p); + match self.machine_st.call_continuation_chunk(chunk, return_p) { + Ok(ret_p) => { + return_p = ret_p; + } + Err(err) => { + self.machine_st.throw_resource_error(err); + } + } } Ok(()) @@ -4092,7 +4100,7 @@ impl Machine { fn write_op_functors_to_heap( heap: &mut Heap, op_descs: impl Iterator, - ) -> Result { + ) -> Result { let mut num_functors = 0; for (name, op_desc) in op_descs { @@ -5130,7 +5138,11 @@ impl Machine { } #[cfg(feature = "ffi")] - fn build_struct(&mut self, name: Atom, mut args: Vec) -> Result { + fn build_struct( + &mut self, + name: Atom, + mut args: Vec, + ) -> Result { args.insert(0, Value::CString(CString::new(&*name.as_str()).unwrap())); let cells: Vec<_> = args @@ -5150,7 +5162,7 @@ impl Machine { Value::Struct(name, struct_args) => self.build_struct(name, struct_args)?, }) }) - .collect::>()?; + .collect::>()?; sized_iter_to_heap_list(&mut self.machine_st.heap, cells.len(), cells.into_iter()) } @@ -7579,7 +7591,7 @@ impl Machine { false } - fn walk_code_at_ptr(&mut self, index_ptr: usize) -> Result { + fn walk_code_at_ptr(&mut self, index_ptr: usize) -> Result { let orig_h = self.machine_st.heap.cell_len(); let mut h = orig_h; @@ -8400,7 +8412,7 @@ impl Machine { } #[inline(always)] - pub(crate) fn load_html(&mut self) -> Result<(), usize> { + pub(crate) fn load_html(&mut self) -> Result<(), AllocError> { if let Some(string) = self .machine_st .value_to_str_like(self.machine_st.registers[1]) @@ -8429,7 +8441,7 @@ impl Machine { } #[inline(always)] - pub(crate) fn load_xml(&mut self) -> Result<(), usize> { + pub(crate) fn load_xml(&mut self) -> Result<(), AllocError> { if let Some(string) = self .machine_st .value_to_str_like(self.machine_st.registers[1]) @@ -8965,8 +8977,8 @@ impl Machine { Ok(loc) => { unify!(self.machine_st, status_r, loc); } - Err(resource_err_loc) => { - self.machine_st.throw_resource_error(resource_err_loc); + Err(err) => { + self.machine_st.throw_resource_error(err); } } Ok(()) @@ -8983,8 +8995,8 @@ impl Machine { Ok(loc) => { unify!(self.machine_st, status_r, loc); } - Err(resource_err_loc) => { - self.machine_st.throw_resource_error(resource_err_loc); + Err(err) => { + self.machine_st.throw_resource_error(err); } } Ok(()) @@ -9320,7 +9332,7 @@ impl Machine { pub(super) fn xml_node_to_term( &mut self, node: roxmltree::Node, - ) -> Result { + ) -> Result { if node.is_text() { self.machine_st.heap.allocate_cstr(node.text().unwrap()) } else { @@ -9371,7 +9383,7 @@ impl Machine { pub(super) fn html_node_to_term( &mut self, node: ego_tree::NodeRef<'_, scraper::Node>, - ) -> Result { + ) -> Result { match node.value() { scraper::Node::Document | scraper::Node::Fragment => { unreachable!("we never iterate the root itself only its children") @@ -9476,7 +9488,7 @@ impl Machine { } } - pub(super) fn u8s_to_string(&mut self, data: &[u8]) -> Result { + pub(super) fn u8s_to_string(&mut self, data: &[u8]) -> Result { let buffer = String::from_iter(data.iter().map(|b| *b as char)); if buffer.is_empty() { diff --git a/src/macros.rs b/src/macros.rs index 0bf7b6af..92e2179e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -451,8 +451,8 @@ macro_rules! step_or_resource_error { ($machine_st:expr, $val:expr) => {{ match $val { Ok(r) => r, - Err(err_loc) => { - $machine_st.throw_resource_error(err_loc); + Err(err) => { + $machine_st.throw_resource_error(err); return; } } @@ -460,8 +460,8 @@ macro_rules! step_or_resource_error { ($machine_st:expr, $val:expr, $fail:block) => {{ match $val { Ok(r) => r, - Err(err_loc) => { - $machine_st.throw_resource_error(err_loc); + Err(err) => { + $machine_st.throw_resource_error(err); $fail } } diff --git a/src/offset_table.rs b/src/offset_table.rs index 74967c60..ec3f6031 100644 --- a/src/offset_table.rs +++ b/src/offset_table.rs @@ -10,6 +10,7 @@ use fxhash::FxBuildHasher; use indexmap::IndexMap; use parking_lot::{Mutex, RwLock}; +use crate::machine::heap::AllocError; use crate::machine::machine_indices::IndexPtr; use crate::raw_block::RawBlock; use crate::raw_block::RawBlockTraits; @@ -58,8 +59,10 @@ impl From>> for OffsetTableImpl< impl OffsetTableImpl { #[inline(always)] - pub fn new() -> Self { - Self(InnerOffsetTableImpl::Serial(SerialOffsetTable::new())) + pub fn new() -> Result { + Ok(Self( + InnerOffsetTableImpl::Serial(SerialOffsetTable::new()?), + )) } #[must_use = "the returned concurrent table must be absorbed into the owned OffsetTable"] @@ -116,7 +119,7 @@ impl OffsetTableImpl { impl Default for OffsetTableImpl { fn default() -> Self { - Self::new() + Self::new().unwrap() } } @@ -201,10 +204,10 @@ impl OffsetTable for OffsetTableImpl { impl SerialOffsetTable { #[inline] - fn new() -> Self { - Self { - block: RawBlock::new(), - } + fn new() -> Result { + Ok(Self { + block: RawBlock::new()?, + }) } unsafe fn build_with(&mut self, value: T) -> usize { @@ -374,11 +377,11 @@ pub enum F64Table { } impl F64Table { - pub fn new() -> Self { - Self::Serial(SerialF64Table { + pub fn new() -> Result { + Ok(Self::Serial(SerialF64Table { indirection_tbl: IndexMap::with_hasher(FxBuildHasher::new()), - offset_tbl: SerialOffsetTable::new(), - }) + offset_tbl: SerialOffsetTable::new()?, + })) } pub fn build_with(&mut self, value: OrderedFloat) -> F64Offset { diff --git a/src/raw_block.rs b/src/raw_block.rs index 02bea9f0..64346883 100644 --- a/src/raw_block.rs +++ b/src/raw_block.rs @@ -4,6 +4,8 @@ use std::alloc; use std::cell::UnsafeCell; use std::ptr; +use crate::machine::heap::AllocError; + pub trait RawBlockTraits { fn init_size() -> usize; fn align() -> usize; @@ -29,65 +31,57 @@ impl RawBlock { } #[allow(clippy::new_without_default)] - pub fn new() -> Self { + pub fn new() -> Result { let mut block = Self::empty_block(); unsafe { - block.grow(); + block.grow()?; } - block + Ok(block) } - unsafe fn init_at_size(&mut self, cap: usize) { + unsafe fn init_at_size(&mut self, cap: usize) -> Result<(), AllocError> { let layout = alloc::Layout::from_size_align_unchecked(cap, T::align()); let new_base = alloc::alloc(layout).cast_const(); if new_base.is_null() { - panic!( - "failed to allocate in init_at_size for {}", - std::any::type_name::() - ); + return Err(AllocError); } self.base = new_base; self.top = self.base.add(cap); *self.ptr.get_mut() = self.base.cast_mut(); + Ok(()) } - pub unsafe fn grow(&mut self) -> bool { + pub unsafe fn grow(&mut self) -> Result<(), AllocError> { if self.base.is_null() { - self.init_at_size(T::init_size()); - true + self.init_at_size(T::init_size()) } else { let size = self.size(); let layout = alloc::Layout::from_size_align_unchecked(size, T::align()); let new_base = alloc::realloc(self.base.cast_mut(), layout, size * 2).cast_const(); if new_base.is_null() { - false + Err(AllocError) } else { self.base = new_base; self.top = self.base.add(size * 2); *self.ptr.get_mut() = self.base.add(size).cast_mut(); - true + Ok(()) } } } - pub unsafe fn grow_new(&self) -> Option { + pub unsafe fn grow_new(&self) -> Result { if self.base.is_null() { - Some(Self::new()) + Self::new() } else { let mut new_block = Self::empty_block(); - new_block.init_at_size(self.size() * 2); - if new_block.base.is_null() { - // allocation failed - None - } else { - let allocated = (*self.ptr.get()).addr() - self.base.addr(); - self.base.copy_to(new_block.base.cast_mut(), allocated); - *new_block.ptr.get_mut() = new_block.base.add(allocated).cast_mut(); - Some(new_block) - } + new_block.init_at_size(self.size() * 2)?; + let allocated = (*self.ptr.get()).addr() - self.base.addr(); + self.base.copy_to(new_block.base.cast_mut(), allocated); + *new_block.ptr.get_mut() = new_block.base.add(allocated).cast_mut(); + Ok(new_block) } } diff --git a/src/read.rs b/src/read.rs index e9603522..b8817aa6 100644 --- a/src/read.rs +++ b/src/read.rs @@ -381,7 +381,7 @@ impl<'a> TermWriter<'a> { fn push_cell(&mut self, cell: HeapCellValue) -> Result<(), CompilationError> { self.heap .push_cell(cell) - .map_err(CompilationError::FiniteMemoryInHeap) + .map_err(|err| err.to_compilation_error(self.heap)) } fn term_as_addr(&mut self, term: &TermRef, h: usize) -> HeapCellValue { @@ -478,7 +478,7 @@ impl<'a> TermWriter<'a> { let cell = self .heap .allocate_cstr(src) - .map_err(CompilationError::FiniteMemoryInHeap)?; + .map_err(|err| err.to_compilation_error(self.heap))?; let new_h = self.heap.cell_len(); self.push_cell(cell)?; @@ -499,7 +499,7 @@ impl<'a> TermWriter<'a> { let cell = self .heap .allocate_pstr(src) - .map_err(CompilationError::FiniteMemoryInHeap)?; + .map_err(|err| err.to_compilation_error(self.heap))?; let tail_h = self.heap.cell_len(); self.push_stub_addr()?; -- 2.54.0