]> Repositorios git - scryer-prolog.git/commitdiff
[WIP] move towards lockless AtomTable
authorBennet Bleßmann <[email protected]>
Mon, 28 Aug 2023 21:24:27 +0000 (23:24 +0200)
committerBennet Bleßmann <[email protected]>
Tue, 5 Sep 2023 17:39:46 +0000 (19:39 +0200)
17 files changed:
src/arena.rs
src/atom_table.rs
src/codegen.rs
src/forms.rs
src/heap_print.rs
src/indexing.rs
src/machine/dispatch.rs
src/machine/heap.rs
src/machine/machine_state.rs
src/machine/partial_string.rs
src/machine/preprocessor.rs
src/machine/stack.rs
src/parser/ast.rs
src/parser/parser.rs
src/raw_block.rs
src/read.rs
src/repl_helper.rs

index b2e43466c707ef8a1ad2304f321bca46361ef7c7..758285dad301c6340369e382c824ef36aa18c9c8 100644 (file)
@@ -70,12 +70,6 @@ pub struct F64Table {
     block: RawBlock<F64Table>,
 }
 
-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| {
index 523d897408800294a3d0339e662f8cce7ae7571f..6ad7cc304f163a6080b33c2bb3cde6eae96e90d6 100644 (file)
@@ -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<bool> for Atom {
     }
 }
 
-impl indexmap::Equivalent<Atom> for LookupKey<'_, '_> {
+impl indexmap::Equivalent<Atom> 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<Weak<RwLock<AtomTable>>> {
+#[inline(always)]
+fn global_atom_table() -> &'static RwLock<Weak<AtomTable>> {
     #[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<Weak<RwLock<AtomTable>>> = RwLock::const_new(Weak::new());
+        static GLOBAL_ATOM_TABLE: RwLock<Weak<AtomTable>> = RwLock::const_new(Weak::new());
         &GLOBAL_ATOM_TABLE
     }
     #[cfg(not(feature = "rust_beta_channel"))]
     {
         use std::sync::OnceLock;
-        static GLOBAL_ATOM_TABLE: OnceLock<RwLock<Weak<RwLock<AtomTable>>>> = OnceLock::new();
+        static GLOBAL_ATOM_TABLE: OnceLock<RwLock<Weak<AtomTable>>> = OnceLock::new();
         GLOBAL_ATOM_TABLE.get_or_init(|| RwLock::new(Weak::new()))
     }
 }
 
