From: Bennet Bleßmann Date: Mon, 28 Aug 2023 21:24:27 +0000 (+0200) Subject: [WIP] move towards lockless AtomTable X-Git-Tag: remove~112^2~2 X-Git-Url: https://git.sagredo.dev/?a=commitdiff_plain;h=01aeb7515d4b7b4f08de771db0f3de231d59518f;p=scryer-prolog.git [WIP] move towards lockless AtomTable --- diff --git a/src/arena.rs b/src/arena.rs index b2e43466..758285da 100644 --- a/src/arena.rs +++ b/src/arena.rs @@ -70,12 +70,6 @@ pub struct F64Table { block: RawBlock, } -impl Drop for F64Table { - fn drop(&mut self) { - self.block.deallocate(); - } -} - #[cfg(test)] fn set_f64_tbl_buf_base(ptr: *const u8) { F64_TABLE_BUF_BASE.with(|f64_table_buf_base| { diff --git a/src/atom_table.rs b/src/atom_table.rs index 523d8974..6ad7cc30 100644 --- a/src/atom_table.rs +++ b/src/atom_table.rs @@ -7,16 +7,16 @@ use std::hash::{Hash, Hasher}; use std::mem; use std::ops::Deref; use std::ptr; +use std::ptr::NonNull; use std::slice; use std::str; use std::sync::Arc; +use std::sync::Mutex; use std::sync::Weak; use indexmap::IndexSet; use modular_bitfield::prelude::*; -use tokio::runtime::Handle; -use tokio::sync::OwnedRwLockReadGuard; use tokio::sync::RwLock; #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -46,45 +46,35 @@ impl From for Atom { } } -impl indexmap::Equivalent for LookupKey<'_, '_> { +impl indexmap::Equivalent for str { fn equivalent(&self, key: &Atom) -> bool { - &*key.as_str_with_table(self.0) == self.1 + &*key.as_str() == self } } const ATOM_TABLE_INIT_SIZE: usize = 1 << 16; const ATOM_TABLE_ALIGN: usize = 8; -fn global_atom_table() -> &'static RwLock>> { +#[inline(always)] +fn global_atom_table() -> &'static RwLock> { #[cfg(feature = "rust_beta_channel")] { // const Weak::new will be stabilized in 1.73 which is currently in beta, // till then we need a OnceLock for initialization - static GLOBAL_ATOM_TABLE: RwLock>> = RwLock::const_new(Weak::new()); + static GLOBAL_ATOM_TABLE: RwLock> = RwLock::const_new(Weak::new()); &GLOBAL_ATOM_TABLE } #[cfg(not(feature = "rust_beta_channel"))] { use std::sync::OnceLock; - static GLOBAL_ATOM_TABLE: OnceLock>>> = OnceLock::new(); + static GLOBAL_ATOM_TABLE: OnceLock>> = OnceLock::new(); GLOBAL_ATOM_TABLE.get_or_init(|| RwLock::new(Weak::new())) } } -fn owned_atom_table_read_guard() -> Option> { - let atom_table = global_atom_table().blocking_read().upgrade()?; - - let guard = { - // some test don't start a Runtime - if let Ok(handle) = Handle::try_current() { - handle.block_on(atom_table.read_owned()) - } else { - tokio::runtime::Runtime::new() - .unwrap() - .block_on(atom_table.read_owned()) - } - }; - Some(guard) +#[inline(always)] +fn arc_atom_table() -> Option> { + global_atom_table().blocking_read().upgrade() } impl RawBlockTraits for AtomTable { @@ -132,7 +122,7 @@ macro_rules! is_char { pub enum AtomString<'a> { Static(&'a str), - Dynamic(OwnedRwLockReadGuard), + Dynamic(AtomTableRef), } impl AtomString<'_> { @@ -142,7 +132,7 @@ impl AtomString<'_> { { match self { Self::Static(reference) => Self::Static(f(reference)), - Self::Dynamic(guard) => Self::Dynamic(OwnedRwLockReadGuard::map(guard, f)), + Self::Dynamic(guard) => Self::Dynamic(AtomTableRef::map(guard, f)), } } } @@ -180,51 +170,28 @@ impl rustyline::completion::Candidate for AtomString<'_> { } impl Atom { - #[inline] - pub fn buf(self) -> Option> { - if let Some(guard) = self.as_ptr() { - Some(OwnedRwLockReadGuard::map(guard, |ptr| unsafe { - (ptr as *const u8) - .offset(mem::size_of::() as isize) - .as_ref() - .unwrap() - })) - } else { - None - } - } - #[inline(always)] pub fn is_static(self) -> bool { (self.index as usize) < STRINGS.len() << 3 } #[inline(always)] - pub fn as_ptr_with_table<'at>(&self, atom_table: &'at AtomTable) -> Option<&'at u8> { + pub fn as_ptr(self) -> Option> { if self.is_static() { None } else { + let atom_table = + arc_atom_table().expect("We should only have an Atom while there is an AtomTable"); unsafe { - atom_table - .buf() - .offset(((self.index as usize) - (STRINGS.len() << 3)) as isize) - .as_ref() + AtomTableRef::try_map(atom_table.buf(), |buf| { + (buf as *const u8) + .offset(((self.index as usize) - (STRINGS.len() << 3)) as isize) + .as_ref() + }) } } } - #[inline(always)] - pub fn as_ptr(self) -> Option> { - if self.is_static() { - None - } else { - let guard = owned_atom_table_read_guard() - .expect("We should only have an Atom while there is an AtomTable"); - OwnedRwLockReadGuard::try_map(guard, |atom_table| self.as_ptr_with_table(atom_table)) - .ok() - } - } - #[inline(always)] pub fn from(index: u64) -> Self { Self { index } @@ -260,35 +227,29 @@ impl Atom { } } - #[inline(always)] - pub fn as_str_with_table<'at>(&self, atom_table: &'at AtomTable) -> &'at str { - if let Some(ptr) = self.as_ptr_with_table(atom_table) { - let header = unsafe { ptr::read::(ptr as *const u8 as *const AtomHeader) }; - let len = header.len() as usize; - let buf = (unsafe { (ptr as *const u8).offset(mem::size_of::() as isize) }) - as *mut u8; - - unsafe { str::from_utf8_unchecked(slice::from_raw_parts(buf, len)) } - } else { - &STRINGS[(self.index >> 3) as usize] - } - } - - #[track_caller] #[inline] pub fn as_str(&self) -> AtomString<'static> { if self.is_static() { AtomString::Static(STRINGS[(self.index >> 3) as usize]) } else { - let guard = owned_atom_table_read_guard() - .expect("We should only have an Atom while there is an AtomTable"); - AtomString::Dynamic(OwnedRwLockReadGuard::map(guard, |atom_table| { - self.as_str_with_table(atom_table) - })) + if let Some(ptr) = self.as_ptr() { + AtomString::Dynamic(AtomTableRef::map(ptr, |ptr| { + let header = + unsafe { ptr::read::(ptr as *const u8 as *const AtomHeader) }; + let len = header.len() as usize; + let buf = (unsafe { + (ptr as *const u8).offset(mem::size_of::() as isize) + }) as *mut u8; + + unsafe { str::from_utf8_unchecked(slice::from_raw_parts(buf, len)) } + })) + } else { + AtomString::Static(&STRINGS[(self.index >> 3) as usize]) + } } } - pub fn defrock_brackets(&self, atom_tbl: &Arc>) -> Self { + pub fn defrock_brackets(&self, atom_tbl: &AtomTable) -> Self { let s = self.as_str(); let sub_str = if s.starts_with('(') && s.ends_with(')') { @@ -297,9 +258,7 @@ impl Atom { return *self; }; - let val = sub_str.to_string(); - drop(s); // wee need to drop s as it holds a read lock on the AtomTable and build_with may need to acquire a write lock - AtomTable::build_with(&atom_tbl, &val) + AtomTable::build_with(&atom_tbl, &sub_str) } } @@ -323,29 +282,80 @@ impl Ord for Atom { } } +pub struct AtomTableRef +where + M: ?Sized, +{ + arc: Arc, + data: NonNull, +} + +impl Clone for AtomTableRef { + fn clone(&self) -> Self { + Self { + arc: Arc::clone(&self.arc), + data: self.data, + } + } +} + +impl AtomTableRef { + pub fn map FnOnce(&'a M) -> &'a N>( + referece: Self, + f: F, + ) -> AtomTableRef { + AtomTableRef { + arc: referece.arc, + data: f(unsafe { referece.data.as_ref() }).into(), + } + } + + pub fn try_map FnOnce(&'a M) -> Option<&'a N>>( + referece: Self, + f: F, + ) -> Option> { + let val = f(unsafe { referece.data.as_ref() })?; + Some(AtomTableRef { + arc: Arc::clone(&referece.arc), + data: val.into(), + }) + } +} + +impl Deref for AtomTableRef { + type Target = M; + + fn deref(&self) -> &Self::Target { + unsafe { self.data.as_ref() } + } +} + #[derive(Debug)] -pub struct AtomTable { +pub struct InnerAtomTable { block: RawBlock, pub table: RwLock>, } -impl Drop for AtomTable { - fn drop(&mut self) { - self.block.deallocate(); - } +#[derive(Debug)] +pub struct AtomTable { + inner: RwLock>, + // this lock is taking during resizing + update: Mutex<()>, } -struct LookupKey<'table, 'key>(&'table AtomTable, &'key str); - -impl Hash for LookupKey<'_, '_> { - fn hash(&self, state: &mut H) { - self.1.hash(state); +impl InnerAtomTable { + #[inline(always)] + fn lookup_str(self: &InnerAtomTable, string: &str) -> Option { + STATIC_ATOMS_MAP + .get(string) + .cloned() + .or_else(|| self.table.blocking_read().get(string).cloned()) } } impl AtomTable { #[inline] - pub fn new() -> Arc> { + pub fn new() -> Arc { let upgraded = global_atom_table().blocking_read().upgrade(); // don't inline upgraded, otherwise temporary will be dropped too late in case of None if let Some(atom_table) = upgraded { @@ -356,84 +366,89 @@ impl AtomTable { if let Some(atom_table) = guard.upgrade() { atom_table } else { - let atom_table = Arc::new(RwLock::new(Self { - block: RawBlock::new(), - table: RwLock::new(IndexSet::new()), - })); + let atom_table = Arc::new(Self { + inner: RwLock::new(Arc::new(InnerAtomTable { + block: RawBlock::new(), + table: RwLock::new(IndexSet::new()), + })), + update: Mutex::new(()), + }); *guard = Arc::downgrade(&atom_table); atom_table } } } - #[inline] - pub fn buf(&self) -> *const u8 { - self.block.base as *const u8 + pub fn active_epoch(&self) -> AtomTableRef { + let arc = Arc::clone(&self.inner.blocking_read()); + AtomTableRef { + data: arc.deref().into(), + arc, + } } #[inline] - pub fn top(&self) -> *const u8 { - self.block.top - } - - #[inline(always)] - fn lookup_str(self: &AtomTable, string: &str) -> Option { - STATIC_ATOMS_MAP.get(string).cloned().or_else(|| { - self.table - .blocking_read() - .get(&LookupKey(self, string)) - .cloned() + pub fn buf(&self) -> AtomTableRef { + AtomTableRef::::map(self.active_epoch(), |inner| { + unsafe { inner.block.base.as_ref() }.unwrap() }) } - pub fn build_with(atom_table: &RwLock, string: &str) -> Atom { - let mut atom_table = loop { - // we can't just use blocking_write as tokio's RwLock is fair - // and we can't block readers here as otherwise we will deadlock, see the guard downgrade below - if let Ok(guard) = atom_table.try_write() { - break guard; + pub fn build_with(atom_table: &AtomTable, string: &str) -> Atom { + loop { + let mut epoch = atom_table.active_epoch(); + let count = epoch.table.blocking_read().len(); + + if let Some(atom) = epoch.lookup_str(string) { + return atom; } - }; - if let Some(atom) = atom_table.lookup_str(string) { - return atom; - } + let update_guard = atom_table.update.lock().unwrap(); + + let is_same_allocation = Arc::ptr_eq(&epoch.arc, &atom_table.active_epoch().arc); + let is_same_atom_count = count == epoch.table.blocking_read().len(); + + if !(is_same_allocation && is_same_atom_count) { + // some other thread raced us between our lookup and us aquring the update lock, try again + continue; + } - unsafe { let size = mem::size_of::() + string.len(); let align_offset = 8 * mem::align_of::(); let size = (size & !(align_offset - 1)) + align_offset; - let len_ptr = loop { - let ptr = atom_table.block.alloc(size); - - if ptr.is_null() { - atom_table.block.grow(); - } else { - break ptr; - } - }; + unsafe { + let len_ptr = loop { + let ptr = epoch.block.alloc(size); - let ptr_base = atom_table.block.base as usize; + if ptr.is_null() { + let new_block = epoch.block.grow_new().unwrap(); + let new_table = RwLock::new(epoch.table.blocking_read().clone()); + let new_alloc = Arc::new(InnerAtomTable { + block: new_block, + table: new_table, + }); + *atom_table.inner.blocking_write() = new_alloc; + epoch = atom_table.active_epoch(); + } else { + break ptr; + } + }; - write_to_ptr(string, len_ptr); + let ptr_base = epoch.block.base as usize; - let atom = Atom { - index: ((STRINGS.len() << 3) + len_ptr as usize - ptr_base) as u64, - }; + write_to_ptr(string, len_ptr); - // we need to downgrade to a read so that Atom::hash can read from the AtomTable, - // so that it can calculate the hash for inserting the atom - // we can't just drop the guard as otherwise another thread could race us with another atom insertion - let atom_table = atom_table.downgrade(); + let atom = Atom { + index: ((STRINGS.len() << 3) + len_ptr as usize - ptr_base) as u64, + }; - // NOTE: there is no race between downgrade and blocking write as table is only accessed writable in this function - // and only after the write lock is acquired as we have the guard and just convert it from a write to a read guard no writer can race us - atom_table.table.blocking_write().insert(atom); + epoch.table.blocking_write().insert(atom); - drop(atom_table); // we need to keep the guard around till after the insert + drop(update_guard); - atom + return atom; + } } } } diff --git a/src/codegen.rs b/src/codegen.rs index 65dd7817..e4e75006 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -18,8 +18,6 @@ use crate::machine::machine_errors::*; use fxhash::FxBuildHasher; use indexmap::IndexSet; -use tokio::sync::RwLock; - use std::cell::Cell; use std::collections::VecDeque; @@ -264,7 +262,7 @@ impl CodeGenSettings { #[derive(Debug)] pub(crate) struct CodeGenerator<'a> { - pub(crate) atom_tbl: &'a RwLock, + pub(crate) atom_tbl: &'a AtomTable, marker: DebrayAllocator, settings: CodeGenSettings, pub(crate) skeleton: PredicateSkeleton, @@ -364,7 +362,7 @@ fn structure_cell(term: &Term) -> Option<&Cell> { } impl<'b> CodeGenerator<'b> { - pub(crate) fn new(atom_tbl: &'b RwLock, settings: CodeGenSettings) -> Self { + pub(crate) fn new(atom_tbl: &'b AtomTable, settings: CodeGenSettings) -> Self { CodeGenerator { atom_tbl, marker: DebrayAllocator::new(), diff --git a/src/forms.rs b/src/forms.rs index 5f2a6ec7..9280e535 100644 --- a/src/forms.rs +++ b/src/forms.rs @@ -16,8 +16,6 @@ use fxhash::FxBuildHasher; use indexmap::{IndexMap, IndexSet}; use ordered_float::OrderedFloat; -use tokio::sync::RwLock; - use std::cell::Cell; use std::collections::VecDeque; use std::convert::TryFrom; @@ -483,7 +481,7 @@ pub enum AtomOrString { impl AtomOrString { #[inline] - pub fn as_atom(&self, atom_tbl: &RwLock) -> Atom { + pub fn as_atom(&self, atom_tbl: &AtomTable) -> Atom { match self { &AtomOrString::Atom(atom) => atom, AtomOrString::String(string) => AtomTable::build_with(atom_tbl, &string), diff --git a/src/heap_print.rs b/src/heap_print.rs index c6e81da5..9880a4d1 100644 --- a/src/heap_print.rs +++ b/src/heap_print.rs @@ -24,8 +24,6 @@ use ordered_float::OrderedFloat; use indexmap::IndexMap; -use tokio::sync::RwLock; - use std::cell::Cell; use std::convert::TryFrom; use std::iter::once; @@ -483,7 +481,7 @@ pub fn fmt_float(mut fl: f64) -> String { pub struct HCPrinter<'a, Outputter> { outputter: Outputter, iter: StackfulPreOrderHeapIter<'a>, - atom_tbl: Arc>, + atom_tbl: Arc, op_dir: &'a OpDir, state_stack: Vec, toplevel_spec: Option, @@ -552,7 +550,7 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> { pub fn new( heap: &'a mut Heap, - atom_tbl: Arc>, + atom_tbl: Arc, stack: &'a mut Stack, op_dir: &'a OpDir, output: Outputter, diff --git a/src/indexing.rs b/src/indexing.rs index 94ae266b..ce04bbac 100644 --- a/src/indexing.rs +++ b/src/indexing.rs @@ -6,7 +6,6 @@ use crate::instructions::*; use fxhash::FxBuildHasher; use indexmap::IndexMap; -use tokio::sync::RwLock; use std::collections::VecDeque; use std::hash::Hash; @@ -1094,7 +1093,7 @@ fn uncap_choice_seq_with_try(prelude: &mut [IndexedChoiceInstruction]) { pub(crate) fn constant_key_alternatives( constant: Literal, - atom_tbl: &RwLock, + atom_tbl: &AtomTable, // arena: &mut Arena, ) -> Vec { let mut constants = vec![]; @@ -1455,7 +1454,7 @@ impl CodeOffsets { fn index_constant( &mut self, - atom_tbl: &RwLock, + atom_tbl: &AtomTable, constant: Literal, index: usize, ) -> Vec { @@ -1512,7 +1511,7 @@ impl CodeOffsets { optimal_arg: &Term, index: usize, clause_index_info: &mut ClauseIndexInfo, - atom_tbl: &RwLock, + atom_tbl: &AtomTable, ) { match optimal_arg { &Term::Clause(_, atom!("."), ref terms) if terms.len() == 2 => { diff --git a/src/machine/dispatch.rs b/src/machine/dispatch.rs index af4ca466..35a73f58 100644 --- a/src/machine/dispatch.rs +++ b/src/machine/dispatch.rs @@ -3000,7 +3000,7 @@ impl Machine { HeapCellValueTag::StackVar | HeapCellValueTag::Var) => { let target_cell = self.machine_st.push_str_to_heap( - string.as_str(), + &string.as_str(), has_tail, ); diff --git a/src/machine/heap.rs b/src/machine/heap.rs index 142b6359..dc52d916 100644 --- a/src/machine/heap.rs +++ b/src/machine/heap.rs @@ -1,5 +1,3 @@ -use tokio::sync::RwLock; - use crate::arena::*; use crate::atom_table::*; use crate::forms::*; @@ -132,11 +130,7 @@ pub fn print_heap_terms<'a, I: Iterator>(heap: I, h: u } #[inline] -pub(crate) fn put_complete_string( - heap: &mut Heap, - s: &str, - atom_tbl: &RwLock, -) -> HeapCellValue { +pub(crate) fn put_complete_string(heap: &mut Heap, s: &str, atom_tbl: &AtomTable) -> HeapCellValue { match allocate_pstr(heap, s, atom_tbl) { Some(h) => { heap.pop(); // pop the trailing variable cell from the heap planted by allocate_pstr. @@ -159,11 +153,7 @@ pub(crate) fn put_complete_string( } #[inline] -pub(crate) fn put_partial_string( - heap: &mut Heap, - s: &str, - atom_tbl: &RwLock, -) -> HeapCellValue { +pub(crate) fn put_partial_string(heap: &mut Heap, s: &str, atom_tbl: &AtomTable) -> HeapCellValue { match allocate_pstr(heap, s, atom_tbl) { Some(h) => { pstr_loc_as_cell!(h) @@ -175,11 +165,7 @@ pub(crate) fn put_partial_string( } #[inline] -pub(crate) fn allocate_pstr( - heap: &mut Heap, - mut src: &str, - atom_tbl: &RwLock, -) -> Option { +pub(crate) fn allocate_pstr(heap: &mut Heap, mut src: &str, atom_tbl: &AtomTable) -> Option { let orig_h = heap.len(); loop { diff --git a/src/machine/machine_state.rs b/src/machine/machine_state.rs index e099e894..eb608458 100644 --- a/src/machine/machine_state.rs +++ b/src/machine/machine_state.rs @@ -18,7 +18,6 @@ use crate::types::*; use crate::parser::dashu::Integer; use indexmap::IndexMap; -use tokio::sync::RwLock; use std::convert::TryFrom; use std::fmt; @@ -59,7 +58,7 @@ pub enum OnEOF { } pub struct MachineState { - pub atom_tbl: Arc>, + pub atom_tbl: Arc, pub arena: Arena, pub(super) pdl: Vec, pub(super) s: HeapPtr, @@ -205,7 +204,7 @@ pub fn pstr_loc_and_offset(heap: &[HeapCellValue], index: usize) -> (usize, Fixn fn push_var_eq_functors<'a>( heap: &mut Heap, iter: impl Iterator, - atom_tbl: &RwLock, + atom_tbl: &AtomTable, ) -> Vec { let mut list_of_var_eqs = vec![]; diff --git a/src/machine/partial_string.rs b/src/machine/partial_string.rs index 1260ebdc..8d03eefe 100644 --- a/src/machine/partial_string.rs +++ b/src/machine/partial_string.rs @@ -1,5 +1,3 @@ -use tokio::sync::RwLock; - use crate::atom_table::*; use crate::parser::ast::*; @@ -45,7 +43,7 @@ impl Into for PartialString { impl PartialString { #[inline] - pub(super) fn new<'a>(src: &'a str, atom_tbl: &RwLock) -> Option<(Self, &'a str)> { + pub(super) fn new<'a>(src: &'a str, atom_tbl: &AtomTable) -> Option<(Self, &'a str)> { let terminator_idx = scan_for_terminator(src.chars()); let pstr = PartialString(AtomTable::build_with(&atom_tbl, &src[..terminator_idx])); Some(if terminator_idx < src.as_bytes().len() { diff --git a/src/machine/preprocessor.rs b/src/machine/preprocessor.rs index 79946a49..b51ee2fb 100644 --- a/src/machine/preprocessor.rs +++ b/src/machine/preprocessor.rs @@ -8,7 +8,6 @@ use crate::machine::machine_errors::*; use crate::parser::ast::*; use indexmap::IndexSet; -use tokio::sync::RwLock; use std::cell::Cell; use std::convert::TryFrom; @@ -26,10 +25,7 @@ pub(crate) fn to_op_decl(prec: u16, spec: Atom, name: Atom) -> Result, - atom_tbl: &RwLock, -) -> Result { +fn setup_op_decl(mut terms: Vec, atom_tbl: &AtomTable) -> Result { let name = match terms.pop().unwrap() { Term::Literal(_, Literal::Atom(name)) => name, Term::Literal(_, Literal::Char(c)) => AtomTable::build_with(atom_tbl, &c.to_string()), @@ -86,7 +82,7 @@ fn setup_predicate_indicator(term: &mut Term) -> Result, + atom_tbl: &AtomTable, ) -> Result { setup_predicate_indicator(&mut term) .map(ModuleExport::PredicateKey) @@ -112,7 +108,7 @@ pub(crate) fn build_rule_body(vars: &[Term], body_term: Term) -> Term { pub(super) fn setup_module_export_list( mut export_list: Term, - atom_tbl: &RwLock, + atom_tbl: &AtomTable, ) -> Result, CompilationError> { let mut exports = vec![]; @@ -132,7 +128,7 @@ pub(super) fn setup_module_export_list( fn setup_module_decl( mut terms: Vec, - atom_tbl: &RwLock, + atom_tbl: &AtomTable, ) -> Result { let export_list = terms.pop().unwrap(); let name = terms.pop().unwrap(); @@ -165,7 +161,7 @@ type UseModuleExport = (ModuleSource, IndexSet); fn setup_qualified_import( mut terms: Vec, - atom_tbl: &RwLock, + atom_tbl: &AtomTable, ) -> Result { let mut export_list = terms.pop().unwrap(); let module_src = match terms.pop().unwrap() { diff --git a/src/machine/stack.rs b/src/machine/stack.rs index a4be4974..387c40d7 100644 --- a/src/machine/stack.rs +++ b/src/machine/stack.rs @@ -30,12 +30,6 @@ pub struct Stack { _marker: PhantomData, } -impl Drop for Stack { - fn drop(&mut self) { - self.buf.deallocate(); - } -} - #[derive(Debug)] pub(crate) struct AndFramePrelude { pub(crate) num_cells: usize, @@ -189,7 +183,7 @@ impl Stack { let frame_size = AndFrame::size_of(num_cells); unsafe { - let e = self.buf.ptr as usize - self.buf.base as usize; + let e = (*self.buf.ptr.get_mut()) as usize - self.buf.base as usize; let new_ptr = self.alloc(frame_size); let mut offset = prelude_size::(); @@ -213,7 +207,7 @@ impl Stack { let frame_size = OrFrame::size_of(num_cells); unsafe { - let b = self.buf.ptr as usize - self.buf.base as usize; + let b = (*self.buf.ptr.get_mut()) as usize - self.buf.base as usize; let new_ptr = self.alloc(frame_size); let mut offset = prelude_size::(); @@ -269,8 +263,8 @@ impl Stack { pub(crate) fn truncate(&mut self, b: usize) { let base = self.buf.base as usize + b; - if base < self.buf.ptr as usize { - self.buf.ptr = base as *mut _; + if base < (*self.buf.ptr.get_mut()) as usize { + *self.buf.ptr.get_mut() = base as *mut _; } } } diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 9df6f50c..1351b044 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -19,7 +19,6 @@ use fxhash::FxBuildHasher; use indexmap::IndexMap; use modular_bitfield::error::OutOfBounds; use modular_bitfield::prelude::*; -use tokio::sync::RwLock; pub type Specifier = u32; @@ -633,7 +632,7 @@ impl fmt::Display for Literal { } impl Literal { - pub fn to_atom(&self, atom_tbl: &Arc>) -> Option { + pub fn to_atom(&self, atom_tbl: &Arc) -> Option { match self { Literal::Atom(atom) => Some(atom.defrock_brackets(atom_tbl)), _ => None, diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 462bcf24..2160c764 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -1,6 +1,5 @@ use dashu::Integer; use dashu::Rational; -use tokio::sync::RwLock; use crate::arena::*; use crate::atom_table::*; @@ -286,14 +285,14 @@ fn read_tokens(lexer: &mut Lexer) -> Result, ParserEr Ok(tokens) } -fn atomize_term(atom_tbl: &RwLock, term: &Term) -> Option { +fn atomize_term(atom_tbl: &AtomTable, term: &Term) -> Option { match term { Term::Literal(_, ref c) => atomize_constant(atom_tbl, *c), _ => None, } } -fn atomize_constant(atom_tbl: &RwLock, c: Literal) -> Option { +fn atomize_constant(atom_tbl: &AtomTable, c: Literal) -> Option { match c { Literal::Atom(ref name) => Some(*name), Literal::Char(c) => Some(AtomTable::build_with(atom_tbl, &c.to_string())), diff --git a/src/raw_block.rs b/src/raw_block.rs index a96ae1af..53b4f198 100644 --- a/src/raw_block.rs +++ b/src/raw_block.rs @@ -1,6 +1,7 @@ use core::marker::PhantomData; use std::alloc; +use std::cell::UnsafeCell; use std::ptr; pub trait RawBlockTraits { @@ -12,7 +13,7 @@ pub trait RawBlockTraits { pub struct RawBlock { pub base: *const u8, pub top: *const u8, - pub ptr: *mut u8, + pub ptr: UnsafeCell<*mut u8>, _marker: PhantomData, } @@ -22,7 +23,7 @@ impl RawBlock { RawBlock { base: ptr::null(), top: ptr::null(), - ptr: ptr::null_mut(), + ptr: UnsafeCell::new(ptr::null_mut()), _marker: PhantomData, } } @@ -42,7 +43,7 @@ impl RawBlock { self.base = alloc::alloc(layout) as *const _; self.top = (self.base as usize + cap) as *const _; - self.ptr = self.base as *mut _; + *self.ptr.get_mut() = self.base as *mut _; } pub unsafe fn grow(&mut self) { @@ -54,7 +55,25 @@ impl RawBlock { self.base = alloc::realloc(self.base as *mut _, layout, size * 2) as *const _; self.top = (self.base as usize + size * 2) as *const _; - self.ptr = (self.base as usize + size) as *mut _; + *self.ptr.get_mut() = (self.base as usize + size) as *mut _; + } + } + + pub unsafe fn grow_new(&self) -> Option { + if self.base.is_null() { + Some(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()) as usize - self.base as usize; + self.base.copy_to(new_block.base.cast_mut(), allocated); + *new_block.ptr.get_mut() = new_block.base.offset(allocated as isize).cast_mut(); + Some(new_block) + } } } @@ -64,35 +83,39 @@ impl RawBlock { } #[inline(always)] - fn free_space(&self) -> usize { + unsafe fn free_space(&self) -> usize { debug_assert!( - self.ptr as *const _ >= self.base, + *self.ptr.get() as *const _ >= self.base, "self.ptr = {:?} < {:?} = self.base", - self.ptr, + *self.ptr.get(), self.base ); - self.top as usize - self.ptr as usize + self.top as usize - (*self.ptr.get()) as usize } - pub unsafe fn alloc(&mut self, size: usize) -> *mut u8 { + pub unsafe fn alloc(&self, size: usize) -> *mut u8 { if self.free_space() >= size { - let ptr = self.ptr; - self.ptr = (self.ptr as usize + size) as *mut _; + let ptr = *self.ptr.get(); + *self.ptr.get() = (ptr as usize + size) as *mut _; ptr } else { ptr::null_mut() } } +} - pub fn deallocate(&mut self) { - unsafe { - let layout = alloc::Layout::from_size_align_unchecked(self.size(), T::align()); - alloc::dealloc(self.base as *mut _, layout); +impl Drop for RawBlock { + fn drop(&mut self) { + if !self.base.is_null() { + unsafe { + let layout = alloc::Layout::from_size_align_unchecked(self.size(), T::align()); + alloc::dealloc(self.base as *mut _, layout); + } self.top = ptr::null(); self.base = ptr::null(); - self.ptr = ptr::null_mut(); + *self.ptr.get_mut() = ptr::null_mut(); } } } diff --git a/src/read.rs b/src/read.rs index 17fa3257..1baf8720 100644 --- a/src/read.rs +++ b/src/read.rs @@ -16,10 +16,6 @@ use crate::types::*; use fxhash::FxBuildHasher; -use tokio::sync::RwLock; - -use std::sync::Arc; - #[cfg(feature = "repl")] use rustyline::error::ReadlineError; #[cfg(feature = "repl")] @@ -29,6 +25,7 @@ use rustyline::{Config, Editor}; use std::collections::VecDeque; use std::io::{Cursor, Error, ErrorKind, Read}; +use std::sync::Arc; type SubtermDeque = VecDeque<(usize, usize)>; @@ -148,7 +145,7 @@ impl ReadlineStream { } } - pub fn set_atoms_for_completion(&mut self, atoms: &Arc>) { + pub fn set_atoms_for_completion(&mut self, atoms: &Arc) { #[cfg(feature = "repl")] { let helper = self.rl.helper_mut().unwrap(); @@ -291,7 +288,7 @@ impl CharRead for ReadlineStream { pub(crate) fn write_term_to_heap<'a, 'b>( term: &'a Term, heap: &'b mut Heap, - atom_tbl: &RwLock, + atom_tbl: &AtomTable, ) -> Result { let term_writer = TermWriter::new(heap, atom_tbl); term_writer.write_term_to_heap(term) @@ -300,7 +297,7 @@ pub(crate) fn write_term_to_heap<'a, 'b>( #[derive(Debug)] struct TermWriter<'a, 'b> { heap: &'a mut Heap, - atom_tbl: &'b RwLock, + atom_tbl: &'b AtomTable, queue: SubtermDeque, var_dict: HeapVarDict, } @@ -313,7 +310,7 @@ pub struct TermWriteResult { impl<'a, 'b> TermWriter<'a, 'b> { #[inline] - fn new(heap: &'a mut Heap, atom_tbl: &'b RwLock) -> Self { + fn new(heap: &'a mut Heap, atom_tbl: &'b AtomTable) -> Self { TermWriter { heap, atom_tbl, diff --git a/src/repl_helper.rs b/src/repl_helper.rs index 7f891655..4e33b555 100644 --- a/src/repl_helper.rs +++ b/src/repl_helper.rs @@ -4,8 +4,6 @@ use rustyline::hint::Hinter; use rustyline::validate::Validator; use rustyline::{Context, Helper as RlHelper, Result}; -use tokio::sync::RwLock; - use std::sync::Weak; use crate::atom_table::{AtomString, AtomTable, STATIC_ATOMS_MAP}; @@ -13,7 +11,7 @@ use crate::atom_table::{AtomString, AtomTable, STATIC_ATOMS_MAP}; // TODO: Maybe add validation to the helper pub struct Helper { highligher: MatchingBracketHighlighter, - pub atoms: Weak>, + pub atoms: Weak, } impl Helper { @@ -71,11 +69,10 @@ impl Completer for Helper { let sub_str = line.get(idx..pos).unwrap(); let atom_table = self.atoms.upgrade().unwrap(); - let guard = atom_table.blocking_read(); - let mut matching = guard - .table - .blocking_read() + let index_set = atom_table.active_epoch().table.blocking_read().clone(); + + let mut matching = index_set .iter() .chain(STATIC_ATOMS_MAP.values()) .map(|a| a.as_str())