-fn owned_atom_table_read_guard() -> Option<OwnedRwLockReadGuard<AtomTable>> {
-    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<Arc<AtomTable>> {
+    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<AtomTable, str>),
+    Dynamic(AtomTableRef<str>),
 }
 
 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<OwnedRwLockReadGuard<AtomTable, u8>> {
-        if let Some(guard) = self.as_ptr() {
-            Some(OwnedRwLockReadGuard::map(guard, |ptr| unsafe {
-                (ptr as *const u8)
-                    .offset(mem::size_of::<AtomHeader>() 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<AtomTableRef<u8>> {
         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<OwnedRwLockReadGuard<AtomTable, u8>> {
-        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::<AtomHeader>(ptr as *const u8 as *const AtomHeader) };
-            let len = header.len() as usize;
-            let buf = (unsafe { (ptr as *const u8).offset(mem::size_of::<AtomHeader>() 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::<AtomHeader>(ptr as *const u8 as *const AtomHeader) };
+                    let len = header.len() as usize;
+                    let buf = (unsafe {
+                        (ptr as *const u8).offset(mem::size_of::<AtomHeader>() 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<RwLock<AtomTable>>) -> 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<M>
+where
+    M: ?Sized,
+{
+    arc: Arc<InnerAtomTable>,
+    data: NonNull<M>,
+}
+
+impl<M> Clone for AtomTableRef<M> {
+    fn clone(&self) -> Self {
+        Self {
+            arc: Arc::clone(&self.arc),
+            data: self.data,
+        }
+    }
+}
+
+impl<M: ?Sized> AtomTableRef<M> {
+    pub fn map<N: ?Sized, F: for<'a> FnOnce(&'a M) -> &'a N>(
+        referece: Self,
+        f: F,
+    ) -> AtomTableRef<N> {
+        AtomTableRef {
+            arc: referece.arc,
+            data: f(unsafe { referece.data.as_ref() }).into(),
+        }
+    }
+
+    pub fn try_map<N, F: for<'a> FnOnce(&'a M) -> Option<&'a N>>(
+        referece: Self,
+        f: F,
+    ) -> Option<AtomTableRef<N>> {
+        let val = f(unsafe { referece.data.as_ref() })?;
+        Some(AtomTableRef {
+            arc: Arc::clone(&referece.arc),
+            data: val.into(),
+        })
+    }
+}
+
+impl<M: ?Sized> Deref for AtomTableRef<M> {
+    type Target = M;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe { self.data.as_ref() }
+    }
+}
+
 #[derive(Debug)]
-pub struct AtomTable {
+pub struct InnerAtomTable {
     block: RawBlock<AtomTable>,
     pub table: RwLock<IndexSet<Atom>>,
 }
 
-impl Drop for AtomTable {
-    fn drop(&mut self) {
-        self.block.deallocate();
-    }
+#[derive(Debug)]
+pub struct AtomTable {
+    inner: RwLock<Arc<InnerAtomTable>>,
+    // this lock is taking during resizing
+    update: Mutex<()>,
 }
 
-struct LookupKey<'table, 'key>(&'table AtomTable, &'key str);
-
-impl Hash for LookupKey<'_, '_> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.1.hash(state);
+impl InnerAtomTable {
+    #[inline(always)]
+    fn lookup_str(self: &InnerAtomTable, string: &str) -> Option<Atom> {
+        STATIC_ATOMS_MAP
+            .get(string)
+            .cloned()
+            .or_else(|| self.table.blocking_read().get(string).cloned())
     }
 }
 
 impl AtomTable {
     #[inline]
-    pub fn new() -> Arc<RwLock<Self>> {
+    pub fn new() -> Arc<Self> {
         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<InnerAtomTable> {
+        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<Atom> {
-        STATIC_ATOMS_MAP.get(string).cloned().or_else(|| {
-            self.table
-                .blocking_read()
-                .get(&LookupKey(self, string))
-                .cloned()
+    pub fn buf(&self) -> AtomTableRef<u8> {
+        AtomTableRef::<InnerAtomTable>::map(self.active_epoch(), |inner| {
+            unsafe { inner.block.base.as_ref() }.unwrap()
         })
     }
 
-    pub fn build_with(atom_table: &RwLock<AtomTable>, 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::<AtomHeader>() + string.len();
             let align_offset = 8 * mem::align_of::<AtomHeader>();
             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;
+            }
         }
     }
 }
index 65dd781713b53697e0df9a88aa3182f9c54366ea..e4e75006c299a7d42209643616d2da1185434f00 100644 (file)
@@ -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<AtomTable>,
+    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<RegType>> {
 }
 
 impl<'b> CodeGenerator<'b> {
-    pub(crate) fn new(atom_tbl: &'b RwLock<AtomTable>, settings: CodeGenSettings) -> Self {
+    pub(crate) fn new(atom_tbl: &'b AtomTable, settings: CodeGenSettings) -> Self {
         CodeGenerator {
             atom_tbl,
             marker: DebrayAllocator::new(),
index 5f2a6ec73d7a5cf5cf4b97a9f52ad4924c445053..9280e5350ddd707a12bb7129c12ace9bbf6b83dc 100644 (file)
@@ -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<AtomTable>) -> 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),
index c6e81da591348a3920eda4ab077f9c8621a3c513..9880a4d1730ab70242fb35021d607a0951eb519e 100644 (file)
@@ -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<RwLock<AtomTable>>,
+    atom_tbl: Arc<AtomTable>,
     op_dir: &'a OpDir,
     state_stack: Vec<TokenOrRedirect>,
     toplevel_spec: Option<DirectedOp>,
@@ -552,7 +550,7 @@ pub(crate) fn numbervar(offset: &Integer, addr: HeapCellValue) -> Option<String>
 impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
     pub fn new(
         heap: &'a mut Heap,
-        atom_tbl: Arc<RwLock<AtomTable>>,
+        atom_tbl: Arc<AtomTable>,
         stack: &'a mut Stack,
         op_dir: &'a OpDir,
         output: Outputter,
index 94ae266bedd6d0b6201ee0dcf22ec4b4400efbfe..ce04bbacdf713ad2e97a145fdd52335f39ecfd00 100644 (file)
@@ -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<AtomTable>,
+    atom_tbl: &AtomTable,
     // arena: &mut Arena,
 ) -> Vec<Literal> {
     let mut constants = vec![];
@@ -1455,7 +1454,7 @@ impl<I: Indexer> CodeOffsets<I> {
 
     fn index_constant(
         &mut self,
-        atom_tbl: &RwLock<AtomTable>,
+        atom_tbl: &AtomTable,
         constant: Literal,
         index: usize,
     ) -> Vec<Literal> {
@@ -1512,7 +1511,7 @@ impl<I: Indexer> CodeOffsets<I> {
         optimal_arg: &Term,
         index: usize,
         clause_index_info: &mut ClauseIndexInfo,
-        atom_tbl: &RwLock<AtomTable>,
+        atom_tbl: &AtomTable,
     ) {
         match optimal_arg {
             &Term::Clause(_, atom!("."), ref terms) if terms.len() == 2 => {
index af4ca4666a011fab4bc4e7577b027a733f53ceb1..35a73f581135e9087be12b3ac420e70a831e82c1 100644 (file)
@@ -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,
                                 );
 
index 142b635963dce8f8fe03a92d7e1a6f6cd861affc..dc52d9167f48632982928276e373552dd636f88b 100644 (file)
@@ -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<Item = &'a HeapCellValue>>(heap: I, h: u
 }
 
 #[inline]
-pub(crate) fn put_complete_string(
-    heap: &mut Heap,
-    s: &str,
-    atom_tbl: &RwLock<AtomTable>,
-) -> 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<AtomTable>,
-) -> 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<AtomTable>,
-) -> Option<usize> {
+pub(crate) fn allocate_pstr(heap: &mut Heap, mut src: &str, atom_tbl: &AtomTable) -> Option<usize> {
     let orig_h = heap.len();
 
     loop {
index e099e8948ceca789c29fae414e4e3333f1a93298..eb608458633bc003040df8fc832e29e673ad11fb 100644 (file)
@@ -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<RwLock<AtomTable>>,
+    pub atom_tbl: Arc<AtomTable>,
     pub arena: Arena,
     pub(super) pdl: Vec<HeapCellValue>,
     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<Item = (&'a VarKey, &'a HeapCellValue)>,
-    atom_tbl: &RwLock<AtomTable>,
+    atom_tbl: &AtomTable,
 ) -> Vec<HeapCellValue> {
     let mut list_of_var_eqs = vec![];
 
index 1260ebdcd22ac25dd3e4989ef61832630a60bd13..8d03eefe8c9fe18ed4ae7f3928175390082cb404 100644 (file)
@@ -1,5 +1,3 @@
-use tokio::sync::RwLock;
-
 use crate::atom_table::*;
 use crate::parser::ast::*;
 
@@ -45,7 +43,7 @@ impl Into<Atom> for PartialString {
 
 impl PartialString {
     #[inline]
-    pub(super) fn new<'a>(src: &'a str, atom_tbl: &RwLock<AtomTable>) -> 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() {
index 79946a490c281f8152cab09bf7d8ec3a7c9eb869..b51ee2fba1530e7770cc706b6f2458935c99fcec 100644 (file)
@@ -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<OpDecl, Co
     }
 }
 
-fn setup_op_decl(
-    mut terms: Vec<Term>,
-    atom_tbl: &RwLock<AtomTable>,
-) -> Result<OpDecl, CompilationError> {
+fn setup_op_decl(mut terms: Vec<Term>, atom_tbl: &AtomTable) -> Result<OpDecl, CompilationError> {
     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<PredicateKey, Compilatio
 
 fn setup_module_export(
     mut term: Term,
-    atom_tbl: &RwLock<AtomTable>,
+    atom_tbl: &AtomTable,
 ) -> Result<ModuleExport, CompilationError> {
     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<AtomTable>,
+    atom_tbl: &AtomTable,
 ) -> Result<Vec<ModuleExport>, CompilationError> {
     let mut exports = vec![];
 
@@ -132,7 +128,7 @@ pub(super) fn setup_module_export_list(
 
 fn setup_module_decl(
     mut terms: Vec<Term>,
-    atom_tbl: &RwLock<AtomTable>,
+    atom_tbl: &AtomTable,
 ) -> Result<ModuleDecl, CompilationError> {
     let export_list = terms.pop().unwrap();
     let name = terms.pop().unwrap();
@@ -165,7 +161,7 @@ type UseModuleExport = (ModuleSource, IndexSet<ModuleExport>);
 
 fn setup_qualified_import(
     mut terms: Vec<Term>,
-    atom_tbl: &RwLock<AtomTable>,
+    atom_tbl: &AtomTable,
 ) -> Result<UseModuleExport, CompilationError> {
     let mut export_list = terms.pop().unwrap();
     let module_src = match terms.pop().unwrap() {
index a4be4974d024d5f3313d1af6e376e87bf41d1f55..387c40d7fe827169a046d50cfc1ba78b70395e94 100644 (file)
@@ -30,12 +30,6 @@ pub struct Stack {
     _marker: PhantomData<HeapCellValue>,
 }
 
-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::<AndFramePrelude>();
 
@@ -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::<OrFramePrelude>();
 
@@ -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 _;
         }
     }
 }
index 9df6f50c396295dc775cc4d7bbebc2600b00ee94..1351b044687b2b500e6e14ed84cf2b9f7346b165 100644 (file)
@@ -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<RwLock<AtomTable>>) -> Option<Atom> {
+    pub fn to_atom(&self, atom_tbl: &Arc<AtomTable>) -> Option<Atom> {
         match self {
             Literal::Atom(atom) => Some(atom.defrock_brackets(atom_tbl)),
             _ => None,
index 462bcf24b5562f8fffe8f0a507fe9bcb8218fd1c..2160c764a34ef08d0c8b23cf80f04753c753fc01 100644 (file)
@@ -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<R: CharRead>(lexer: &mut Lexer<R>) -> Result<Vec<Token>, ParserEr
     Ok(tokens)
 }
 
-fn atomize_term(atom_tbl: &RwLock<AtomTable>, term: &Term) -> Option<Atom> {
+fn atomize_term(atom_tbl: &AtomTable, term: &Term) -> Option<Atom> {
     match term {
         Term::Literal(_, ref c) => atomize_constant(atom_tbl, *c),
         _ => None,
     }
 }
 
-fn atomize_constant(atom_tbl: &RwLock<AtomTable>, c: Literal) -> Option<Atom> {
+fn atomize_constant(atom_tbl: &AtomTable, c: Literal) -> Option<Atom> {
     match c {
         Literal::Atom(ref name) => Some(*name),
         Literal::Char(c) => Some(AtomTable::build_with(atom_tbl, &c.to_string())),
index a96ae1aff613fed432705c308a4dd17b6aaa15a9..53b4f198dd5c978d50d0d07b491134c09430f164 100644 (file)
@@ -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<T: RawBlockTraits> {
     pub base: *const u8,
     pub top: *const u8,
-    pub ptr: *mut u8,
+    pub ptr: UnsafeCell<*mut u8>,
     _marker: PhantomData<T>,
 }
 
@@ -22,7 +23,7 @@ impl<T: RawBlockTraits> RawBlock<T> {
         RawBlock {
             base: ptr::null(),
             top: ptr::null(),
-            ptr: ptr::null_mut(),
+            ptr: UnsafeCell::new(ptr::null_mut()),
             _marker: PhantomData,
         }
     }
@@ -42,7 +43,7 @@ impl<T: RawBlockTraits> RawBlock<T> {
 
         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<T: RawBlockTraits> RawBlock<T> {
 
             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<Self> {
+        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<T: RawBlockTraits> RawBlock<T> {
     }
 
     #[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<T: RawBlockTraits> Drop for RawBlock<T> {
+    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();
         }
     }
 }
index 17fa32579098e2de40f884af2664c38f6282afb2..1baf8720ee74f57e9d3872637e3b947a064e6b54 100644 (file)
@@ -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<RwLock<AtomTable>>) {
+    pub fn set_atoms_for_completion(&mut self, atoms: &Arc<AtomTable>) {
         #[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<AtomTable>,
+    atom_tbl: &AtomTable,
 ) -> Result<TermWriteResult, CompilationError> {
     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<AtomTable>,
+    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<AtomTable>) -> Self {
+    fn new(heap: &'a mut Heap, atom_tbl: &'b AtomTable) -> Self {
         TermWriter {
             heap,
             atom_tbl,
index 7f891655e4e8fd55c5ba454f973f590c2bc40f72..4e33b555f2bc9c69d3f4bdf66b874743d8704591 100644 (file)
@@ -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<RwLock<AtomTable>>,
+    pub atoms: Weak<AtomTable>,
 }
 
 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())