9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
# operating systems
|
# operating systems
|
||||||
- { os: windows-latest, rust-version: stable, target: 'x86_64-pc-windows-msvc', publish: true }
|
- { os: windows-latest, rust-version: stable, target: 'x86_64-pc-windows-msvc', publish: true }
|
||||||
- { os: macos-11, rust-version: stable, target: 'x86_64-apple-darwin', publish: true }
|
- { os: macos-latest, rust-version: stable, target: 'x86_64-apple-darwin', publish: true }
|
||||||
- { os: ubuntu-20.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true }
|
- { os: ubuntu-20.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true }
|
||||||
# architectures
|
# architectures
|
||||||
- { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true }
|
- { os: ubuntu-22.04, rust-version: stable, target: 'x86_64-unknown-linux-gnu', publish: true }
|
||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
- { os: ubuntu-22.04, rust-version: "1.77", target: 'x86_64-unknown-linux-gnu'}
|
- { os: ubuntu-22.04, rust-version: "1.77", target: 'x86_64-unknown-linux-gnu'}
|
||||||
# rust versions
|
# rust versions
|
||||||
- { os: ubuntu-22.04, rust-version: beta, target: 'x86_64-unknown-linux-gnu'}
|
- { os: ubuntu-22.04, rust-version: beta, target: 'x86_64-unknown-linux-gnu'}
|
||||||
- { os: ubuntu-22.04, rust-version: nightly, target: 'x86_64-unknown-linux-gnu'}
|
- { os: ubuntu-22.04, rust-version: nightly, target: 'x86_64-unknown-linux-gnu', miri: true, components: "miri"}
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -66,6 +66,7 @@ jobs:
|
|||||||
rust-version: ${{ matrix.rust-version }}
|
rust-version: ${{ matrix.rust-version }}
|
||||||
targets: ${{ matrix.target }}
|
targets: ${{ matrix.target }}
|
||||||
cache-context: ${{ matrix.os }}
|
cache-context: ${{ matrix.os }}
|
||||||
|
components: ${{ matrix.components }}
|
||||||
|
|
||||||
# Build and test.
|
# Build and test.
|
||||||
- name: Build library
|
- name: Build library
|
||||||
@@ -73,6 +74,10 @@ jobs:
|
|||||||
- name: Test
|
- name: Test
|
||||||
run: cargo test --target ${{ matrix.target }} ${{ matrix.test-args }} --all
|
run: cargo test --target ${{ matrix.target }} ${{ matrix.test-args }} --all
|
||||||
|
|
||||||
|
- name: Check miri
|
||||||
|
if: matrix.miri
|
||||||
|
run: cargo miri test
|
||||||
|
|
||||||
# On stable rust builds, build a binary and publish as a github actions
|
# On stable rust builds, build a binary and publish as a github actions
|
||||||
# artifact. These binaries could be useful for testing the pipeline but
|
# artifact. These binaries could be useful for testing the pipeline but
|
||||||
# are only retained by github for 90 days.
|
# are only retained by github for 90 days.
|
||||||
|
|||||||
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -108,6 +108,12 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arcu"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8727c0fb4c436605c8f11c579ec86edcb729134aec4ee66e454efd99a91859f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@@ -2291,12 +2297,6 @@ dependencies = [
|
|||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ref_thread_local"
|
|
||||||
version = "0.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d813022b2e00774a48eaf43caaa3c20b45f040ba8cbf398e2e8911a06668dbe6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.2"
|
version = "1.10.2"
|
||||||
@@ -2558,6 +2558,7 @@ dependencies = [
|
|||||||
name = "scryer-prolog"
|
name = "scryer-prolog"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arcu",
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
"base64 0.12.3",
|
"base64 0.12.3",
|
||||||
"bit-set",
|
"bit-set",
|
||||||
@@ -2598,7 +2599,6 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"rand",
|
"rand",
|
||||||
"ref_thread_local",
|
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"ring 0.17.7",
|
"ring 0.17.7",
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ repl = ["dep:crossterm", "dep:ctrlc", "dep:rustyline"]
|
|||||||
hostname = ["dep:hostname"]
|
hostname = ["dep:hostname"]
|
||||||
tls = ["dep:native-tls"]
|
tls = ["dep:native-tls"]
|
||||||
http = ["dep:warp", "dep:reqwest"]
|
http = ["dep:warp", "dep:reqwest"]
|
||||||
rust_beta_channel = []
|
|
||||||
crypto-full = []
|
crypto-full = []
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
@@ -62,7 +61,6 @@ num-order = { version = "1.2.0" }
|
|||||||
ordered-float = "2.6.0"
|
ordered-float = "2.6.0"
|
||||||
phf = { version = "0.9", features = ["macros"] }
|
phf = { version = "0.9", features = ["macros"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
ref_thread_local = "0.0.0"
|
|
||||||
regex = "1.9.1"
|
regex = "1.9.1"
|
||||||
ring = { version = "0.17.5", features = ["wasm32_unknown_unknown_js"] }
|
ring = { version = "0.17.5", features = ["wasm32_unknown_unknown_js"] }
|
||||||
ripemd160 = "0.8.0"
|
ripemd160 = "0.8.0"
|
||||||
@@ -75,6 +73,7 @@ static_assertions = "1.1.0"
|
|||||||
|
|
||||||
serde_json = "1.0.95"
|
serde_json = "1.0.95"
|
||||||
serde = "1.0.159"
|
serde = "1.0.159"
|
||||||
|
arcu = { version = "0.1.1", features = ["thread_local_counter"] }
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
crossterm = { version = "0.20.0", optional = true }
|
crossterm = { version = "0.20.0", optional = true }
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ pub enum Strategy {
|
|||||||
Reuse,
|
Reuse,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub struct PrologBenchmark {
|
pub struct PrologBenchmark {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
pub filename: &'static str,
|
pub filename: &'static str,
|
||||||
|
|||||||
@@ -11,34 +11,45 @@ use std::io::Write;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
fn find_prolog_files(libraries: &mut File, prefix: &str, current_dir: &Path) {
|
fn find_prolog_files(
|
||||||
|
libraries: &mut File,
|
||||||
|
path_prefix: &str,
|
||||||
|
const_prefix: &str,
|
||||||
|
current_dir: &Path,
|
||||||
|
) -> Vec<(String, String)> {
|
||||||
|
let mut constants = vec![];
|
||||||
|
|
||||||
let entries = match current_dir.read_dir() {
|
let entries = match current_dir.read_dir() {
|
||||||
Ok(entries) => entries,
|
Ok(entries) => entries,
|
||||||
Err(_) => return,
|
Err(_) => return constants,
|
||||||
};
|
};
|
||||||
|
|
||||||
for entry in entries.filter_map(Result::ok).map(|e| e.path()) {
|
for entry in entries.filter_map(Result::ok).map(|e| e.path()) {
|
||||||
if entry.is_dir() {
|
if entry.is_dir() {
|
||||||
if let Some(file_name) = entry.file_name() {
|
if let Some(file_name) = entry.file_name() {
|
||||||
let new_prefix = prefix.to_owned() + file_name.to_str().unwrap() + "/";
|
let file_name = file_name.to_str().unwrap();
|
||||||
find_prolog_files(libraries, &new_prefix, &entry);
|
let new_path_prefix = format!("{path_prefix}{file_name}/");
|
||||||
|
let new_const_prefix = format!("{const_prefix}_{}", file_name.to_uppercase());
|
||||||
|
let new_consts =
|
||||||
|
find_prolog_files(libraries, &new_path_prefix, &new_const_prefix, &entry);
|
||||||
|
constants.extend(new_consts);
|
||||||
}
|
}
|
||||||
} else if entry.is_file() {
|
} else if entry.is_file() {
|
||||||
let ext = std::ffi::OsStr::new("pl");
|
let ext = std::ffi::OsStr::new("pl");
|
||||||
if entry.extension() == Some(ext) {
|
if entry.extension() == Some(ext) {
|
||||||
let contain = String::from_utf8(fs::read(&entry).unwrap()).unwrap();
|
let contain = String::from_utf8(fs::read(&entry).unwrap()).unwrap();
|
||||||
let name = entry.file_stem().unwrap().to_str().unwrap();
|
let name = entry.file_stem().unwrap().to_str().unwrap();
|
||||||
|
let lib_name = format!("{path_prefix}{name}");
|
||||||
|
let const_name = format!("{const_prefix}_{}", name.to_uppercase());
|
||||||
|
|
||||||
let line = format!(
|
writeln!(libraries, "const {const_name}: &str = {contain:?};").unwrap();
|
||||||
" m.insert(\"{}\",\n{:?});\n",
|
|
||||||
prefix.to_owned() + name,
|
|
||||||
contain
|
|
||||||
);
|
|
||||||
|
|
||||||
libraries.write_all(line.as_bytes()).unwrap();
|
constants.push((lib_name, const_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constants
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -58,16 +69,42 @@ fn main() {
|
|||||||
let mut libraries = File::create(dest_path).unwrap();
|
let mut libraries = File::create(dest_path).unwrap();
|
||||||
let lib_path = Path::new("src/lib");
|
let lib_path = Path::new("src/lib");
|
||||||
|
|
||||||
libraries
|
writeln!(
|
||||||
.write_all(
|
libraries,
|
||||||
b"ref_thread_local::ref_thread_local! {
|
"\
|
||||||
pub(crate) static managed LIBRARIES: IndexMap<&'static str, &'static str> = {
|
use indexmap::IndexMap;\
|
||||||
let mut m = IndexMap::new();\n",
|
"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let constants = find_prolog_files(&mut libraries, "", "LIB", lib_path);
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
libraries,
|
||||||
|
"\
|
||||||
|
std::thread_local!{{
|
||||||
|
static LIBRARIES: IndexMap<&'static str, &'static str> = {{
|
||||||
|
let mut m = IndexMap::new();"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
for (name, constant) in constants {
|
||||||
|
writeln!(
|
||||||
|
libraries,
|
||||||
|
"\
|
||||||
|
m.insert(\"{name}\",{constant});"
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
find_prolog_files(&mut libraries, "", lib_path);
|
writeln!(
|
||||||
libraries.write_all(b"\n m\n };\n}\n").unwrap();
|
libraries,
|
||||||
|
"
|
||||||
|
m
|
||||||
|
}};
|
||||||
|
}}"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let instructions_path = Path::new(&out_dir).join("instructions.rs");
|
let instructions_path = Path::new(&out_dir).join("instructions.rs");
|
||||||
let mut instructions_file = File::create(&instructions_path).unwrap();
|
let mut instructions_file = File::create(&instructions_path).unwrap();
|
||||||
|
|||||||
411
src/arena.rs
411
src/arena.rs
@@ -1,30 +1,39 @@
|
|||||||
|
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||||
|
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
use crate::http::{HttpListener, HttpResponse};
|
use crate::http::{HttpListener, HttpResponse};
|
||||||
use crate::machine::loader::LiveLoadState;
|
use crate::machine::loader::LiveLoadState;
|
||||||
use crate::machine::machine_indices::*;
|
use crate::machine::machine_indices::*;
|
||||||
use crate::machine::streams::*;
|
use crate::machine::streams::*;
|
||||||
use crate::raw_block::*;
|
use crate::raw_block::*;
|
||||||
use crate::rcu::Rcu;
|
|
||||||
use crate::rcu::RcuRef;
|
|
||||||
use crate::read::*;
|
use crate::read::*;
|
||||||
|
use crate::types::UntypedArenaPtr;
|
||||||
|
|
||||||
use crate::parser::dashu::{Integer, Rational};
|
use crate::parser::dashu::{Integer, Rational};
|
||||||
|
use arcu::atomic::Arcu;
|
||||||
|
use arcu::epoch_counters::GlobalEpochCounterPool;
|
||||||
|
use arcu::rcu_ref::RcuRef;
|
||||||
|
use arcu::Rcu;
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
|
|
||||||
use std::cell::UnsafeCell;
|
use std::cell::UnsafeCell;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::mem::ManuallyDrop;
|
||||||
use std::net::TcpListener;
|
use std::net::TcpListener;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::ptr::addr_of_mut;
|
||||||
|
use std::ptr::NonNull;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! arena_alloc {
|
macro_rules! arena_alloc {
|
||||||
($e:expr, $arena:expr) => {{
|
($e:expr, $arena:expr) => {{
|
||||||
let result = $e;
|
let result = $e;
|
||||||
ArenaAllocated::alloc($arena, result)
|
$crate::arena::AllocateInArena::arena_allocate(result, $arena)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,29 +45,18 @@ macro_rules! float_alloc {
|
|||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn header_offset_from_payload<Payload: Sized>() -> usize {
|
pub fn header_offset_from_payload<T: ?Sized + ArenaAllocated>() -> usize
|
||||||
let payload_offset = mem::offset_of!(TypedAllocSlab<Payload>, payload);
|
where
|
||||||
let slab_offset = mem::offset_of!(TypedAllocSlab<Payload>, slab);
|
T::Payload: Sized,
|
||||||
|
{
|
||||||
|
let payload_offset = mem::offset_of!(TypedAllocSlab<T>, payload);
|
||||||
|
let slab_offset = mem::offset_of!(TypedAllocSlab<T>, slab);
|
||||||
let header_offset = slab_offset + mem::offset_of!(AllocSlab, header);
|
let header_offset = slab_offset + mem::offset_of!(AllocSlab, header);
|
||||||
|
|
||||||
debug_assert!(payload_offset > header_offset);
|
debug_assert!(payload_offset > header_offset);
|
||||||
payload_offset - header_offset
|
payload_offset - header_offset
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ptr_to_allocated<Payload: ArenaAllocated>(slab: &mut AllocSlab) -> TypedArenaPtr<Payload> {
|
|
||||||
let typed_slab: &mut TypedAllocSlab<Payload> = unsafe { mem::transmute(slab) };
|
|
||||||
typed_slab.to_typed_arena_ptr()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! gen_ptr_to_allocated {
|
|
||||||
($payload: ty) => {
|
|
||||||
fn ptr_to_allocated(slab: &mut AllocSlab) -> TypedArenaPtr<$payload> {
|
|
||||||
ptr_to_allocated::<$payload>(slab)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::sync::Weak;
|
use std::sync::Weak;
|
||||||
@@ -68,19 +66,8 @@ const F64_TABLE_ALIGN: usize = 8;
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn global_f64table() -> &'static RwLock<Weak<F64Table>> {
|
fn global_f64table() -> &'static RwLock<Weak<F64Table>> {
|
||||||
#[cfg(feature = "rust_beta_channel")]
|
static GLOBAL_ATOM_TABLE: RwLock<Weak<F64Table>> = RwLock::new(Weak::new());
|
||||||
{
|
&GLOBAL_ATOM_TABLE
|
||||||
// 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<F64Table>> = 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<F64Table>>> = OnceLock::new();
|
|
||||||
GLOBAL_ATOM_TABLE.get_or_init(|| RwLock::new(Weak::new()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RawBlockTraits for F64Table {
|
impl RawBlockTraits for F64Table {
|
||||||
@@ -97,7 +84,7 @@ impl RawBlockTraits for F64Table {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct F64Table {
|
pub struct F64Table {
|
||||||
block: Rcu<RawBlock<F64Table>>,
|
block: Arcu<RawBlock<F64Table>, GlobalEpochCounterPool>,
|
||||||
update: Mutex<()>,
|
update: Mutex<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +98,7 @@ pub fn lookup_float(
|
|||||||
.upgrade()
|
.upgrade()
|
||||||
.expect("We should only be looking up floats while there is a float table");
|
.expect("We should only be looking up floats while there is a float table");
|
||||||
|
|
||||||
RcuRef::try_map(f64table.block.active_epoch(), |raw_block| unsafe {
|
RcuRef::try_map(f64table.block.read(), |raw_block| unsafe {
|
||||||
raw_block
|
raw_block
|
||||||
.base
|
.base
|
||||||
.add(offset.0)
|
.add(offset.0)
|
||||||
@@ -136,7 +123,7 @@ impl F64Table {
|
|||||||
atom_table
|
atom_table
|
||||||
} else {
|
} else {
|
||||||
let atom_table = Arc::new(Self {
|
let atom_table = Arc::new(Self {
|
||||||
block: Rcu::new(RawBlock::new()),
|
block: Arcu::new(RawBlock::new(), GlobalEpochCounterPool),
|
||||||
update: Mutex::new(()),
|
update: Mutex::new(()),
|
||||||
});
|
});
|
||||||
*guard = Arc::downgrade(&atom_table);
|
*guard = Arc::downgrade(&atom_table);
|
||||||
@@ -151,7 +138,7 @@ impl F64Table {
|
|||||||
|
|
||||||
// we don't have an index table for lookups as AtomTable does so
|
// we don't have an index table for lookups as AtomTable does so
|
||||||
// just get the epoch after we take the upgrade lock
|
// just get the epoch after we take the upgrade lock
|
||||||
let mut block_epoch = self.block.active_epoch();
|
let mut block_epoch = self.block.read();
|
||||||
|
|
||||||
let mut ptr;
|
let mut ptr;
|
||||||
|
|
||||||
@@ -161,7 +148,7 @@ impl F64Table {
|
|||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
let new_block = block_epoch.grow_new().unwrap();
|
let new_block = block_epoch.grow_new().unwrap();
|
||||||
self.block.replace(new_block);
|
self.block.replace(new_block);
|
||||||
block_epoch = self.block.active_epoch();
|
block_epoch = self.block.read();
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -210,6 +197,7 @@ pub enum ArenaHeaderTag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[bitfield]
|
#[bitfield]
|
||||||
|
#[repr(align(8))]
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct ArenaHeader {
|
pub struct ArenaHeader {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -236,76 +224,96 @@ impl ArenaHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TypedArenaPtr<T: ?Sized>(ptr::NonNull<T>);
|
pub struct TypedArenaPtr<T: ?Sized + ArenaAllocated>(ptr::NonNull<T::Payload>);
|
||||||
|
|
||||||
impl<T: ?Sized + PartialOrd> PartialOrd for TypedArenaPtr<T> {
|
impl<T: ?Sized + ArenaAllocated> PartialOrd for TypedArenaPtr<T>
|
||||||
|
where
|
||||||
|
T::Payload: PartialOrd,
|
||||||
|
{
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
(**self).partial_cmp(&**other)
|
(**self).partial_cmp(&**other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + PartialEq> PartialEq for TypedArenaPtr<T> {
|
impl<T: ?Sized + ArenaAllocated> PartialEq for TypedArenaPtr<T>
|
||||||
|
where
|
||||||
|
T::Payload: PartialEq,
|
||||||
|
{
|
||||||
fn eq(&self, other: &TypedArenaPtr<T>) -> bool {
|
fn eq(&self, other: &TypedArenaPtr<T>) -> bool {
|
||||||
self.0 == other.0 || **self == **other
|
std::ptr::addr_eq(self.0.as_ptr(), other.0.as_ptr()) || **self == **other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + PartialEq> Eq for TypedArenaPtr<T> {}
|
impl<T: ?Sized + ArenaAllocated> Eq for TypedArenaPtr<T> where T::Payload: Eq {}
|
||||||
|
|
||||||
impl<T: ?Sized + Ord> Ord for TypedArenaPtr<T> {
|
impl<T: ?Sized + ArenaAllocated> Ord for TypedArenaPtr<T>
|
||||||
|
where
|
||||||
|
T::Payload: Ord,
|
||||||
|
{
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
(**self).cmp(&**other)
|
(**self).cmp(&**other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Hash> Hash for TypedArenaPtr<T> {
|
impl<T: ?Sized + ArenaAllocated> Hash for TypedArenaPtr<T>
|
||||||
|
where
|
||||||
|
T::Payload: Hash,
|
||||||
|
{
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
fn hash<H: Hasher>(&self, hasher: &mut H) {
|
||||||
(self as &T).hash(hasher)
|
(self as &T::Payload).hash(hasher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized> Clone for TypedArenaPtr<T> {
|
impl<T: ?Sized + ArenaAllocated> Clone for TypedArenaPtr<T> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized> Copy for TypedArenaPtr<T> {}
|
impl<T: ?Sized + ArenaAllocated> Copy for TypedArenaPtr<T> {}
|
||||||
|
|
||||||
impl<T: ?Sized> Deref for TypedArenaPtr<T> {
|
impl<T: ?Sized + ArenaAllocated> Deref for TypedArenaPtr<T> {
|
||||||
type Target = T;
|
type Target = T::Payload;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
unsafe { self.0.as_ref() }
|
unsafe { self.0.as_ref() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized> DerefMut for TypedArenaPtr<T> {
|
impl<T: ?Sized + ArenaAllocated> DerefMut for TypedArenaPtr<T> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
unsafe { self.0.as_mut() }
|
unsafe { self.0.as_mut() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: fmt::Display> fmt::Display for TypedArenaPtr<T> {
|
impl<T: ArenaAllocated> fmt::Display for TypedArenaPtr<T>
|
||||||
|
where
|
||||||
|
T::Payload: fmt::Display,
|
||||||
|
{
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}", **self)
|
write!(f, "{}", (self as &T::Payload))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + ArenaAllocated> TypedArenaPtr<T> {
|
impl<T: ?Sized + ArenaAllocated> TypedArenaPtr<T> {
|
||||||
// data must be allocated in the arena already.
|
|
||||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn new(data: *mut T) -> Self {
|
pub fn as_ptr(&self) -> *mut T::Payload {
|
||||||
unsafe { TypedArenaPtr(ptr::NonNull::new_unchecked(data)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn as_ptr(&self) -> *mut T {
|
|
||||||
self.0.as_ptr()
|
self.0.as_ptr()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P, T: ?Sized + ArenaAllocated<Payload = ManuallyDrop<P>>> TypedArenaPtr<T> {
|
||||||
|
pub fn drop_payload(&mut self) {
|
||||||
|
self.set_tag(ArenaHeaderTag::Dropped);
|
||||||
|
unsafe { ManuallyDrop::drop(&mut *self.as_ptr()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> TypedArenaPtr<T>
|
||||||
|
where
|
||||||
|
T::Payload: Sized,
|
||||||
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn header_ptr(&self) -> *const ArenaHeader {
|
pub fn header_ptr(&self) -> *const ArenaHeader {
|
||||||
unsafe { self.as_ptr().byte_sub(T::header_offset_from_payload()) as *const _ }
|
unsafe { self.as_ptr().byte_sub(T::header_offset_from_payload()) as *const _ }
|
||||||
@@ -348,36 +356,77 @@ impl<T: ?Sized + ArenaAllocated> TypedArenaPtr<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ArenaAllocated: Sized {
|
pub trait AllocateInArena<AllocFor>
|
||||||
type PtrToAllocated;
|
where
|
||||||
|
AllocFor: ArenaAllocated,
|
||||||
|
{
|
||||||
|
fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr<AllocFor>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P, T: ArenaAllocated<Payload = P>> AllocateInArena<T> for P {
|
||||||
|
fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr<T> {
|
||||||
|
T::alloc(arena, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* apparently this overlaps the planket impl above somehow
|
||||||
|
impl<P, T: ArenaAllocated<Payload = ManuallyDrop<P>>> AllocateInArena<T> for P {
|
||||||
|
fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr<T> {
|
||||||
|
T::alloc(arena, ManuallyDrop::new(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub trait ArenaAllocated {
|
||||||
|
type Payload: ?Sized;
|
||||||
|
|
||||||
fn tag() -> ArenaHeaderTag;
|
fn tag() -> ArenaHeaderTag;
|
||||||
fn ptr_to_allocated(slab: &mut AllocSlab) -> Self::PtrToAllocated;
|
|
||||||
|
|
||||||
fn header_offset_from_payload() -> usize {
|
fn header_offset_from_payload() -> usize
|
||||||
|
where
|
||||||
|
Self::Payload: Sized,
|
||||||
|
{
|
||||||
header_offset_from_payload::<Self>()
|
header_offset_from_payload::<Self>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// - the caller must guarantee that the pointee type of UntypedArenaPtr is Self
|
||||||
|
/// - the pointer must be non-null
|
||||||
|
unsafe fn typed_ptr(ptr: UntypedArenaPtr) -> TypedArenaPtr<Self>
|
||||||
|
where
|
||||||
|
Self::Payload: Sized,
|
||||||
|
{
|
||||||
|
TypedArenaPtr(NonNull::new_unchecked(
|
||||||
|
ptr.payload_offset().cast_mut().cast::<Self::Payload>(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::missing_safety_doc)]
|
#[allow(clippy::missing_safety_doc)]
|
||||||
fn alloc(arena: &mut Arena, value: Self) -> Self::PtrToAllocated {
|
fn alloc(arena: &mut Arena, value: Self::Payload) -> TypedArenaPtr<Self>
|
||||||
|
where
|
||||||
|
Self::Payload: Sized,
|
||||||
|
{
|
||||||
let size = mem::size_of::<TypedAllocSlab<Self>>();
|
let size = mem::size_of::<TypedAllocSlab<Self>>();
|
||||||
let slab = Box::new(TypedAllocSlab {
|
let slab = Box::new(TypedAllocSlab {
|
||||||
slab: AllocSlab {
|
slab: AllocSlab {
|
||||||
next: arena.base.take(),
|
next: arena.base.take(),
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
_padding: 0,
|
|
||||||
header: ArenaHeader::build_with(size as u64, Self::tag()),
|
header: ArenaHeader::build_with(size as u64, Self::tag()),
|
||||||
},
|
},
|
||||||
payload: value,
|
payload: value,
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut untyped_slab = unsafe { Box::from_raw(Box::into_raw(slab) as *mut AllocSlab) };
|
let (allocated_ptr, untyped_slab) = slab.to_untyped();
|
||||||
let allocated_ptr = Self::ptr_to_allocated(untyped_slab.as_mut());
|
|
||||||
|
|
||||||
arena.base = Some(untyped_slab);
|
arena.base = Some(untyped_slab);
|
||||||
|
|
||||||
allocated_ptr
|
allocated_ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// - ptr points to an allocated slab of the correct kind
|
||||||
|
unsafe fn dealloc(ptr: NonNull<TypedAllocSlab<Self>>) {
|
||||||
|
drop(unsafe { Box::from_raw(ptr.as_ptr()) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -512,10 +561,7 @@ impl fmt::Display for F64Offset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ArenaAllocated for Integer {
|
impl ArenaAllocated for Integer {
|
||||||
type PtrToAllocated = TypedArenaPtr<Integer>;
|
type Payload = Self;
|
||||||
|
|
||||||
gen_ptr_to_allocated!(Integer);
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn tag() -> ArenaHeaderTag {
|
fn tag() -> ArenaHeaderTag {
|
||||||
ArenaHeaderTag::Integer
|
ArenaHeaderTag::Integer
|
||||||
@@ -523,32 +569,50 @@ impl ArenaAllocated for Integer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ArenaAllocated for Rational {
|
impl ArenaAllocated for Rational {
|
||||||
type PtrToAllocated = TypedArenaPtr<Rational>;
|
type Payload = Self;
|
||||||
|
|
||||||
gen_ptr_to_allocated!(Rational);
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn tag() -> ArenaHeaderTag {
|
fn tag() -> ArenaHeaderTag {
|
||||||
ArenaHeaderTag::Rational
|
ArenaHeaderTag::Rational
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AllocateInArena<LiveLoadState> for LiveLoadState {
|
||||||
|
fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr<LiveLoadState> {
|
||||||
|
LiveLoadState::alloc(arena, ManuallyDrop::new(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ArenaAllocated for LiveLoadState {
|
impl ArenaAllocated for LiveLoadState {
|
||||||
type PtrToAllocated = TypedArenaPtr<LiveLoadState>;
|
type Payload = ManuallyDrop<Self>;
|
||||||
|
|
||||||
gen_ptr_to_allocated!(LiveLoadState);
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn tag() -> ArenaHeaderTag {
|
fn tag() -> ArenaHeaderTag {
|
||||||
ArenaHeaderTag::LiveLoadState
|
ArenaHeaderTag::LiveLoadState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn dealloc(ptr: NonNull<TypedAllocSlab<Self>>) {
|
||||||
|
let mut slab = unsafe { Box::from_raw(ptr.as_ptr()) };
|
||||||
|
|
||||||
|
match slab.tag() {
|
||||||
|
ArenaHeaderTag::LiveLoadState | ArenaHeaderTag::InactiveLoadState => {
|
||||||
|
unsafe { ManuallyDrop::drop(&mut slab.payload) };
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::Dropped => {}
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop(slab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AllocateInArena<TcpListener> for TcpListener {
|
||||||
|
fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr<TcpListener> {
|
||||||
|
TcpListener::alloc(arena, ManuallyDrop::new(self))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ArenaAllocated for TcpListener {
|
impl ArenaAllocated for TcpListener {
|
||||||
type PtrToAllocated = TypedArenaPtr<TcpListener>;
|
type Payload = ManuallyDrop<Self>;
|
||||||
|
|
||||||
gen_ptr_to_allocated!(TcpListener);
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn tag() -> ArenaHeaderTag {
|
fn tag() -> ArenaHeaderTag {
|
||||||
ArenaHeaderTag::TcpListener
|
ArenaHeaderTag::TcpListener
|
||||||
@@ -557,10 +621,7 @@ impl ArenaAllocated for TcpListener {
|
|||||||
|
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
impl ArenaAllocated for HttpListener {
|
impl ArenaAllocated for HttpListener {
|
||||||
type PtrToAllocated = TypedArenaPtr<HttpListener>;
|
type Payload = Self;
|
||||||
|
|
||||||
gen_ptr_to_allocated!(HttpListener);
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn tag() -> ArenaHeaderTag {
|
fn tag() -> ArenaHeaderTag {
|
||||||
ArenaHeaderTag::HttpListener
|
ArenaHeaderTag::HttpListener
|
||||||
@@ -569,10 +630,7 @@ impl ArenaAllocated for HttpListener {
|
|||||||
|
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
impl ArenaAllocated for HttpResponse {
|
impl ArenaAllocated for HttpResponse {
|
||||||
type PtrToAllocated = TypedArenaPtr<HttpResponse>;
|
type Payload = Self;
|
||||||
|
|
||||||
gen_ptr_to_allocated!(HttpResponse);
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn tag() -> ArenaHeaderTag {
|
fn tag() -> ArenaHeaderTag {
|
||||||
ArenaHeaderTag::HttpResponse
|
ArenaHeaderTag::HttpResponse
|
||||||
@@ -580,65 +638,147 @@ impl ArenaAllocated for HttpResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ArenaAllocated for IndexPtr {
|
impl ArenaAllocated for IndexPtr {
|
||||||
type PtrToAllocated = TypedArenaPtr<IndexPtr>;
|
type Payload = Self;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn tag() -> ArenaHeaderTag {
|
fn tag() -> ArenaHeaderTag {
|
||||||
ArenaHeaderTag::IndexPtrUndefined
|
ArenaHeaderTag::IndexPtrUndefined
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn ptr_to_allocated(slab: &mut AllocSlab) -> Self::PtrToAllocated {
|
|
||||||
TypedArenaPtr::new(ptr::addr_of_mut!(slab.header) as *mut _)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn header_offset_from_payload() -> usize {
|
fn header_offset_from_payload() -> usize {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// - the caller must guarantee that the pointee type of UntypedArenaPtr is T
|
||||||
|
/// - the pointer must be non-null
|
||||||
|
unsafe fn typed_ptr(ptr: UntypedArenaPtr) -> TypedArenaPtr<Self> {
|
||||||
|
TypedArenaPtr(NonNull::new_unchecked(
|
||||||
|
ptr.get_ptr().cast_mut().cast::<IndexPtr>(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn alloc(arena: &mut Arena, value: Self) -> Self::PtrToAllocated {
|
fn alloc(arena: &mut Arena, value: Self) -> TypedArenaPtr<Self> {
|
||||||
let mut slab = Box::new(AllocSlab {
|
let slab = Box::new(IndexPtrSlab {
|
||||||
next: arena.base.take(),
|
next: arena.base.take(),
|
||||||
#[cfg(target_pointer_width = "32")]
|
index_ptr: value,
|
||||||
_padding: 0,
|
|
||||||
header: unsafe { mem::transmute(value) },
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let allocated_ptr =
|
let (allocated_ptr, untyped_slab) = slab.to_untyped();
|
||||||
TypedArenaPtr::new(unsafe { mem::transmute(ptr::addr_of_mut!(slab.header)) });
|
arena.base = Some(untyped_slab);
|
||||||
arena.base = Some(slab);
|
|
||||||
allocated_ptr
|
allocated_ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// - ptr points to an allocated slab of the correct kind
|
||||||
|
unsafe fn dealloc(ptr: NonNull<TypedAllocSlab<Self>>) {
|
||||||
|
drop(unsafe { Box::from_raw(ptr.as_ptr().cast::<IndexPtrSlab>()) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AllocSlab {
|
pub struct AllocSlab {
|
||||||
next: Option<Box<AllocSlab>>,
|
next: Option<UntypedArenaSlab>,
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
_padding: u32,
|
|
||||||
header: ArenaHeader,
|
header: ArenaHeader,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TypedAllocSlab<Payload> {
|
pub struct IndexPtrSlab {
|
||||||
slab: AllocSlab,
|
next: Option<UntypedArenaSlab>,
|
||||||
payload: Payload,
|
index_ptr: IndexPtr,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Payload: ArenaAllocated> TypedAllocSlab<Payload> {
|
const _: () = {
|
||||||
|
if std::mem::align_of::<AllocSlab>() < std::mem::align_of::<*const ()>() {
|
||||||
|
panic!("alignment of AllocSlab is too low");
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::mem::offset_of!(AllocSlab, header) % std::mem::align_of::<*const ()>() != 0 {
|
||||||
|
panic!("alignment of header not a multiple of pointers alignment");
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::mem::offset_of!(AllocSlab, header) != std::mem::offset_of!(IndexPtrSlab, index_ptr) {
|
||||||
|
panic!("IndexPtrSlab.index_ptr and AllocSlab.header are at different offsets");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
impl IndexPtrSlab {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_typed_arena_ptr(&mut self) -> TypedArenaPtr<Payload> {
|
pub fn to_untyped(self: Box<Self>) -> (TypedArenaPtr<IndexPtr>, UntypedArenaSlab) {
|
||||||
TypedArenaPtr::new(&mut self.payload as *mut _)
|
let raw_box = Box::into_raw(self);
|
||||||
|
|
||||||
|
// safety: the pointer from Box::into_raw fullfills addr_of_mut's saftey requirements
|
||||||
|
let index_ptr_ptr = unsafe { ptr::addr_of_mut!((*raw_box).index_ptr) };
|
||||||
|
let allocated_ptr = TypedArenaPtr(
|
||||||
|
// safety: the pointer points into a valid allocation so it is non null
|
||||||
|
unsafe { NonNull::new_unchecked(index_ptr_ptr) },
|
||||||
|
);
|
||||||
|
|
||||||
|
let untyped_arena = UntypedArenaSlab {
|
||||||
|
// safety: pointer from Box::into_raw is never null
|
||||||
|
slab: unsafe { NonNull::new_unchecked(raw_box.cast::<AllocSlab>()) },
|
||||||
|
tag: <IndexPtr as ArenaAllocated>::tag(),
|
||||||
|
};
|
||||||
|
|
||||||
|
(allocated_ptr, untyped_arena)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TypedAllocSlab<T: ?Sized + ArenaAllocated> {
|
||||||
|
slab: AllocSlab,
|
||||||
|
payload: T::Payload,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized + ArenaAllocated> TypedAllocSlab<T> {
|
||||||
|
pub fn tag(&self) -> ArenaHeaderTag {
|
||||||
|
self.slab.header.tag()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn payload(&mut self) -> &mut T::Payload {
|
||||||
|
&mut self.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn to_untyped(self: Box<Self>) -> (TypedArenaPtr<T>, UntypedArenaSlab) {
|
||||||
|
let raw_box = Box::into_raw(self);
|
||||||
|
|
||||||
|
// safety: the pointer from Box::into_raw fullfills addr_of_mut's saftey requirements
|
||||||
|
let payload_ptr = unsafe { addr_of_mut!((*raw_box).payload) };
|
||||||
|
|
||||||
|
(
|
||||||
|
TypedArenaPtr(unsafe {
|
||||||
|
// safety: the pointer points into a valid allocation so it is non null
|
||||||
|
ptr::NonNull::new_unchecked(payload_ptr)
|
||||||
|
}),
|
||||||
|
UntypedArenaSlab {
|
||||||
|
// safety: pointer from Box::into_raw is never null
|
||||||
|
slab: unsafe { NonNull::new_unchecked(raw_box.cast::<AllocSlab>()) },
|
||||||
|
tag: T::tag(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UntypedArenaSlab {
|
||||||
|
slab: NonNull<AllocSlab>,
|
||||||
|
tag: ArenaHeaderTag,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for UntypedArenaSlab {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { drop_slab_in_place(self.slab, self.tag) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Arena {
|
pub struct Arena {
|
||||||
base: Option<Box<AllocSlab>>,
|
base: Option<UntypedArenaSlab>,
|
||||||
pub f64_tbl: Arc<F64Table>,
|
pub f64_tbl: Arc<F64Table>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,15 +796,14 @@ impl Arena {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn drop_slab_in_place(value: &mut AllocSlab) {
|
unsafe fn drop_slab_in_place(value: NonNull<AllocSlab>, tag: ArenaHeaderTag) {
|
||||||
macro_rules! drop_typed_slab_in_place {
|
macro_rules! drop_typed_slab_in_place {
|
||||||
($payload: ty, $value: expr) => {
|
($payload: ty, $value: expr) => {
|
||||||
let slab: &mut TypedAllocSlab<$payload> = mem::transmute($value);
|
<$payload as ArenaAllocated>::dealloc($value.cast::<TypedAllocSlab<$payload>>())
|
||||||
ptr::drop_in_place(&mut slab.payload);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match value.header.tag() {
|
match tag {
|
||||||
ArenaHeaderTag::Integer => {
|
ArenaHeaderTag::Integer => {
|
||||||
drop_typed_slab_in_place!(Integer, value);
|
drop_typed_slab_in_place!(Integer, value);
|
||||||
}
|
}
|
||||||
@@ -722,28 +861,32 @@ unsafe fn drop_slab_in_place(value: &mut AllocSlab) {
|
|||||||
ArenaHeaderTag::StandardErrorStream => {
|
ArenaHeaderTag::StandardErrorStream => {
|
||||||
drop_typed_slab_in_place!(StandardErrorStream, value);
|
drop_typed_slab_in_place!(StandardErrorStream, value);
|
||||||
}
|
}
|
||||||
ArenaHeaderTag::NullStream
|
ArenaHeaderTag::IndexPtrUndefined
|
||||||
| ArenaHeaderTag::IndexPtrUndefined
|
|
||||||
| ArenaHeaderTag::IndexPtrDynamicUndefined
|
| ArenaHeaderTag::IndexPtrDynamicUndefined
|
||||||
| ArenaHeaderTag::IndexPtrDynamicIndex
|
| ArenaHeaderTag::IndexPtrDynamicIndex
|
||||||
| ArenaHeaderTag::IndexPtrIndex => {}
|
| ArenaHeaderTag::IndexPtrIndex => {
|
||||||
|
drop_typed_slab_in_place!(IndexPtr, value);
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::NullStream => {
|
||||||
|
unreachable!("NullStream is never arena allocated!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Arena {
|
impl Drop for Arena {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
// we un-nest UntypedArenaSlab to prevent stackoverflow due to the recursive drop
|
||||||
|
|
||||||
let mut ptr = self.base.take();
|
let mut ptr = self.base.take();
|
||||||
|
|
||||||
while let Some(mut slab) = ptr {
|
while let Some(mut slab) = ptr {
|
||||||
unsafe {
|
ptr = unsafe { slab.slab.as_mut() }.next.take();
|
||||||
drop_slab_in_place(&mut slab);
|
drop(slab);
|
||||||
ptr = slab.next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const_assert!(mem::size_of::<AllocSlab>() == 16);
|
const_assert!(mem::size_of::<AllocSlab>() <= 24);
|
||||||
const_assert!(mem::size_of::<OrderedFloat<f64>>() == 8);
|
const_assert!(mem::size_of::<OrderedFloat<f64>>() == 8);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -781,7 +924,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
|
||||||
fn heap_cell_value_const_cast() {
|
fn heap_cell_value_const_cast() {
|
||||||
let mut wam = MockWAM::new();
|
let mut wam = MockWAM::new();
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
@@ -825,7 +967,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on arena.rs UB")]
|
|
||||||
fn heap_put_literal_tests() {
|
fn heap_put_literal_tests() {
|
||||||
let mut wam = MockWAM::new();
|
let mut wam = MockWAM::new();
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||||
|
|
||||||
use crate::allocator::*;
|
use crate::allocator::*;
|
||||||
use crate::arena::*;
|
use crate::arena::*;
|
||||||
use crate::atom_table::*;
|
use crate::atom_table::*;
|
||||||
@@ -166,7 +168,7 @@ fn push_literal(interm: &mut Vec<ArithmeticTerm>, c: &Literal) -> Result<(), Ari
|
|||||||
Number::Float(OrderedFloat(std::f64::consts::PI)),
|
Number::Float(OrderedFloat(std::f64::consts::PI)),
|
||||||
)),
|
)),
|
||||||
Literal::Atom(name) if name == &atom!("epsilon") => interm.push(ArithmeticTerm::Number(
|
Literal::Atom(name) if name == &atom!("epsilon") => interm.push(ArithmeticTerm::Number(
|
||||||
Number::Float(OrderedFloat(std::f64::EPSILON)),
|
Number::Float(OrderedFloat(f64::EPSILON)),
|
||||||
)),
|
)),
|
||||||
_ => return Err(ArithmeticError::NonEvaluableFunctor(*c, 0)),
|
_ => return Err(ArithmeticError::NonEvaluableFunctor(*c, 0)),
|
||||||
}
|
}
|
||||||
@@ -545,26 +547,8 @@ impl PartialEq for Number {
|
|||||||
(&Number::Float(n1), Number::Integer(ref n2)) => {
|
(&Number::Float(n1), Number::Integer(ref n2)) => {
|
||||||
n1.eq(&OrderedFloat(n2.to_f64().value()))
|
n1.eq(&OrderedFloat(n2.to_f64().value()))
|
||||||
}
|
}
|
||||||
(Number::Integer(ref n1), Number::Rational(ref n2)) => {
|
(Number::Integer(ref n1), Number::Rational(ref n2)) => n1.num_eq(&**n2),
|
||||||
#[cfg(feature = "num")]
|
(Number::Rational(ref n1), Number::Integer(ref n2)) => n1.num_eq(&**n2),
|
||||||
{
|
|
||||||
&Rational::from(&**n1) == &**n2
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "num"))]
|
|
||||||
{
|
|
||||||
n1.num_eq(&**n2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Number::Rational(ref n1), Number::Integer(ref n2)) => {
|
|
||||||
#[cfg(feature = "num")]
|
|
||||||
{
|
|
||||||
n1 == &Rational::from(&**n2)
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "num"))]
|
|
||||||
{
|
|
||||||
n1.num_eq(&**n2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Number::Rational(ref n1), &Number::Float(n2)) => {
|
(Number::Rational(ref n1), &Number::Float(n2)) => {
|
||||||
OrderedFloat(n1.to_f64().value()).eq(&n2)
|
OrderedFloat(n1.to_f64().value()).eq(&n2)
|
||||||
}
|
}
|
||||||
@@ -643,24 +627,10 @@ impl Ord for Number {
|
|||||||
n1.cmp(&OrderedFloat(n2.to_f64().value()))
|
n1.cmp(&OrderedFloat(n2.to_f64().value()))
|
||||||
}
|
}
|
||||||
(&Number::Integer(n1), &Number::Rational(n2)) => {
|
(&Number::Integer(n1), &Number::Rational(n2)) => {
|
||||||
#[cfg(feature = "num")]
|
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||||
{
|
|
||||||
Rational::from(&**n1).cmp(n2)
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "num"))]
|
|
||||||
{
|
|
||||||
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(&Number::Rational(n1), &Number::Integer(n2)) => {
|
(&Number::Rational(n1), &Number::Integer(n2)) => {
|
||||||
#[cfg(feature = "num")]
|
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
||||||
{
|
|
||||||
(&**n1).cmp(&Rational::from(&**n2))
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "num"))]
|
|
||||||
{
|
|
||||||
(*n1).num_partial_cmp(&*n2).unwrap_or(Ordering::Less)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(&Number::Rational(n1), &Number::Float(n2)) => {
|
(&Number::Rational(n1), &Number::Float(n2)) => {
|
||||||
OrderedFloat(n1.to_f64().value()).cmp(&n2)
|
OrderedFloat(n1.to_f64().value()).cmp(&n2)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||||
|
|
||||||
use crate::parser::ast::MAX_ARITY;
|
use crate::parser::ast::MAX_ARITY;
|
||||||
use crate::raw_block::*;
|
use crate::raw_block::*;
|
||||||
use crate::rcu::{Rcu, RcuRef};
|
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
@@ -8,13 +9,16 @@ use std::hash::{Hash, Hasher};
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
use std::sync::Weak;
|
use std::sync::Weak;
|
||||||
|
|
||||||
|
use arcu::atomic::Arcu;
|
||||||
|
use arcu::epoch_counters::GlobalEpochCounterPool;
|
||||||
|
use arcu::rcu_ref::RcuRef;
|
||||||
|
use arcu::Rcu;
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
|
|
||||||
use scryer_modular_bitfield::prelude::*;
|
use scryer_modular_bitfield::prelude::*;
|
||||||
@@ -57,19 +61,8 @@ const ATOM_TABLE_ALIGN: usize = 8;
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn global_atom_table() -> &'static RwLock<Weak<AtomTable>> {
|
fn global_atom_table() -> &'static RwLock<Weak<AtomTable>> {
|
||||||
#[cfg(feature = "rust_beta_channel")]
|
static GLOBAL_ATOM_TABLE: RwLock<Weak<AtomTable>> = RwLock::new(Weak::new());
|
||||||
{
|
&GLOBAL_ATOM_TABLE
|
||||||
// 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<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<AtomTable>>> = OnceLock::new();
|
|
||||||
GLOBAL_ATOM_TABLE.get_or_init(|| RwLock::new(Weak::new()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -99,6 +92,12 @@ struct AtomHeader {
|
|||||||
padding: B13,
|
padding: B13,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct AtomData {
|
||||||
|
header: AtomHeader,
|
||||||
|
data: str,
|
||||||
|
}
|
||||||
|
|
||||||
impl AtomHeader {
|
impl AtomHeader {
|
||||||
fn build_with(len: u64) -> Self {
|
fn build_with(len: u64) -> Self {
|
||||||
AtomHeader::new().with_len(len).with_m(false)
|
AtomHeader::new().with_len(len).with_m(false)
|
||||||
@@ -177,19 +176,23 @@ impl Atom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_ptr(self) -> Option<AtomTableRef<u8>> {
|
pub fn as_ptr(self) -> Option<AtomTableRef<AtomData>> {
|
||||||
if self.is_static() {
|
if self.is_static() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let atom_table =
|
let atom_table =
|
||||||
arc_atom_table().expect("We should only have an Atom while there is an AtomTable");
|
arc_atom_table().expect("We should only have an Atom while there is an AtomTable");
|
||||||
unsafe {
|
|
||||||
AtomTableRef::try_map(atom_table.buf(), |buf| {
|
AtomTableRef::try_map(atom_table.inner.read(), |buf| unsafe {
|
||||||
(buf as *const u8)
|
let ptr = buf
|
||||||
.add((self.index as usize) - (STRINGS.len() << 3))
|
.block
|
||||||
.as_ref()
|
.base
|
||||||
})
|
.add((self.index as usize) - (STRINGS.len() << 3));
|
||||||
}
|
// TODO use std::ptr::from_raw_parts instead when feature ptr_metadata is stable rust-lang/rust#81513
|
||||||
|
let atom_data = &*(std::ptr::slice_from_raw_parts(ptr, 0) as *const AtomData);
|
||||||
|
let len = atom_data.header.len();
|
||||||
|
Some(&*(std::ptr::slice_from_raw_parts(ptr, len as usize) as *const AtomData))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,9 +206,8 @@ impl Atom {
|
|||||||
if self.is_static() {
|
if self.is_static() {
|
||||||
STRINGS[(self.index >> 3) as usize].len()
|
STRINGS[(self.index >> 3) as usize].len()
|
||||||
} else {
|
} else {
|
||||||
let ptr = self.as_ptr().unwrap();
|
let len: u64 = self.as_ptr().unwrap().header.len();
|
||||||
let ptr = ptr.deref() as *const u8 as *const AtomHeader;
|
len as usize
|
||||||
unsafe { ptr::read(ptr) }.len() as _
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,15 +239,7 @@ impl Atom {
|
|||||||
if self.is_static() {
|
if self.is_static() {
|
||||||
AtomString::Static(STRINGS[(self.index >> 3) as usize])
|
AtomString::Static(STRINGS[(self.index >> 3) as usize])
|
||||||
} else if let Some(ptr) = self.as_ptr() {
|
} else if let Some(ptr) = self.as_ptr() {
|
||||||
AtomString::Dynamic(AtomTableRef::map(ptr, |ptr| {
|
AtomString::Dynamic(AtomTableRef::map(ptr, |ptr| &ptr.data))
|
||||||
let header =
|
|
||||||
// Miri seems to hit this line a lot
|
|
||||||
unsafe { ptr::read::<AtomHeader>(ptr as *const u8 as *const AtomHeader) };
|
|
||||||
let len = header.len() as usize;
|
|
||||||
let buf = unsafe { (ptr as *const u8).add(mem::size_of::<AtomHeader>()) };
|
|
||||||
|
|
||||||
unsafe { str::from_utf8_unchecked(slice::from_raw_parts(buf, len)) }
|
|
||||||
}))
|
|
||||||
} else {
|
} else {
|
||||||
AtomString::Static(STRINGS[(self.index >> 3) as usize])
|
AtomString::Static(STRINGS[(self.index >> 3) as usize])
|
||||||
}
|
}
|
||||||
@@ -287,17 +281,17 @@ impl Ord for Atom {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InnerAtomTable {
|
pub struct InnerAtomTable {
|
||||||
block: RawBlock<AtomTable>,
|
block: RawBlock<AtomTable>,
|
||||||
pub table: Rcu<IndexSet<Atom>>,
|
pub table: Arcu<IndexSet<Atom>, GlobalEpochCounterPool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AtomTable {
|
pub struct AtomTable {
|
||||||
inner: Rcu<InnerAtomTable>,
|
inner: Arcu<InnerAtomTable, GlobalEpochCounterPool>,
|
||||||
// this lock is taking during resizing
|
// this lock is taking during resizing
|
||||||
update: Mutex<()>,
|
update: Mutex<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type AtomTableRef<M> = RcuRef<InnerAtomTable, M>;
|
pub type AtomTableRef<M> = arcu::rcu_ref::RcuRef<InnerAtomTable, M>;
|
||||||
|
|
||||||
impl InnerAtomTable {
|
impl InnerAtomTable {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -305,7 +299,7 @@ impl InnerAtomTable {
|
|||||||
STATIC_ATOMS_MAP
|
STATIC_ATOMS_MAP
|
||||||
.get(string)
|
.get(string)
|
||||||
.cloned()
|
.cloned()
|
||||||
.or_else(|| self.table.active_epoch().get(string).cloned())
|
.or_else(|| self.table.read().get(string).cloned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,10 +317,13 @@ impl AtomTable {
|
|||||||
atom_table
|
atom_table
|
||||||
} else {
|
} else {
|
||||||
let atom_table = Arc::new(Self {
|
let atom_table = Arc::new(Self {
|
||||||
inner: Rcu::new(InnerAtomTable {
|
inner: Arcu::new(
|
||||||
block: RawBlock::new(),
|
InnerAtomTable {
|
||||||
table: Rcu::new(IndexSet::new()),
|
block: RawBlock::new(),
|
||||||
}),
|
table: Arcu::new(IndexSet::new(), GlobalEpochCounterPool),
|
||||||
|
},
|
||||||
|
GlobalEpochCounterPool,
|
||||||
|
),
|
||||||
update: Mutex::new(()),
|
update: Mutex::new(()),
|
||||||
});
|
});
|
||||||
*guard = Arc::downgrade(&atom_table);
|
*guard = Arc::downgrade(&atom_table);
|
||||||
@@ -335,21 +332,14 @@ impl AtomTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn buf(&self) -> AtomTableRef<u8> {
|
|
||||||
AtomTableRef::<InnerAtomTable>::map(self.inner.active_epoch(), |inner| {
|
|
||||||
unsafe { inner.block.base.as_ref() }.unwrap()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn active_table(&self) -> RcuRef<IndexSet<Atom>, IndexSet<Atom>> {
|
pub fn active_table(&self) -> RcuRef<IndexSet<Atom>, IndexSet<Atom>> {
|
||||||
self.inner.active_epoch().table.active_epoch()
|
self.inner.read().table.read()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_with(atom_table: &AtomTable, string: &str) -> Atom {
|
pub fn build_with(atom_table: &AtomTable, string: &str) -> Atom {
|
||||||
loop {
|
loop {
|
||||||
let mut block_epoch = atom_table.inner.active_epoch();
|
let mut block_epoch = atom_table.inner.read();
|
||||||
let mut table_epoch = block_epoch.table.active_epoch();
|
let mut table_epoch = block_epoch.table.read();
|
||||||
|
|
||||||
if let Some(atom) = block_epoch.lookup_str(string) {
|
if let Some(atom) = block_epoch.lookup_str(string) {
|
||||||
return atom;
|
return atom;
|
||||||
@@ -358,10 +348,8 @@ impl AtomTable {
|
|||||||
// take a lock to prevent concurrent updates
|
// take a lock to prevent concurrent updates
|
||||||
let update_guard = atom_table.update.lock().unwrap();
|
let update_guard = atom_table.update.lock().unwrap();
|
||||||
|
|
||||||
let is_same_allocation =
|
let is_same_allocation = RcuRef::same_epoch(&block_epoch, &atom_table.inner.read());
|
||||||
RcuRef::same_epoch(&block_epoch, &atom_table.inner.active_epoch());
|
let is_same_atom_list = RcuRef::same_epoch(&table_epoch, &block_epoch.table.read());
|
||||||
let is_same_atom_list =
|
|
||||||
RcuRef::same_epoch(&table_epoch, &block_epoch.table.active_epoch());
|
|
||||||
|
|
||||||
if !(is_same_allocation && is_same_atom_list) {
|
if !(is_same_allocation && is_same_atom_list) {
|
||||||
// some other thread raced us between our lookup and
|
// some other thread raced us between our lookup and
|
||||||
@@ -371,8 +359,7 @@ impl AtomTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let size = mem::size_of::<AtomHeader>() + string.len();
|
let size = mem::size_of::<AtomHeader>() + string.len();
|
||||||
let align_offset = 8 * mem::align_of::<AtomHeader>();
|
let size = size.next_multiple_of(AtomTable::align());
|
||||||
let size = (size & !(align_offset - 1)) + align_offset;
|
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let len_ptr = loop {
|
let len_ptr = loop {
|
||||||
@@ -381,14 +368,14 @@ impl AtomTable {
|
|||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
// garbage collection would go here
|
// garbage collection would go here
|
||||||
let new_block = block_epoch.block.grow_new().unwrap();
|
let new_block = block_epoch.block.grow_new().unwrap();
|
||||||
let new_table = Rcu::new(table_epoch.clone());
|
let new_table = Arcu::new(table_epoch.clone(), GlobalEpochCounterPool);
|
||||||
let new_alloc = InnerAtomTable {
|
let new_alloc = InnerAtomTable {
|
||||||
block: new_block,
|
block: new_block,
|
||||||
table: new_table,
|
table: new_table,
|
||||||
};
|
};
|
||||||
atom_table.inner.replace(new_alloc);
|
atom_table.inner.replace(new_alloc);
|
||||||
block_epoch = atom_table.inner.active_epoch();
|
block_epoch = atom_table.inner.read();
|
||||||
table_epoch = block_epoch.table.active_epoch();
|
table_epoch = block_epoch.table.read();
|
||||||
} else {
|
} else {
|
||||||
break ptr;
|
break ptr;
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/ffi.rs
26
src/ffi.rs
@@ -436,18 +436,20 @@ impl ForeignFunctionTable {
|
|||||||
}
|
}
|
||||||
libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64),
|
libffi::raw::FFI_TYPE_SINT64 => read_and_push_int!(i64),
|
||||||
libffi::raw::FFI_TYPE_POINTER => read_and_push_int!(i64),
|
libffi::raw::FFI_TYPE_POINTER => read_and_push_int!(i64),
|
||||||
libffi::raw::FFI_TYPE_FLOAT => {
|
libffi::raw::FFI_TYPE_FLOAT => {
|
||||||
field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<f32>()));
|
field_ptr =
|
||||||
let n = std::ptr::read(field_ptr as *mut f32);
|
field_ptr.add(field_ptr.align_offset(std::mem::align_of::<f32>()));
|
||||||
returns.push(Value::Float(f32::from(n).into()));
|
let n: f32 = std::ptr::read(field_ptr as *mut f32);
|
||||||
field_ptr = field_ptr.add(std::mem::size_of::<f32>());
|
returns.push(Value::Float(n.into()));
|
||||||
}
|
field_ptr = field_ptr.add(std::mem::size_of::<f32>());
|
||||||
libffi::raw::FFI_TYPE_DOUBLE => {
|
}
|
||||||
field_ptr = field_ptr.add(field_ptr.align_offset(std::mem::align_of::<f64>()));
|
libffi::raw::FFI_TYPE_DOUBLE => {
|
||||||
let n = std::ptr::read(field_ptr as *mut f64);
|
field_ptr =
|
||||||
returns.push(Value::Float(f64::from(n)));
|
field_ptr.add(field_ptr.align_offset(std::mem::align_of::<f64>()));
|
||||||
field_ptr = field_ptr.add(std::mem::size_of::<f64>());
|
let n: f64 = std::ptr::read(field_ptr as *mut f64);
|
||||||
}
|
returns.push(Value::Float(n));
|
||||||
|
field_ptr = field_ptr.add(std::mem::size_of::<f64>());
|
||||||
|
}
|
||||||
libffi::raw::FFI_TYPE_STRUCT => {
|
libffi::raw::FFI_TYPE_STRUCT => {
|
||||||
let substruct = struct_type.atom_fields[i].as_str();
|
let substruct = struct_type.atom_fields[i].as_str();
|
||||||
let struct_type = self
|
let struct_type = self
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) use crate::machine::gc::StacklessPreOrderHeapIter;
|
pub(crate) use crate::machine::gc::StacklessPreOrderHeapIter;
|
||||||
|
|
||||||
@@ -1758,7 +1760,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on atom_table.rs UB")]
|
|
||||||
fn heap_stackful_iter_tests() {
|
fn heap_stackful_iter_tests() {
|
||||||
let mut wam = MockWAM::new();
|
let mut wam = MockWAM::new();
|
||||||
|
|
||||||
@@ -2351,7 +2352,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on atom_table.rs UB")]
|
|
||||||
fn heap_stackful_post_order_iter() {
|
fn heap_stackful_post_order_iter() {
|
||||||
let mut wam = MockWAM::new();
|
let mut wam = MockWAM::new();
|
||||||
|
|
||||||
@@ -2835,7 +2835,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on atom_table.rs UB")]
|
|
||||||
fn heap_stackless_post_order_iter() {
|
fn heap_stackless_post_order_iter() {
|
||||||
let mut wam = MockWAM::new();
|
let mut wam = MockWAM::new();
|
||||||
|
|
||||||
|
|||||||
@@ -1841,7 +1841,7 @@ mod tests {
|
|||||||
use crate::machine::mock_wam::*;
|
use crate::machine::mock_wam::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn term_printing_tests() {
|
fn term_printing_tests() {
|
||||||
let mut wam = MockWAM::new();
|
let mut wam = MockWAM::new();
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,6 @@ pub mod types;
|
|||||||
|
|
||||||
use instructions::instr;
|
use instructions::instr;
|
||||||
|
|
||||||
mod rcu;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
|||||||
@@ -641,10 +641,17 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
|||||||
let n1_i = n1.get_num();
|
let n1_i = n1.get_num();
|
||||||
let n2_i = n2.get_num();
|
let n2_i = n2.get_num();
|
||||||
|
|
||||||
|
// FIXME(arithmetic_overflow)
|
||||||
|
// what should this do for too large n2,
|
||||||
|
// - logical right shift should probably turn to 0
|
||||||
|
// - arithmetic right shift should maybe differ for negative numbers
|
||||||
|
//
|
||||||
|
// note: negaitve n2 is already handled above
|
||||||
|
#[allow(arithmetic_overflow)]
|
||||||
if let Ok(n2) = usize::try_from(n2_i) {
|
if let Ok(n2) = usize::try_from(n2_i) {
|
||||||
Ok(Number::arena_from(n1_i >> n2, arena))
|
Ok(Number::arena_from(n1_i >> n2, arena))
|
||||||
} else {
|
} else {
|
||||||
Ok(Number::arena_from(n1_i >> usize::max_value(), arena))
|
Ok(Number::arena_from(n1_i >> usize::MAX, arena))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||||
@@ -654,25 +661,19 @@ pub(crate) fn shr(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
|||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(n2) => Ok(Number::arena_from(n1 >> n2, arena)),
|
Ok(n2) => Ok(Number::arena_from(n1 >> n2, arena)),
|
||||||
Err(_) => Ok(Number::arena_from(n1 >> usize::max_value(), arena)),
|
Err(_) => Ok(Number::arena_from(n1 >> usize::MAX, arena)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
|
(Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
|
||||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
|
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
|
||||||
_ => Ok(Number::arena_from(
|
_ => Ok(Number::arena_from(Integer::from(&*n1 >> usize::MAX), arena)),
|
||||||
Integer::from(&*n1 >> usize::max_value()),
|
|
||||||
arena,
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
(Number::Integer(n1), Number::Integer(n2)) => {
|
(Number::Integer(n1), Number::Integer(n2)) => {
|
||||||
let result: Result<usize, _> = (&*n2).try_into();
|
let result: Result<usize, _> = (&*n2).try_into();
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
|
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 >> n2), arena)),
|
||||||
Err(_) => Ok(Number::arena_from(
|
Err(_) => Ok(Number::arena_from(Integer::from(&*n1 >> usize::MAX), arena)),
|
||||||
Integer::from(&*n1 >> usize::max_value()),
|
|
||||||
arena,
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
(Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||||
@@ -700,7 +701,7 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
|||||||
Ok(Number::arena_from(n1_i << n2, arena))
|
Ok(Number::arena_from(n1_i << n2, arena))
|
||||||
} else {
|
} else {
|
||||||
let n1 = Integer::from(n1_i);
|
let n1 = Integer::from(n1_i);
|
||||||
Ok(Number::arena_from(n1 << usize::max_value(), arena))
|
Ok(Number::arena_from(n1 << usize::MAX, arena))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
(Number::Fixnum(n1), Number::Integer(n2)) => {
|
||||||
@@ -708,22 +709,16 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
|
|||||||
|
|
||||||
match (&*n2).try_into() as Result<usize, _> {
|
match (&*n2).try_into() as Result<usize, _> {
|
||||||
Ok(n2) => Ok(Number::arena_from(n1 << n2, arena)),
|
Ok(n2) => Ok(Number::arena_from(n1 << n2, arena)),
|
||||||
_ => Ok(Number::arena_from(n1 << usize::max_value(), arena)),
|
_ => Ok(Number::arena_from(n1 << usize::MAX, arena)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
|
(Number::Integer(n1), Number::Fixnum(n2)) => match usize::try_from(n2.get_num()) {
|
||||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
|
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
|
||||||
_ => Ok(Number::arena_from(
|
_ => Ok(Number::arena_from(Integer::from(&*n1 << usize::MAX), arena)),
|
||||||
Integer::from(&*n1 << usize::max_value()),
|
|
||||||
arena,
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
(Number::Integer(n1), Number::Integer(n2)) => match (&*n2).try_into() as Result<usize, _> {
|
(Number::Integer(n1), Number::Integer(n2)) => match (&*n2).try_into() as Result<usize, _> {
|
||||||
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
|
Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
|
||||||
_ => Ok(Number::arena_from(
|
_ => Ok(Number::arena_from(Integer::from(&*n1 << usize::MAX), arena)),
|
||||||
Integer::from(&*n1 << usize::max_value()),
|
|
||||||
arena,
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
(Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
(Number::Integer(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||||
(Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
(Number::Fixnum(_), n2) => Err(numerical_type_error(ValidType::Integer, n2, stub_gen)),
|
||||||
@@ -1420,7 +1415,6 @@ mod tests {
|
|||||||
use crate::machine::mock_wam::*;
|
use crate::machine::mock_wam::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
|
||||||
fn arith_eval_by_metacall_tests() {
|
fn arith_eval_by_metacall_tests() {
|
||||||
let mut wam = MachineState::new();
|
let mut wam = MachineState::new();
|
||||||
let mut op_dir = default_op_dir();
|
let mut op_dir = default_op_dir();
|
||||||
|
|||||||
@@ -398,7 +398,6 @@ mod tests {
|
|||||||
use crate::machine::mock_wam::*;
|
use crate::machine::mock_wam::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on atom_table.rs UB")]
|
|
||||||
fn copier_tests() {
|
fn copier_tests() {
|
||||||
let mut wam = MockWAM::new();
|
let mut wam = MockWAM::new();
|
||||||
|
|
||||||
|
|||||||
@@ -369,7 +369,6 @@ mod tests {
|
|||||||
use crate::machine::mock_wam::*;
|
use crate::machine::mock_wam::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
|
||||||
fn heap_marking_tests() {
|
fn heap_marking_tests() {
|
||||||
let mut wam = MockWAM::new();
|
let mut wam = MockWAM::new();
|
||||||
|
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ impl TryFrom<HeapCellValue> for Literal {
|
|||||||
(ArenaHeaderTag::Rational, n) => {
|
(ArenaHeaderTag::Rational, n) => {
|
||||||
Ok(Literal::Rational(n))
|
Ok(Literal::Rational(n))
|
||||||
}
|
}
|
||||||
(ArenaHeaderTag::IndexPtr, _ip) => {
|
(ArenaHeaderTag::IndexPtr, ip) => {
|
||||||
Ok(Literal::CodeIndex(CodeIndex::from(cons_ptr)))
|
Ok(Literal::CodeIndex(CodeIndex::from(ip)))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
Err(())
|
Err(())
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ impl Machine {
|
|||||||
printer.quoted = true;
|
printer.quoted = true;
|
||||||
printer.max_depth = 1000; // NOTE: set this to 0 for unbounded depth
|
printer.max_depth = 1000; // NOTE: set this to 0 for unbounded depth
|
||||||
printer.double_quotes = true;
|
printer.double_quotes = true;
|
||||||
printer.var_names = var_names.clone();
|
printer.var_names.clone_from(&var_names);
|
||||||
|
|
||||||
let outputter = printer.print();
|
let outputter = printer.print();
|
||||||
|
|
||||||
@@ -238,7 +238,7 @@ mod tests {
|
|||||||
use crate::machine::{QueryMatch, QueryResolution, Value};
|
use crate::machine::{QueryMatch, QueryResolution, Value};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn programatic_query() {
|
fn programatic_query() {
|
||||||
let mut machine = Machine::new_lib();
|
let mut machine = Machine::new_lib();
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn failing_query() {
|
fn failing_query() {
|
||||||
let mut machine = Machine::new_lib();
|
let mut machine = Machine::new_lib();
|
||||||
let query = String::from(r#"triple("a",P,"b")."#);
|
let query = String::from(r#"triple("a",P,"b")."#);
|
||||||
@@ -292,7 +292,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore)]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn complex_results() {
|
fn complex_results() {
|
||||||
let mut machine = Machine::new_lib();
|
let mut machine = Machine::new_lib();
|
||||||
machine.load_module_string(
|
machine.load_module_string(
|
||||||
@@ -349,7 +349,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn empty_predicate() {
|
fn empty_predicate() {
|
||||||
let mut machine = Machine::new_lib();
|
let mut machine = Machine::new_lib();
|
||||||
machine.load_module_string(
|
machine.load_module_string(
|
||||||
@@ -365,7 +365,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn list_results() {
|
fn list_results() {
|
||||||
let mut machine = Machine::new_lib();
|
let mut machine = Machine::new_lib();
|
||||||
machine.load_module_string(
|
machine.load_module_string(
|
||||||
@@ -394,7 +394,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn consult() {
|
fn consult() {
|
||||||
let mut machine = Machine::new_lib();
|
let mut machine = Machine::new_lib();
|
||||||
|
|
||||||
@@ -453,7 +453,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn integration_test() {
|
fn integration_test() {
|
||||||
let mut machine = Machine::new_lib();
|
let mut machine = Machine::new_lib();
|
||||||
|
|
||||||
@@ -488,19 +488,15 @@ mod tests {
|
|||||||
} else if let Some(result) = block.strip_prefix("result") {
|
} else if let Some(result) = block.strip_prefix("result") {
|
||||||
i += 1;
|
i += 1;
|
||||||
if let Some(Ok(ref last_result)) = last_result {
|
if let Some(Ok(ref last_result)) = last_result {
|
||||||
println!(
|
println!("\n\n=====Result No. {i}=======\n{last_result}\n===============");
|
||||||
"\n\n=====Result No. {}=======\n{}\n===============",
|
assert_eq!(last_result.to_string(), result.to_string().trim(),)
|
||||||
i,
|
|
||||||
last_result.to_string().trim()
|
|
||||||
);
|
|
||||||
assert_eq!(last_result.to_string().trim(), result.to_string().trim(),)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn findall() {
|
fn findall() {
|
||||||
let mut machine = Machine::new_lib();
|
let mut machine = Machine::new_lib();
|
||||||
|
|
||||||
@@ -533,6 +529,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn dont_return_partial_matches() {
|
fn dont_return_partial_matches() {
|
||||||
let mut machine = Machine::new_lib();
|
let mut machine = Machine::new_lib();
|
||||||
|
|
||||||
@@ -556,6 +553,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn dont_return_partial_matches_without_discountiguous() {
|
fn dont_return_partial_matches_without_discountiguous() {
|
||||||
let mut machine = Machine::new_lib();
|
let mut machine = Machine::new_lib();
|
||||||
|
|
||||||
@@ -587,6 +585,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn non_existent_predicate_should_not_cause_panic_when_other_predicates_are_defined() {
|
fn non_existent_predicate_should_not_cause_panic_when_other_predicates_are_defined() {
|
||||||
let mut machine = Machine::new_lib();
|
let mut machine = Machine::new_lib();
|
||||||
|
|
||||||
@@ -611,6 +610,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn issue_2341() {
|
fn issue_2341() {
|
||||||
let mut machine = Machine::new_lib();
|
let mut machine = Machine::new_lib();
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ use crate::parser::ast::*;
|
|||||||
|
|
||||||
use fxhash::FxBuildHasher;
|
use fxhash::FxBuildHasher;
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
pub use ref_thread_local::RefThreadLocal;
|
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@@ -1176,7 +1175,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
|||||||
ListingSource::File(filename, path_buf),
|
ListingSource::File(filename, path_buf),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ModuleSource::Library(library) => match LIBRARIES.borrow().get(&*library.as_str()) {
|
ModuleSource::Library(library) => match libraries::get(&library.as_str()) {
|
||||||
Some(code) => {
|
Some(code) => {
|
||||||
if let Some(module) = self.wam_prelude.indices.modules.get(&library) {
|
if let Some(module) = self.wam_prelude.indices.modules.get(&library) {
|
||||||
if let ListingSource::DynamicallyGenerated = &module.listing_src {
|
if let ListingSource::DynamicallyGenerated = &module.listing_src {
|
||||||
@@ -1257,7 +1256,7 @@ impl<'a, LS: LoadState<'a>> Loader<'a, LS> {
|
|||||||
ListingSource::File(filename, path_buf),
|
ListingSource::File(filename, path_buf),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ModuleSource::Library(library) => match LIBRARIES.borrow().get(&*library.as_str()) {
|
ModuleSource::Library(library) => match libraries::get(&library.as_str()) {
|
||||||
Some(code) => {
|
Some(code) => {
|
||||||
if self.wam_prelude.indices.modules.contains_key(&library) {
|
if self.wam_prelude.indices.modules.contains_key(&library) {
|
||||||
return self.import_qualified_module(library, exports);
|
return self.import_qualified_module(library, exports);
|
||||||
|
|||||||
@@ -304,11 +304,15 @@ impl<'a> LoadState<'a> for LiveLoadAndMachineState<'a> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn evacuate(mut loader: Loader<'a, Self>) -> Result<Self::Evacuable, SessionError> {
|
fn evacuate(mut loader: Loader<'a, Self>) -> Result<Self::Evacuable, SessionError> {
|
||||||
loader
|
if loader.payload.load_state.get_tag() != ArenaHeaderTag::Dropped {
|
||||||
.payload
|
loader
|
||||||
.load_state
|
.payload
|
||||||
.set_tag(ArenaHeaderTag::InactiveLoadState);
|
.load_state
|
||||||
Ok(loader.payload.load_state)
|
.set_tag(ArenaHeaderTag::InactiveLoadState);
|
||||||
|
Ok(loader.payload.load_state)
|
||||||
|
} else {
|
||||||
|
unreachable!("we never evacuate after dropping")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -319,7 +323,7 @@ impl<'a> LoadState<'a> for LiveLoadAndMachineState<'a> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn reset_machine(loader: &mut Loader<'a, Self>) {
|
fn reset_machine(loader: &mut Loader<'a, Self>) {
|
||||||
if loader.payload.load_state.get_tag() != ArenaHeaderTag::Dropped {
|
if loader.payload.load_state.get_tag() != ArenaHeaderTag::Dropped {
|
||||||
loader.payload.load_state.set_tag(ArenaHeaderTag::Dropped);
|
loader.payload.load_state.drop_payload();
|
||||||
loader.reset_machine();
|
loader.reset_machine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -353,7 +357,7 @@ impl<'a> LoadState<'a> for LiveLoadAndMachineState<'a> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn err_on_builtin_module_overwrite(module_name: Atom) -> Result<(), SessionError> {
|
fn err_on_builtin_module_overwrite(module_name: Atom) -> Result<(), SessionError> {
|
||||||
if LIBRARIES.borrow().contains_key(&*module_name.as_str()) {
|
if libraries::contains(&module_name.as_str()) {
|
||||||
Err(SessionError::CannotOverwriteBuiltInModule(module_name))
|
Err(SessionError::CannotOverwriteBuiltInModule(module_name))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1757,7 +1761,7 @@ impl Machine {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_load_state_payload(&mut self) {
|
pub(crate) fn push_load_state_payload(&mut self) {
|
||||||
let payload = arena_alloc!(
|
let payload: TypedArenaPtr<LiveLoadState> = arena_alloc!(
|
||||||
LoadStatePayload::new(self.code.len(), LiveTermStream::new(ListingSource::User),),
|
LoadStatePayload::new(self.code.len(), LiveTermStream::new(ListingSource::User),),
|
||||||
&mut self.machine_st.arena
|
&mut self.machine_st.arena
|
||||||
);
|
);
|
||||||
@@ -1784,11 +1788,8 @@ impl Machine {
|
|||||||
(HeapCellValueTag::Cons, cons_ptr) => {
|
(HeapCellValueTag::Cons, cons_ptr) => {
|
||||||
match_untyped_arena_ptr!(cons_ptr,
|
match_untyped_arena_ptr!(cons_ptr,
|
||||||
(ArenaHeaderTag::LiveLoadState, payload) => {
|
(ArenaHeaderTag::LiveLoadState, payload) => {
|
||||||
unsafe {
|
let mut payload = payload;
|
||||||
std::ptr::drop_in_place(
|
payload.drop_payload()
|
||||||
payload.as_ptr() as *mut LiveLoadState,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||||
|
|
||||||
use crate::parser::ast::*;
|
use crate::parser::ast::*;
|
||||||
|
|
||||||
use crate::arena::*;
|
use crate::arena::*;
|
||||||
@@ -157,13 +159,6 @@ impl From<CodeIndex> for UntypedArenaPtr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<UntypedArenaPtr> for CodeIndex {
|
|
||||||
#[inline(always)]
|
|
||||||
fn from(ptr: UntypedArenaPtr) -> CodeIndex {
|
|
||||||
CodeIndex(TypedArenaPtr::new(ptr.get_ptr() as *mut IndexPtr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<TypedArenaPtr<IndexPtr>> for CodeIndex {
|
impl From<TypedArenaPtr<IndexPtr>> for CodeIndex {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(ptr: TypedArenaPtr<IndexPtr>) -> CodeIndex {
|
fn from(ptr: TypedArenaPtr<IndexPtr>) -> CodeIndex {
|
||||||
|
|||||||
@@ -679,15 +679,13 @@ impl MachineState {
|
|||||||
indices: &mut IndexStore,
|
indices: &mut IndexStore,
|
||||||
) -> CallResult {
|
) -> CallResult {
|
||||||
if let Stream::Readline(ptr) = stream {
|
if let Stream::Readline(ptr) = stream {
|
||||||
unsafe {
|
let readline = unsafe { ptr.as_ptr().as_mut() }.unwrap();
|
||||||
let readline = ptr.as_ptr().as_mut().unwrap();
|
readline.set_atoms_for_completion(&self.atom_tbl);
|
||||||
readline.set_atoms_for_completion(&self.atom_tbl);
|
return self.read_term(
|
||||||
return self.read_term(
|
stream,
|
||||||
stream,
|
indices,
|
||||||
indices,
|
MachineState::read_term_from_user_input_eof_handler,
|
||||||
MachineState::read_term_from_user_input_eof_handler,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Stream::Byte(_) = stream {
|
if let Stream::Byte(_) = stream {
|
||||||
|
|||||||
@@ -260,7 +260,6 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
|
||||||
fn unify_tests() {
|
fn unify_tests() {
|
||||||
let mut wam = MachineState::new();
|
let mut wam = MachineState::new();
|
||||||
let mut op_dir = default_op_dir();
|
let mut op_dir = default_op_dir();
|
||||||
@@ -482,7 +481,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
|
||||||
fn test_unify_with_occurs_check() {
|
fn test_unify_with_occurs_check() {
|
||||||
let mut wam = MachineState::new();
|
let mut wam = MachineState::new();
|
||||||
let mut op_dir = default_op_dir();
|
let mut op_dir = default_op_dir();
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ use std::env;
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use self::config::MachineConfig;
|
use self::config::MachineConfig;
|
||||||
use self::parsed_results::*;
|
use self::parsed_results::*;
|
||||||
@@ -110,10 +111,34 @@ impl LoadContext {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn current_dir() -> PathBuf {
|
fn current_dir() -> PathBuf {
|
||||||
env::current_dir().unwrap_or(PathBuf::from("./"))
|
if !cfg!(miri) {
|
||||||
|
env::current_dir().unwrap_or(PathBuf::from("./"))
|
||||||
|
} else {
|
||||||
|
PathBuf::from("./")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/libraries.rs"));
|
mod libraries {
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/libraries.rs"));
|
||||||
|
|
||||||
|
pub(crate) fn contains(name: &str) -> bool {
|
||||||
|
LIBRARIES.with(|libs| libs.contains_key(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get(name: &str) -> Option<&'static str> {
|
||||||
|
LIBRARIES.with(|libs| libs.get(name).copied())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
std::thread_local! {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
static LIBRARIES2 : IndexMap<&'static str, &'static str> = {
|
||||||
|
let mut m = IndexMap::new();
|
||||||
|
m.insert("test", "test2");
|
||||||
|
m
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub static BREAK_FROM_DISPATCH_LOOP_LOC: usize = 0;
|
pub static BREAK_FROM_DISPATCH_LOOP_LOC: usize = 0;
|
||||||
pub static INSTALL_VERIFY_ATTR_INTERRUPT: usize = 1;
|
pub static INSTALL_VERIFY_ATTR_INTERRUPT: usize = 1;
|
||||||
@@ -448,8 +473,6 @@ impl Machine {
|
|||||||
|
|
||||||
#[allow(clippy::new_without_default)]
|
#[allow(clippy::new_without_default)]
|
||||||
pub fn new(config: MachineConfig) -> Self {
|
pub fn new(config: MachineConfig) -> Self {
|
||||||
use ref_thread_local::RefThreadLocal;
|
|
||||||
|
|
||||||
let args = MachineArgs::new();
|
let args = MachineArgs::new();
|
||||||
let mut machine_st = MachineState::new();
|
let mut machine_st = MachineState::new();
|
||||||
|
|
||||||
@@ -488,7 +511,8 @@ impl Machine {
|
|||||||
|
|
||||||
bootstrapping_compile(
|
bootstrapping_compile(
|
||||||
Stream::from_static_string(
|
Stream::from_static_string(
|
||||||
LIBRARIES.borrow()["ops_and_meta_predicates"],
|
libraries::get("ops_and_meta_predicates")
|
||||||
|
.expect("library ops_and_meta_predicates should exist"),
|
||||||
&mut wam.machine_st.arena,
|
&mut wam.machine_st.arena,
|
||||||
),
|
),
|
||||||
&mut wam,
|
&mut wam,
|
||||||
@@ -500,7 +524,10 @@ impl Machine {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
bootstrapping_compile(
|
bootstrapping_compile(
|
||||||
Stream::from_static_string(LIBRARIES.borrow()["builtins"], &mut wam.machine_st.arena),
|
Stream::from_static_string(
|
||||||
|
libraries::get("builtins").expect("library builtins should exist"),
|
||||||
|
&mut wam.machine_st.arena,
|
||||||
|
),
|
||||||
&mut wam,
|
&mut wam,
|
||||||
ListingSource::from_file_and_path(atom!("builtins.pl"), lib_path.clone()),
|
ListingSource::from_file_and_path(atom!("builtins.pl"), lib_path.clone()),
|
||||||
)
|
)
|
||||||
@@ -1235,33 +1262,25 @@ impl Machine {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn run_cleaners(&mut self) -> bool {
|
fn run_cleaners(&mut self) -> bool {
|
||||||
use std::sync::Once;
|
static CLEANER_INIT: OnceLock<(usize, usize)> = OnceLock::new();
|
||||||
|
|
||||||
static CLEANER_INIT: Once = Once::new();
|
let (r_c_w_h, r_c_wo_h) = *CLEANER_INIT.get_or_init(|| {
|
||||||
|
let r_c_w_h_atom = atom!("run_cleaners_with_handling");
|
||||||
|
let r_c_wo_h_atom = atom!("run_cleaners_without_handling");
|
||||||
|
let iso_ext = atom!("iso_ext");
|
||||||
|
|
||||||
static mut RCWH: usize = 0;
|
let r_c_w_h = self
|
||||||
static mut RCWOH: usize = 0;
|
.indices
|
||||||
|
.get_predicate_code_index(r_c_w_h_atom, 0, iso_ext)
|
||||||
let (r_c_w_h, r_c_wo_h) = unsafe {
|
.and_then(|item| item.local())
|
||||||
CLEANER_INIT.call_once(|| {
|
.unwrap();
|
||||||
let r_c_w_h_atom = atom!("run_cleaners_with_handling");
|
let r_c_wo_h = self
|
||||||
let r_c_wo_h_atom = atom!("run_cleaners_without_handling");
|
.indices
|
||||||
let iso_ext = atom!("iso_ext");
|
.get_predicate_code_index(r_c_wo_h_atom, 1, iso_ext)
|
||||||
|
.and_then(|item| item.local())
|
||||||
RCWH = self
|
.unwrap();
|
||||||
.indices
|
(r_c_w_h, r_c_wo_h)
|
||||||
.get_predicate_code_index(r_c_w_h_atom, 0, iso_ext)
|
});
|
||||||
.and_then(|item| item.local())
|
|
||||||
.unwrap();
|
|
||||||
RCWOH = self
|
|
||||||
.indices
|
|
||||||
.get_predicate_code_index(r_c_wo_h_atom, 1, iso_ext)
|
|
||||||
.and_then(|item| item.local())
|
|
||||||
.unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
(RCWH, RCWOH)
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(&(_, b_cutoff, prev_block)) = self.machine_st.cont_pts.last() {
|
if let Some(&(_, b_cutoff, prev_block)) = self.machine_st.cont_pts.last() {
|
||||||
if self.machine_st.b < b_cutoff {
|
if self.machine_st.b < b_cutoff {
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ use dashu::*;
|
|||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
pub type QueryResult = Result<QueryResolution, String>;
|
pub type QueryResult = Result<QueryResolution, String>;
|
||||||
|
|
||||||
@@ -13,17 +15,21 @@ pub enum QueryResolution {
|
|||||||
Matches(Vec<QueryMatch>),
|
Matches(Vec<QueryMatch>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prolog_value_to_json_string(value: Value) -> String {
|
pub fn write_prolog_value_as_json<W: Write>(
|
||||||
|
writer: &mut W,
|
||||||
|
value: &Value,
|
||||||
|
) -> Result<(), std::fmt::Error> {
|
||||||
match value {
|
match value {
|
||||||
Value::Integer(i) => format!("{}", i),
|
Value::Integer(i) => write!(writer, "{}", i),
|
||||||
Value::Float(f) => format!("{}", f),
|
Value::Float(f) => write!(writer, "{}", f),
|
||||||
Value::Rational(r) => format!("{}", r),
|
Value::Rational(r) => write!(writer, "{}", r),
|
||||||
Value::Atom(a) => format!("{}", a.as_str()),
|
Value::Atom(a) => writer.write_str(&a.as_str()),
|
||||||
Value::String(s) => {
|
Value::String(s) => {
|
||||||
if let Err(_e) = serde_json::from_str::<serde_json::Value>(s.as_str()) {
|
if let Err(_e) = serde_json::from_str::<serde_json::Value>(s.as_str()) {
|
||||||
//treat as string literal
|
//treat as string literal
|
||||||
//escape double quotes
|
//escape double quotes
|
||||||
format!(
|
write!(
|
||||||
|
writer,
|
||||||
"\"{}\"",
|
"\"{}\"",
|
||||||
s.replace('\"', "\\\"")
|
s.replace('\"', "\\\"")
|
||||||
.replace('\n', "\\n")
|
.replace('\n', "\\n")
|
||||||
@@ -32,60 +38,71 @@ pub fn prolog_value_to_json_string(value: Value) -> String {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
//return valid json string
|
//return valid json string
|
||||||
s
|
writer.write_str(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::List(l) => {
|
Value::List(l) => {
|
||||||
let mut string_result = "[".to_string();
|
writer.write_char('[')?;
|
||||||
for (i, v) in l.iter().enumerate() {
|
if let Some((first, rest)) = l.split_first() {
|
||||||
if i > 0 {
|
write_prolog_value_as_json(writer, first)?;
|
||||||
string_result.push(',');
|
|
||||||
|
for other in rest {
|
||||||
|
writer.write_char(',')?;
|
||||||
|
write_prolog_value_as_json(writer, other)?;
|
||||||
}
|
}
|
||||||
string_result.push_str(&prolog_value_to_json_string(v.clone()));
|
|
||||||
}
|
}
|
||||||
string_result.push(']');
|
writer.write_char(']')
|
||||||
string_result
|
|
||||||
}
|
}
|
||||||
Value::Structure(s, l) => {
|
Value::Structure(s, l) => {
|
||||||
let mut string_result = format!("\"{}\":[", s.as_str());
|
write!(writer, "\"{}\":[", s.as_str())?;
|
||||||
for (i, v) in l.iter().enumerate() {
|
|
||||||
if i > 0 {
|
if let Some((first, rest)) = l.split_first() {
|
||||||
string_result.push(',');
|
write_prolog_value_as_json(writer, first)?;
|
||||||
|
for other in rest {
|
||||||
|
writer.write_char(',')?;
|
||||||
|
write_prolog_value_as_json(writer, other)?;
|
||||||
}
|
}
|
||||||
string_result.push_str(&prolog_value_to_json_string(v.clone()));
|
|
||||||
}
|
}
|
||||||
string_result.push(']');
|
writer.write_char(']')
|
||||||
string_result
|
|
||||||
}
|
}
|
||||||
_ => "null".to_string(),
|
_ => writer.write_str("null"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prolog_match_to_json_string(query_match: &QueryMatch) -> String {
|
fn write_prolog_match_as_json<W: std::fmt::Write>(
|
||||||
let mut string_result = "{".to_string();
|
writer: &mut W,
|
||||||
for (i, (k, v)) in query_match.bindings.iter().enumerate() {
|
query_match: &QueryMatch,
|
||||||
if i > 0 {
|
) -> Result<(), std::fmt::Error> {
|
||||||
string_result.push(',');
|
writer.write_char('{')?;
|
||||||
|
let mut iter = query_match.bindings.iter();
|
||||||
|
|
||||||
|
if let Some((k, v)) = iter.next() {
|
||||||
|
write!(writer, "\"{k}\":")?;
|
||||||
|
write_prolog_value_as_json(writer, v)?;
|
||||||
|
|
||||||
|
for (k, v) in iter {
|
||||||
|
write!(writer, ",\"{k}\":")?;
|
||||||
|
write_prolog_value_as_json(writer, v)?;
|
||||||
}
|
}
|
||||||
string_result.push_str(&format!(
|
|
||||||
"\"{}\":{}",
|
|
||||||
k,
|
|
||||||
prolog_value_to_json_string(v.clone())
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
string_result.push('}');
|
writer.write_char('}')
|
||||||
string_result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for QueryResolution {
|
impl Display for QueryResolution {
|
||||||
fn to_string(&self) -> String {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
QueryResolution::True => "true".to_string(),
|
QueryResolution::True => f.write_str("true"),
|
||||||
QueryResolution::False => "false".to_string(),
|
QueryResolution::False => f.write_str("false"),
|
||||||
QueryResolution::Matches(matches) => {
|
QueryResolution::Matches(matches) => {
|
||||||
let matches_json: Vec<String> =
|
f.write_char('[')?;
|
||||||
matches.iter().map(prolog_match_to_json_string).collect();
|
if let Some((first, rest)) = matches.split_first() {
|
||||||
format!("[{}]", matches_json.join(","))
|
write_prolog_match_as_json(f, first)?;
|
||||||
|
for other in rest {
|
||||||
|
f.write_char(',')?;
|
||||||
|
write_prolog_match_as_json(f, other)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.write_char(']')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -801,7 +801,7 @@ mod test {
|
|||||||
use crate::machine::mock_wam::*;
|
use crate::machine::mock_wam::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn pstr_iter_tests() {
|
fn pstr_iter_tests() {
|
||||||
let mut wam = MockWAM::new();
|
let mut wam = MockWAM::new();
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ impl RawBlockTraits for Stack {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn align() -> usize {
|
fn align() -> usize {
|
||||||
mem::align_of::<HeapCellValue>()
|
mem::align_of::<OrFrame>()
|
||||||
|
.max(mem::align_of::<AndFrame>())
|
||||||
|
.max(mem::align_of::<HeapCellValue>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +283,6 @@ mod tests {
|
|||||||
use crate::machine::mock_wam::*;
|
use crate::machine::mock_wam::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore)]
|
|
||||||
fn stack_tests() {
|
fn stack_tests() {
|
||||||
let mut wam = MockWAM::new();
|
let mut wam = MockWAM::new();
|
||||||
|
|
||||||
|
|||||||
@@ -324,6 +324,9 @@ impl Write for HttpWriteStream {
|
|||||||
|
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
impl HttpWriteStream {
|
impl HttpWriteStream {
|
||||||
|
// TODO why is this suddenly dead code and should it be used somewhere?
|
||||||
|
// Should this be impl Drop for HttpWriteStream?
|
||||||
|
#[allow(dead_code)]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let headers = unsafe { std::mem::ManuallyDrop::take(&mut self.headers) };
|
let headers = unsafe { std::mem::ManuallyDrop::take(&mut self.headers) };
|
||||||
let buffer = unsafe { std::mem::ManuallyDrop::take(&mut self.buffer) };
|
let buffer = unsafe { std::mem::ManuallyDrop::take(&mut self.buffer) };
|
||||||
@@ -452,15 +455,34 @@ impl<T> DerefMut for StreamLayout<T> {
|
|||||||
|
|
||||||
macro_rules! arena_allocated_impl_for_stream {
|
macro_rules! arena_allocated_impl_for_stream {
|
||||||
($stream_type:ty, $stream_tag:ident) => {
|
($stream_type:ty, $stream_tag:ident) => {
|
||||||
impl ArenaAllocated for StreamLayout<$stream_type> {
|
impl $crate::arena::AllocateInArena<$stream_tag> for StreamLayout<$stream_type> {
|
||||||
type PtrToAllocated = TypedArenaPtr<StreamLayout<$stream_type>>;
|
fn arena_allocate(self, arena: &mut Arena) -> TypedArenaPtr<$stream_tag> {
|
||||||
|
$stream_tag::alloc(arena, core::mem::ManuallyDrop::new(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gen_ptr_to_allocated!(StreamLayout<$stream_type>);
|
impl ArenaAllocated for $stream_tag {
|
||||||
|
type Payload = core::mem::ManuallyDrop<StreamLayout<$stream_type>>;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn tag() -> ArenaHeaderTag {
|
fn tag() -> ArenaHeaderTag {
|
||||||
ArenaHeaderTag::$stream_tag
|
ArenaHeaderTag::$stream_tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn dealloc(ptr: std::ptr::NonNull<TypedAllocSlab<Self>>) {
|
||||||
|
let mut slab = unsafe { Box::from_raw(ptr.as_ptr()) };
|
||||||
|
|
||||||
|
match slab.tag() {
|
||||||
|
ArenaHeaderTag::$stream_tag => {
|
||||||
|
unsafe { std::mem::ManuallyDrop::drop(slab.payload()) };
|
||||||
|
}
|
||||||
|
ArenaHeaderTag::Dropped => {}
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop(slab);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -482,26 +504,26 @@ arena_allocated_impl_for_stream!(StandardErrorStream, StandardErrorStream);
|
|||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum Stream {
|
pub enum Stream {
|
||||||
Byte(TypedArenaPtr<StreamLayout<CharReader<ByteStream>>>),
|
Byte(TypedArenaPtr<ByteStream>),
|
||||||
InputFile(TypedArenaPtr<StreamLayout<CharReader<InputFileStream>>>),
|
InputFile(TypedArenaPtr<InputFileStream>),
|
||||||
OutputFile(TypedArenaPtr<StreamLayout<OutputFileStream>>),
|
OutputFile(TypedArenaPtr<OutputFileStream>),
|
||||||
StaticString(TypedArenaPtr<StreamLayout<StaticStringStream>>),
|
StaticString(TypedArenaPtr<StaticStringStream>),
|
||||||
NamedTcp(TypedArenaPtr<StreamLayout<CharReader<NamedTcpStream>>>),
|
NamedTcp(TypedArenaPtr<NamedTcpStream>),
|
||||||
#[cfg(feature = "tls")]
|
#[cfg(feature = "tls")]
|
||||||
NamedTls(TypedArenaPtr<StreamLayout<CharReader<NamedTlsStream>>>),
|
NamedTls(TypedArenaPtr<NamedTlsStream>),
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
HttpRead(TypedArenaPtr<StreamLayout<CharReader<HttpReadStream>>>),
|
HttpRead(TypedArenaPtr<HttpReadStream>),
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
HttpWrite(TypedArenaPtr<StreamLayout<CharReader<HttpWriteStream>>>),
|
HttpWrite(TypedArenaPtr<HttpWriteStream>),
|
||||||
Null(StreamOptions),
|
Null(StreamOptions),
|
||||||
Readline(TypedArenaPtr<StreamLayout<ReadlineStream>>),
|
Readline(TypedArenaPtr<ReadlineStream>),
|
||||||
StandardOutput(TypedArenaPtr<StreamLayout<StandardOutputStream>>),
|
StandardOutput(TypedArenaPtr<StandardOutputStream>),
|
||||||
StandardError(TypedArenaPtr<StreamLayout<StandardErrorStream>>),
|
StandardError(TypedArenaPtr<StandardErrorStream>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TypedArenaPtr<StreamLayout<ReadlineStream>>> for Stream {
|
impl From<TypedArenaPtr<ReadlineStream>> for Stream {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(stream: TypedArenaPtr<StreamLayout<ReadlineStream>>) -> Stream {
|
fn from(stream: TypedArenaPtr<ReadlineStream>) -> Stream {
|
||||||
Stream::Readline(stream)
|
Stream::Readline(stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -540,29 +562,27 @@ impl Stream {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_tag(tag: ArenaHeaderTag, ptr: *const u8) -> Self {
|
pub fn from_tag(tag: ArenaHeaderTag, ptr: UntypedArenaPtr) -> Self {
|
||||||
match tag {
|
match tag {
|
||||||
ArenaHeaderTag::ByteStream => Stream::Byte(TypedArenaPtr::new(ptr as *mut _)),
|
ArenaHeaderTag::ByteStream => Stream::Byte(unsafe { ptr.as_typed_ptr() }),
|
||||||
ArenaHeaderTag::InputFileStream => Stream::InputFile(TypedArenaPtr::new(ptr as *mut _)),
|
ArenaHeaderTag::InputFileStream => Stream::InputFile(unsafe { ptr.as_typed_ptr() }),
|
||||||
ArenaHeaderTag::OutputFileStream => {
|
ArenaHeaderTag::OutputFileStream => Stream::OutputFile(unsafe { ptr.as_typed_ptr() }),
|
||||||
Stream::OutputFile(TypedArenaPtr::new(ptr as *mut _))
|
ArenaHeaderTag::NamedTcpStream => Stream::NamedTcp(unsafe { ptr.as_typed_ptr() }),
|
||||||
}
|
|
||||||
ArenaHeaderTag::NamedTcpStream => Stream::NamedTcp(TypedArenaPtr::new(ptr as *mut _)),
|
|
||||||
#[cfg(feature = "tls")]
|
#[cfg(feature = "tls")]
|
||||||
ArenaHeaderTag::NamedTlsStream => Stream::NamedTls(TypedArenaPtr::new(ptr as *mut _)),
|
ArenaHeaderTag::NamedTlsStream => Stream::NamedTls(unsafe { ptr.as_typed_ptr() }),
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
ArenaHeaderTag::HttpReadStream => Stream::HttpRead(TypedArenaPtr::new(ptr as *mut _)),
|
ArenaHeaderTag::HttpReadStream => Stream::HttpRead(unsafe { ptr.as_typed_ptr() }),
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
ArenaHeaderTag::HttpWriteStream => Stream::HttpWrite(TypedArenaPtr::new(ptr as *mut _)),
|
ArenaHeaderTag::HttpWriteStream => Stream::HttpWrite(unsafe { ptr.as_typed_ptr() }),
|
||||||
ArenaHeaderTag::ReadlineStream => Stream::Readline(TypedArenaPtr::new(ptr as *mut _)),
|
ArenaHeaderTag::ReadlineStream => Stream::Readline(unsafe { ptr.as_typed_ptr() }),
|
||||||
ArenaHeaderTag::StaticStringStream => {
|
ArenaHeaderTag::StaticStringStream => {
|
||||||
Stream::StaticString(TypedArenaPtr::new(ptr as *mut _))
|
Stream::StaticString(unsafe { ptr.as_typed_ptr() })
|
||||||
}
|
}
|
||||||
ArenaHeaderTag::StandardOutputStream => {
|
ArenaHeaderTag::StandardOutputStream => {
|
||||||
Stream::StandardOutput(TypedArenaPtr::new(ptr as *mut _))
|
Stream::StandardOutput(unsafe { ptr.as_typed_ptr() })
|
||||||
}
|
}
|
||||||
ArenaHeaderTag::StandardErrorStream => {
|
ArenaHeaderTag::StandardErrorStream => {
|
||||||
Stream::StandardError(TypedArenaPtr::new(ptr as *mut _))
|
Stream::StandardError(unsafe { ptr.as_typed_ptr() })
|
||||||
}
|
}
|
||||||
ArenaHeaderTag::Dropped | ArenaHeaderTag::NullStream => {
|
ArenaHeaderTag::Dropped | ArenaHeaderTag::NullStream => {
|
||||||
Stream::Null(StreamOptions::default())
|
Stream::Null(StreamOptions::default())
|
||||||
@@ -996,7 +1016,7 @@ impl Stream {
|
|||||||
past_end_of_stream,
|
past_end_of_stream,
|
||||||
stream,
|
stream,
|
||||||
..
|
..
|
||||||
} = &mut **stream_layout;
|
} = &mut ***stream_layout;
|
||||||
|
|
||||||
stream
|
stream
|
||||||
.get_mut()
|
.get_mut()
|
||||||
@@ -1070,7 +1090,7 @@ impl Stream {
|
|||||||
past_end_of_stream,
|
past_end_of_stream,
|
||||||
stream,
|
stream,
|
||||||
..
|
..
|
||||||
} = &mut **stream_layout;
|
} = &mut ***stream_layout;
|
||||||
|
|
||||||
let cursor_len = stream.get_ref().0.get_ref().len() as u64;
|
let cursor_len = stream.get_ref().0.get_ref().len() as u64;
|
||||||
cursor_position(past_end_of_stream, &stream.get_ref().0, cursor_len)
|
cursor_position(past_end_of_stream, &stream.get_ref().0, cursor_len)
|
||||||
@@ -1080,7 +1100,7 @@ impl Stream {
|
|||||||
past_end_of_stream,
|
past_end_of_stream,
|
||||||
stream,
|
stream,
|
||||||
..
|
..
|
||||||
} = &mut **stream_layout;
|
} = &mut ***stream_layout;
|
||||||
|
|
||||||
let cursor_len = stream.stream.get_ref().len() as u64;
|
let cursor_len = stream.stream.get_ref().len() as u64;
|
||||||
cursor_position(past_end_of_stream, &stream.stream, cursor_len)
|
cursor_position(past_end_of_stream, &stream.stream, cursor_len)
|
||||||
@@ -1092,7 +1112,7 @@ impl Stream {
|
|||||||
past_end_of_stream,
|
past_end_of_stream,
|
||||||
stream,
|
stream,
|
||||||
..
|
..
|
||||||
} = &mut **stream_layout;
|
} = &mut ***stream_layout;
|
||||||
|
|
||||||
match stream.get_ref().file.metadata() {
|
match stream.get_ref().file.metadata() {
|
||||||
Ok(metadata) => {
|
Ok(metadata) => {
|
||||||
@@ -1279,38 +1299,25 @@ impl Stream {
|
|||||||
Stream::NamedTls(ref mut tls_stream) => tls_stream.inner_mut().tls_stream.shutdown(),
|
Stream::NamedTls(ref mut tls_stream) => tls_stream.inner_mut().tls_stream.shutdown(),
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
Stream::HttpRead(ref mut http_stream) => {
|
Stream::HttpRead(ref mut http_stream) => {
|
||||||
unsafe {
|
http_stream.drop_payload();
|
||||||
http_stream.set_tag(ArenaHeaderTag::Dropped);
|
|
||||||
std::ptr::drop_in_place(&mut http_stream.inner_mut().body_reader as *mut _);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
Stream::HttpWrite(ref mut http_stream) => {
|
Stream::HttpWrite(mut http_stream) => {
|
||||||
http_stream.inner_mut().drop();
|
http_stream.drop_payload();
|
||||||
unsafe {
|
|
||||||
http_stream.set_tag(ArenaHeaderTag::Dropped);
|
|
||||||
std::ptr::drop_in_place(&mut http_stream.inner_mut().buffer as *mut _);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Stream::InputFile(mut file_stream) => {
|
Stream::InputFile(mut file_stream) => {
|
||||||
// close the stream by dropping the inner File.
|
// close the stream by dropping the inner File.
|
||||||
unsafe {
|
file_stream.drop_payload();
|
||||||
file_stream.set_tag(ArenaHeaderTag::Dropped);
|
|
||||||
std::ptr::drop_in_place(&mut file_stream.inner_mut().file as *mut _);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Stream::OutputFile(mut file_stream) => {
|
Stream::OutputFile(mut file_stream) => {
|
||||||
// close the stream by dropping the inner File.
|
// close the stream by dropping the inner File.
|
||||||
unsafe {
|
file_stream.drop_payload();
|
||||||
file_stream.set_tag(ArenaHeaderTag::Dropped);
|
|
||||||
std::ptr::drop_in_place(&mut file_stream.file as *mut _);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,8 +39,6 @@ use ordered_float::OrderedFloat;
|
|||||||
use fxhash::{FxBuildHasher, FxHasher};
|
use fxhash::{FxBuildHasher, FxHasher};
|
||||||
use indexmap::IndexSet;
|
use indexmap::IndexSet;
|
||||||
|
|
||||||
pub(crate) use ref_thread_local::RefThreadLocal;
|
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
@@ -103,6 +101,8 @@ use warp::hyper::{HeaderMap, Method};
|
|||||||
#[cfg(feature = "http")]
|
#[cfg(feature = "http")]
|
||||||
use warp::{Buf, Filter};
|
use warp::{Buf, Filter};
|
||||||
|
|
||||||
|
use super::libraries;
|
||||||
|
|
||||||
#[cfg(feature = "repl")]
|
#[cfg(feature = "repl")]
|
||||||
pub(crate) fn get_key() -> KeyEvent {
|
pub(crate) fn get_key() -> KeyEvent {
|
||||||
let key;
|
let key;
|
||||||
@@ -4513,7 +4513,8 @@ impl Machine {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let http_listener = HttpListener { incoming: rx };
|
let http_listener = HttpListener { incoming: rx };
|
||||||
let http_listener = arena_alloc!(http_listener, &mut self.machine_st.arena);
|
let http_listener: TypedArenaPtr<HttpListener> =
|
||||||
|
arena_alloc!(http_listener, &mut self.machine_st.arena);
|
||||||
|
|
||||||
let addr = self.deref_register(2);
|
let addr = self.deref_register(2);
|
||||||
self.machine_st.bind(
|
self.machine_st.bind(
|
||||||
@@ -4578,7 +4579,7 @@ impl Machine {
|
|||||||
self.indices.streams.insert(stream);
|
self.indices.streams.insert(stream);
|
||||||
let stream = stream_as_cell!(stream);
|
let stream = stream_as_cell!(stream);
|
||||||
|
|
||||||
let handle = arena_alloc!(request.response, &mut self.machine_st.arena);
|
let handle: TypedArenaPtr<HttpResponse> = arena_alloc!(request.response, &mut self.machine_st.arena);
|
||||||
|
|
||||||
self.machine_st.bind(method.as_var().unwrap(), atom_as_cell!(method_atom));
|
self.machine_st.bind(method.as_var().unwrap(), atom_as_cell!(method_atom));
|
||||||
self.machine_st.bind(path.as_var().unwrap(), path_cell);
|
self.machine_st.bind(path.as_var().unwrap(), path_cell);
|
||||||
@@ -6510,32 +6511,33 @@ impl Machine {
|
|||||||
format!("{}:{}", socket_atom.as_str(), port)
|
format!("{}:{}", socket_atom.as_str(), port)
|
||||||
};
|
};
|
||||||
|
|
||||||
let (tcp_listener, port) = match TcpListener::bind(server_addr).map_err(|e| e.kind()) {
|
let (tcp_listener, port): (TypedArenaPtr<TcpListener>, _) =
|
||||||
Ok(tcp_listener) => {
|
match TcpListener::bind(server_addr).map_err(|e| e.kind()) {
|
||||||
let port = tcp_listener.local_addr().map(|addr| addr.port()).ok();
|
Ok(tcp_listener) => {
|
||||||
|
let port = tcp_listener.local_addr().map(|addr| addr.port()).ok();
|
||||||
|
|
||||||
if let Some(port) = port {
|
if let Some(port) = port {
|
||||||
(
|
(
|
||||||
arena_alloc!(tcp_listener, &mut self.machine_st.arena),
|
arena_alloc!(tcp_listener, &mut self.machine_st.arena),
|
||||||
port as usize,
|
port as usize,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
self.machine_st.fail = true;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(ErrorKind::PermissionDenied) => {
|
||||||
|
return Err(self.machine_st.open_permission_error(
|
||||||
|
addr,
|
||||||
|
atom!("socket_server_open"),
|
||||||
|
2,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
self.machine_st.fail = true;
|
self.machine_st.fail = true;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
Err(ErrorKind::PermissionDenied) => {
|
|
||||||
return Err(self.machine_st.open_permission_error(
|
|
||||||
addr,
|
|
||||||
atom!("socket_server_open"),
|
|
||||||
2,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.machine_st.fail = true;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let addr = self.deref_register(3);
|
let addr = self.deref_register(3);
|
||||||
self.machine_st.bind(
|
self.machine_st.bind(
|
||||||
@@ -6729,12 +6731,8 @@ impl Machine {
|
|||||||
(HeapCellValueTag::Cons, cons_ptr) => {
|
(HeapCellValueTag::Cons, cons_ptr) => {
|
||||||
match_untyped_arena_ptr!(cons_ptr,
|
match_untyped_arena_ptr!(cons_ptr,
|
||||||
(ArenaHeaderTag::TcpListener, tcp_listener) => {
|
(ArenaHeaderTag::TcpListener, tcp_listener) => {
|
||||||
unsafe {
|
tcp_listener.drop_payload();
|
||||||
// dropping closes the instance.
|
|
||||||
std::ptr::drop_in_place(&mut tcp_listener as *mut _);
|
|
||||||
}
|
|
||||||
|
|
||||||
tcp_listener.set_tag(ArenaHeaderTag::Dropped);
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@@ -7990,10 +7988,7 @@ impl Machine {
|
|||||||
pub(crate) fn load_library_as_stream(&mut self) -> CallResult {
|
pub(crate) fn load_library_as_stream(&mut self) -> CallResult {
|
||||||
let library_name = cell_as_atom!(self.deref_register(1));
|
let library_name = cell_as_atom!(self.deref_register(1));
|
||||||
|
|
||||||
use crate::machine::LIBRARIES;
|
let lib = libraries::get(&library_name.as_str());
|
||||||
|
|
||||||
let lib_ref = LIBRARIES.borrow();
|
|
||||||
let lib = lib_ref.get(&*library_name.as_str());
|
|
||||||
match lib {
|
match lib {
|
||||||
Some(library) => {
|
Some(library) => {
|
||||||
let lib_stream = Stream::from_static_string(library, &mut self.machine_st.arena);
|
let lib_stream = Stream::from_static_string(library, &mut self.machine_st.arena);
|
||||||
|
|||||||
@@ -171,15 +171,18 @@ macro_rules! typed_arena_ptr_as_cell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! raw_ptr_as_cell {
|
macro_rules! raw_ptr_as_cell {
|
||||||
($ptr:expr) => {
|
($ptr:expr) => {{
|
||||||
// Cell is 64-bit, but raw ptr is 32-bit in 32-bit systems
|
// Cell is 64-bit, but raw ptr is 32-bit in 32-bit systems
|
||||||
HeapCellValue::from_raw_ptr_bytes(unsafe { std::mem::transmute($ptr) })
|
// TODO use <*{const,mut} _>::addr instead of as when the strict_provenance feature is stable rust-lang/rust#95228
|
||||||
};
|
// we might need <*{const,mut} _>::expose_provenance for strict provenance, dependening on how we recreate a pointer later
|
||||||
|
let ptr : *const _ = $ptr;
|
||||||
|
HeapCellValue::from_ptr_addr(ptr as usize)
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! untyped_arena_ptr_as_cell {
|
macro_rules! untyped_arena_ptr_as_cell {
|
||||||
($ptr:expr) => {
|
($ptr:expr) => {
|
||||||
HeapCellValue::from_bytes(unsafe { std::mem::transmute($ptr) })
|
HeapCellValue::from_bytes(UntypedArenaPtr::into_bytes($ptr))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,86 +227,69 @@ macro_rules! stream_as_cell {
|
|||||||
macro_rules! cell_as_stream {
|
macro_rules! cell_as_stream {
|
||||||
($cell:expr) => {{
|
($cell:expr) => {{
|
||||||
let ptr = cell_as_untyped_arena_ptr!($cell);
|
let ptr = cell_as_untyped_arena_ptr!($cell);
|
||||||
Stream::from_tag(ptr.get_tag(), ptr.payload_offset())
|
Stream::from_tag(ptr.get_tag(), ptr)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! cell_as_load_state_payload {
|
macro_rules! cell_as_load_state_payload {
|
||||||
($cell:expr) => {
|
($cell:expr) => {{
|
||||||
unsafe {
|
let ptr = cell_as_untyped_arena_ptr!($cell);
|
||||||
let ptr = cell_as_untyped_arena_ptr!($cell);
|
unsafe { ptr.as_typed_ptr::<LiveLoadState>() }
|
||||||
let ptr = std::mem::transmute::<_, *mut LiveLoadState>(ptr.payload_offset());
|
}};
|
||||||
|
|
||||||
TypedArenaPtr::new(ptr)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! match_untyped_arena_ptr_pat_body {
|
macro_rules! match_untyped_arena_ptr_pat_body {
|
||||||
($ptr:ident, Integer, $n:ident, $code:expr) => {{
|
($ptr:ident, Integer, $n:ident, $code:expr) => {{
|
||||||
let payload_ptr = unsafe { std::mem::transmute::<_, *mut Integer>($ptr.payload_offset()) };
|
let $n = unsafe { $ptr.as_typed_ptr::<Integer>() };
|
||||||
let $n = TypedArenaPtr::new(payload_ptr);
|
|
||||||
#[allow(unused_braces)]
|
#[allow(unused_braces)]
|
||||||
$code
|
$code
|
||||||
}};
|
}};
|
||||||
($ptr:ident, Rational, $n:ident, $code:expr) => {{
|
($ptr:ident, Rational, $n:ident, $code:expr) => {{
|
||||||
let payload_ptr = unsafe { std::mem::transmute::<_, *mut Rational>($ptr.payload_offset()) };
|
let $n = unsafe { $ptr.as_typed_ptr::<Rational>() };
|
||||||
let $n = TypedArenaPtr::new(payload_ptr);
|
|
||||||
#[allow(unused_braces)]
|
#[allow(unused_braces)]
|
||||||
$code
|
$code
|
||||||
}};
|
}};
|
||||||
($ptr:ident, OssifiedOpDir, $n:ident, $code:expr) => {{
|
($ptr:ident, OssifiedOpDir, $n:ident, $code:expr) => {{
|
||||||
let payload_ptr =
|
let $n = unsafe { $ptr.as_typed_ptr::<OssifiedOpDir>() };
|
||||||
unsafe { std::mem::transmute::<_, *mut OssifiedOpDir>($ptr.payload_offset()) };
|
|
||||||
let $n = TypedArenaPtr::new(payload_ptr);
|
|
||||||
#[allow(unused_braces)]
|
#[allow(unused_braces)]
|
||||||
$code
|
$code
|
||||||
}};
|
}};
|
||||||
($ptr:ident, LiveLoadState, $n:ident, $code:expr) => {{
|
($ptr:ident, LiveLoadState, $n:ident, $code:expr) => {{
|
||||||
let payload_ptr =
|
let $n = unsafe { $ptr.as_typed_ptr::<LiveLoadState>() };
|
||||||
unsafe { std::mem::transmute::<_, *mut LiveLoadState>($ptr.payload_offset()) };
|
|
||||||
let $n = TypedArenaPtr::new(payload_ptr);
|
|
||||||
#[allow(unused_braces)]
|
#[allow(unused_braces)]
|
||||||
$code
|
$code
|
||||||
}};
|
}};
|
||||||
($ptr:ident, Stream, $s:ident, $code:expr) => {{
|
($ptr:ident, Stream, $s:ident, $code:expr) => {{
|
||||||
let $s = Stream::from_tag($ptr.get_tag(), $ptr.payload_offset());
|
let $s = Stream::from_tag($ptr.get_tag(), $ptr);
|
||||||
#[allow(unused_braces)]
|
#[allow(unused_braces)]
|
||||||
$code
|
$code
|
||||||
}};
|
}};
|
||||||
($ptr:ident, TcpListener, $listener:ident, $code:expr) => {{
|
($ptr:ident, TcpListener, $listener:ident, $code:expr) => {{
|
||||||
let payload_ptr =
|
|
||||||
unsafe { std::mem::transmute::<_, *mut TcpListener>($ptr.payload_offset()) };
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut $listener = TypedArenaPtr::new(payload_ptr);
|
let mut $listener = unsafe { $ptr.as_typed_ptr::<TcpListener>() };
|
||||||
#[allow(unused_braces)]
|
#[allow(unused_braces)]
|
||||||
$code
|
$code
|
||||||
}};
|
}};
|
||||||
($ptr:ident, HttpListener, $listener:ident, $code:expr) => {{
|
($ptr:ident, HttpListener, $listener:ident, $code:expr) => {{
|
||||||
let payload_ptr =
|
|
||||||
unsafe { std::mem::transmute::<_, *mut HttpListener>($ptr.payload_offset()) };
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut $listener = TypedArenaPtr::new(payload_ptr);
|
let mut $listener = unsafe { $ptr.as_typed_ptr::<HttpListener>() };
|
||||||
#[allow(unused_braces)]
|
#[allow(unused_braces)]
|
||||||
$code
|
$code
|
||||||
}};
|
}};
|
||||||
($ptr:ident, HttpResponse, $listener:ident, $code:expr) => {{
|
($ptr:ident, HttpResponse, $listener:ident, $code:expr) => {{
|
||||||
let payload_ptr =
|
|
||||||
unsafe { std::mem::transmute::<_, *mut HttpResponse>($ptr.payload_offset()) };
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut $listener = TypedArenaPtr::new(payload_ptr);
|
let mut $listener = unsafe { $ptr.as_typed_ptr::<HttpResponse>() };
|
||||||
#[allow(unused_braces)]
|
#[allow(unused_braces)]
|
||||||
$code
|
$code
|
||||||
}};
|
}};
|
||||||
($ptr:ident, IndexPtr, $ip:ident, $code:expr) => {{
|
($ptr:ident, IndexPtr, $ip:ident, $code:expr) => {{
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut $ip =
|
let mut $ip = unsafe { $ptr.as_typed_ptr::<IndexPtr>() };
|
||||||
TypedArenaPtr::new(unsafe { std::mem::transmute::<_, *mut IndexPtr>($ptr.get_ptr()) });
|
|
||||||
#[allow(unused_braces)]
|
#[allow(unused_braces)]
|
||||||
$code
|
$code
|
||||||
}};
|
}};
|
||||||
($ptr:ident, $($tags:tt)|+, $s:ident, $code:expr) => {{
|
($ptr:ident, $($tags:tt)|+, $s:ident, $code:expr) => {{
|
||||||
let $s = Stream::from_tag($ptr.get_tag(), $ptr.payload_offset());
|
let $s = Stream::from_tag($ptr.get_tag(), $ptr);
|
||||||
#[allow(unused_braces)]
|
#[allow(unused_braces)]
|
||||||
$code
|
$code
|
||||||
}};
|
}};
|
||||||
@@ -338,6 +324,7 @@ macro_rules! match_untyped_arena_ptr {
|
|||||||
($ptr:expr, $( ($(ArenaHeaderTag::$tag:tt)|+, $n:ident) => $code:block $(,)?)+ $(_ => $misc_code:expr $(,)?)?) => ({
|
($ptr:expr, $( ($(ArenaHeaderTag::$tag:tt)|+, $n:ident) => $code:block $(,)?)+ $(_ => $misc_code:expr $(,)?)?) => ({
|
||||||
let ptr_id = $ptr;
|
let ptr_id = $ptr;
|
||||||
|
|
||||||
|
#[allow(clippy::toplevel_ref_arg)]
|
||||||
match ptr_id.get_tag() {
|
match ptr_id.get_tag() {
|
||||||
$($(match_untyped_arena_ptr_pat!($tag) => {
|
$($(match_untyped_arena_ptr_pat!($tag) => {
|
||||||
match_untyped_arena_ptr_pat_body!(ptr_id, $tag, $n, $code)
|
match_untyped_arena_ptr_pat_body!(ptr_id, $tag, $n, $code)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||||
|
|
||||||
use crate::arena::*;
|
use crate::arena::*;
|
||||||
use crate::atom_table::*;
|
use crate::atom_table::*;
|
||||||
use crate::machine::machine_indices::*;
|
use crate::machine::machine_indices::*;
|
||||||
|
|||||||
@@ -379,7 +379,6 @@ mod tests {
|
|||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
|
||||||
fn plain_string() {
|
fn plain_string() {
|
||||||
let mut read_string = CharReader::new(Cursor::new("a string"));
|
let mut read_string = CharReader::new(Cursor::new("a string"));
|
||||||
|
|
||||||
@@ -392,7 +391,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
|
||||||
fn greek_string() {
|
fn greek_string() {
|
||||||
let mut read_string = CharReader::new(Cursor::new("λέξη"));
|
let mut read_string = CharReader::new(Cursor::new("λέξη"));
|
||||||
|
|
||||||
@@ -405,7 +403,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "slow and not very relevant")]
|
|
||||||
fn russian_string() {
|
fn russian_string() {
|
||||||
let mut read_string = CharReader::new(Cursor::new("слово"));
|
let mut read_string = CharReader::new(Cursor::new("слово"));
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use lexical::parse_lossy;
|
use lexical::parse_lossy;
|
||||||
|
|
||||||
use crate::arena::ArenaAllocated;
|
|
||||||
use crate::atom_table::*;
|
use crate::atom_table::*;
|
||||||
pub use crate::machine::machine_state::*;
|
pub use crate::machine::machine_state::*;
|
||||||
use crate::parser::ast::*;
|
use crate::parser::ast::*;
|
||||||
|
|||||||
@@ -96,9 +96,10 @@ impl<T: RawBlockTraits> RawBlock<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn alloc(&self, size: usize) -> *mut u8 {
|
pub unsafe fn alloc(&self, size: usize) -> *mut u8 {
|
||||||
if self.free_space() >= size {
|
let aligned_size = size.next_multiple_of(size);
|
||||||
|
if self.free_space() >= aligned_size {
|
||||||
let ptr = *self.ptr.get();
|
let ptr = *self.ptr.get();
|
||||||
*self.ptr.get() = ptr.add(size) as *mut _;
|
*self.ptr.get() = ptr.add(aligned_size) as *mut _;
|
||||||
ptr
|
ptr
|
||||||
} else {
|
} else {
|
||||||
ptr::null_mut()
|
ptr::null_mut()
|
||||||
|
|||||||
220
src/rcu.rs
220
src/rcu.rs
@@ -1,220 +0,0 @@
|
|||||||
use std::{
|
|
||||||
cell::OnceCell,
|
|
||||||
fmt::Debug,
|
|
||||||
mem::ManuallyDrop,
|
|
||||||
ops::Deref,
|
|
||||||
ptr::NonNull,
|
|
||||||
sync::{
|
|
||||||
atomic::{AtomicPtr, AtomicU8},
|
|
||||||
Arc, RwLock, Weak,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// the epoch counters of all threads that have ever accessed an Rcu
|
|
||||||
// threads that have finished will have a dangling Weak reference and can be cleand up
|
|
||||||
// having this be shared between all Rcu's is a tradeof,
|
|
||||||
// writes will be slower as more epoch counters need to be waited for
|
|
||||||
// reads should be faster as a thread only needs to register itself once on the first read
|
|
||||||
//
|
|
||||||
static EPOCH_COUNTERS: RwLock<Vec<Weak<AtomicU8>>> = RwLock::new(Vec::new());
|
|
||||||
|
|
||||||
thread_local! {
|
|
||||||
// odd value means the current thread is about to access the active_epoch of an Rcu
|
|
||||||
// a thread has a single epoch counter for all Rcu it accesses,
|
|
||||||
// as a thread can only access one Rcu at a time
|
|
||||||
static THREAD_EPOCH_COUNTER: OnceCell<Arc<AtomicU8>> = const { OnceCell::new() };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Rcu<T> {
|
|
||||||
active_value: AtomicPtr<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: std::fmt::Debug> std::fmt::Debug for Rcu<T> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let active_epoch = self.active_epoch();
|
|
||||||
f.debug_struct("Rcu")
|
|
||||||
.field("active_value", &active_epoch)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Rcu<T> {
|
|
||||||
pub fn new(initial_value: T) -> Self {
|
|
||||||
Rcu {
|
|
||||||
active_value: AtomicPtr::new(Arc::into_raw(Arc::new(initial_value)).cast_mut()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn active_epoch(&self) -> RcuRef<T, T> {
|
|
||||||
THREAD_EPOCH_COUNTER.with(|epoch_counter| {
|
|
||||||
let epoch_counter = epoch_counter.get_or_init(|| {
|
|
||||||
let epoch_counter = Arc::new(AtomicU8::new(0));
|
|
||||||
// register the current threads epoch counter on init
|
|
||||||
EPOCH_COUNTERS
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.push(Arc::downgrade(&epoch_counter));
|
|
||||||
epoch_counter
|
|
||||||
});
|
|
||||||
|
|
||||||
let old = epoch_counter.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
|
|
||||||
assert!(old % 2 == 0, "Old Epoch counter value should be even!");
|
|
||||||
});
|
|
||||||
|
|
||||||
let arc_ptr = self.active_value.load(std::sync::atomic::Ordering::Acquire);
|
|
||||||
|
|
||||||
let arc = unsafe {
|
|
||||||
// Safety:
|
|
||||||
// - the ptr was created in Rcu::new or Rcu::replace with Arc::into_raw
|
|
||||||
// - the Rcu is responsible for of the arc's strong refrences
|
|
||||||
// - the Rcu is alive as this function takes a reference to the Rcu
|
|
||||||
// - replace will wait with decrementing the old values strong count until our epoich counter is even again
|
|
||||||
Arc::increment_strong_count(arc_ptr);
|
|
||||||
// Safety:
|
|
||||||
// - the ptr was created in Rcu::new or Rcu::replace with Arc::into_raw
|
|
||||||
// - we have just ensured an additional strong count by incrementing the count
|
|
||||||
Arc::from_raw(arc_ptr)
|
|
||||||
};
|
|
||||||
|
|
||||||
THREAD_EPOCH_COUNTER.with(|epoch_counter| {
|
|
||||||
let old = epoch_counter
|
|
||||||
.get().expect("we initialized the OnceCell when we incremented the epoch counter the fist time")
|
|
||||||
.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
|
|
||||||
assert!(old % 2 != 0, "Old Epoch counter value should be odd!");
|
|
||||||
});
|
|
||||||
|
|
||||||
RcuRef {
|
|
||||||
data: arc.deref().into(),
|
|
||||||
arc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* replace the Rcu'S content with a new value
|
|
||||||
*
|
|
||||||
* This does not syncronize write and last to update the active_value pointer wins,
|
|
||||||
* all writes that do not win will be lost, though not leaked.
|
|
||||||
* This will block untill the old value can be reclaimed,
|
|
||||||
* i.e. all threads whitnest to be in the read critical sections
|
|
||||||
* have been witnest to have left the critical section at least once
|
|
||||||
*/
|
|
||||||
pub fn replace(&self, new_value: T) {
|
|
||||||
let arc_ptr = self.active_value.swap(
|
|
||||||
Arc::into_raw(Arc::new(new_value)).cast_mut(),
|
|
||||||
std::sync::atomic::Ordering::AcqRel,
|
|
||||||
);
|
|
||||||
|
|
||||||
// maually drop as we need to ensure not to drop the arc while
|
|
||||||
// we have not witnest all threads to be or have been outside the read critical section
|
|
||||||
// i.e. even epoch counter or different odd epoch counter
|
|
||||||
// Safety:
|
|
||||||
// - the ptr was created in Rcu::new or Rcu::replace with Arc::into_raw
|
|
||||||
// - the Rcu itself holds one strong count
|
|
||||||
let arc = unsafe { ManuallyDrop::new(Arc::from_raw(arc_ptr)) };
|
|
||||||
|
|
||||||
let epochs = EPOCH_COUNTERS.read().unwrap().clone();
|
|
||||||
let mut epochs = epochs
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(|elem| {
|
|
||||||
let arc = elem.upgrade()?;
|
|
||||||
let init_val = arc.load(std::sync::atomic::Ordering::Acquire);
|
|
||||||
if init_val % 2 == 0 {
|
|
||||||
// already even can be ignored
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
// odd initial value thread is in read critical section
|
|
||||||
// need to wait for the value to change before we can drop the arc
|
|
||||||
Some((init_val, elem))
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
while !epochs.is_empty() {
|
|
||||||
epochs.retain(|elem| {
|
|
||||||
let Some(arc) = elem.1.upgrade() else {
|
|
||||||
// as the thread is dead it can't have a ref to old arc
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
// the epoch counter has not changed so the thread is still in the same instance of the critical section
|
|
||||||
// any different value is ok as
|
|
||||||
// - even values indicate the thread is outside the critical section
|
|
||||||
// - a diffrent odd value indicates the thread has left the critical section and can subsequently only read the new active_value
|
|
||||||
arc.load(std::sync::atomic::Ordering::Acquire) == elem.0
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safety:
|
|
||||||
// - we have not dropped the arc another way
|
|
||||||
// - we witnessed all threads either with an even epoch count or with a new odd count
|
|
||||||
// as such they must have left the critical section at some point
|
|
||||||
ManuallyDrop::into_inner(arc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RcuRef<T, M>
|
|
||||||
where
|
|
||||||
T: ?Sized,
|
|
||||||
M: ?Sized,
|
|
||||||
{
|
|
||||||
arc: Arc<T>,
|
|
||||||
data: NonNull<M>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized, M: ?Sized + Debug> Debug for RcuRef<T, M> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("RcuRef")
|
|
||||||
.field("data", &self.deref())
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use assoiated functions rather than methods so that we don't overlap
|
|
||||||
// with functions of the Deref Target type
|
|
||||||
impl<T: ?Sized, M: ?Sized> RcuRef<T, M> {
|
|
||||||
pub fn map<N: ?Sized, F: for<'a> FnOnce(&'a M) -> &'a N>(referece: Self, f: F) -> RcuRef<T, N> {
|
|
||||||
RcuRef {
|
|
||||||
arc: referece.arc,
|
|
||||||
data: f(unsafe { referece.data.as_ref() }).into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_map<N: ?Sized, F: for<'a> FnOnce(&'a M) -> Option<&'a N>>(
|
|
||||||
referece: Self,
|
|
||||||
f: F,
|
|
||||||
) -> Option<RcuRef<T, N>> {
|
|
||||||
let val = f(unsafe { referece.data.as_ref() })?;
|
|
||||||
Some(RcuRef {
|
|
||||||
arc: Arc::clone(&referece.arc),
|
|
||||||
data: val.into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn same_epoch<M2>(this: &Self, other: &RcuRef<T, M2>) -> bool {
|
|
||||||
Arc::ptr_eq(&this.arc, &other.arc)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
|
|
||||||
this.data == other.data
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clone(this: &Self) -> Self {
|
|
||||||
Self {
|
|
||||||
arc: Arc::clone(&this.arc),
|
|
||||||
data: this.data,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_root(this: &Self) -> &T {
|
|
||||||
&this.arc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ?Sized, M: ?Sized> Deref for RcuRef<T, M> {
|
|
||||||
type Target = M;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
// Safety: The pointer points into the arc we are holding
|
|
||||||
// while we are alive so is the target
|
|
||||||
// as the content is in an Rcu no mutable acess is given out
|
|
||||||
unsafe { self.data.as_ref() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
76
src/types.rs
76
src/types.rs
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(clippy::new_without_default)] // annotating structs annotated with #[bitfield] doesn't work
|
||||||
|
|
||||||
use crate::arena::*;
|
use crate::arena::*;
|
||||||
use crate::atom_table::*;
|
use crate::atom_table::*;
|
||||||
use crate::forms::*;
|
use crate::forms::*;
|
||||||
@@ -88,18 +90,10 @@ impl ConsPtr {
|
|||||||
.with_tag(tag)
|
.with_tag(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_ptr(self) -> *mut u8 {
|
pub fn as_ptr(self) -> *mut u8 {
|
||||||
let bytes = self.into_bytes();
|
let addr: u64 = self.ptr();
|
||||||
let raw_ptr_bytes = [bytes[1], bytes[2], bytes[3], bytes[4]];
|
addr as usize as *mut _
|
||||||
unsafe { mem::transmute(raw_ptr_bytes) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn as_ptr(self) -> *mut u8 {
|
|
||||||
self.ptr() as *mut _
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -194,7 +188,7 @@ pub enum TrailRef {
|
|||||||
BlackboardOffset(Atom, HeapCellValue), // key atom, key value
|
BlackboardOffset(Atom, HeapCellValue), // key atom, key value
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::enum_variant_names)]
|
#[allow(clippy::enum_variant_names)] // allow the common "Trailed" prefix
|
||||||
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(BitfieldSpecifier, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
#[bits = 6]
|
#[bits = 6]
|
||||||
pub(crate) enum TrailEntryTag {
|
pub(crate) enum TrailEntryTag {
|
||||||
@@ -306,7 +300,10 @@ impl fmt::Debug for HeapCellValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ArenaAllocated> From<TypedArenaPtr<T>> for HeapCellValue {
|
impl<T: ArenaAllocated> From<TypedArenaPtr<T>> for HeapCellValue
|
||||||
|
where
|
||||||
|
T::Payload: Sized,
|
||||||
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(arena_ptr: TypedArenaPtr<T>) -> HeapCellValue {
|
fn from(arena_ptr: TypedArenaPtr<T>) -> HeapCellValue {
|
||||||
HeapCellValue::from(arena_ptr.header_ptr() as u64)
|
HeapCellValue::from(arena_ptr.header_ptr() as u64)
|
||||||
@@ -534,37 +531,13 @@ impl HeapCellValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_raw_ptr_bytes(ptr_bytes: [u8; 4]) -> Self {
|
pub fn from_ptr_addr(ptr_bytes: usize) -> Self {
|
||||||
HeapCellValue::from_bytes([
|
HeapCellValue::from_bytes((ptr_bytes as u64).to_ne_bytes())
|
||||||
ptr_bytes[0],
|
|
||||||
ptr_bytes[1],
|
|
||||||
ptr_bytes[2],
|
|
||||||
ptr_bytes[3],
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
])
|
|
||||||
}
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
|
||||||
#[inline]
|
|
||||||
pub fn from_raw_ptr_bytes(ptr_bytes: [u8; 8]) -> Self {
|
|
||||||
HeapCellValue::from_bytes(ptr_bytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub fn to_ptr_addr(self) -> usize {
|
||||||
#[cfg(target_pointer_width = "32")]
|
u64::from_ne_bytes(self.into_bytes()) as usize
|
||||||
pub fn to_raw_ptr_bytes(self) -> [u8; 4] {
|
|
||||||
let bytes = self.into_bytes();
|
|
||||||
[bytes[0], bytes[1], bytes[2], bytes[3]]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
|
||||||
pub fn to_raw_ptr_bytes(self) -> [u8; 8] {
|
|
||||||
self.into_bytes()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -716,18 +689,10 @@ impl UntypedArenaPtr {
|
|||||||
self.set_m(m);
|
self.set_m(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_ptr(self) -> *const u8 {
|
pub fn get_ptr(self) -> *const u8 {
|
||||||
let bytes = self.into_bytes();
|
let addr: u64 = self.ptr();
|
||||||
let raw_ptr_bytes = [bytes[0], bytes[1], bytes[2], bytes[3]];
|
addr as usize as *const u8
|
||||||
unsafe { mem::transmute(raw_ptr_bytes) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
|
||||||
#[inline]
|
|
||||||
pub fn get_ptr(self) -> *const u8 {
|
|
||||||
self.ptr() as *const u8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -743,6 +708,17 @@ impl UntypedArenaPtr {
|
|||||||
unsafe { self.get_ptr().add(mem::size_of::<ArenaHeader>()) }
|
unsafe { self.get_ptr().add(mem::size_of::<ArenaHeader>()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// - this UntypedArenaPtr actuall pointee type is T
|
||||||
|
/// - the pointer must be non-null
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn as_typed_ptr<T: ?Sized + ArenaAllocated>(self) -> TypedArenaPtr<T>
|
||||||
|
where
|
||||||
|
T::Payload: Sized,
|
||||||
|
{
|
||||||
|
T::typed_ptr(self)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_mark_bit(self) -> bool {
|
pub fn get_mark_bit(self) -> bool {
|
||||||
self.m()
|
self.m()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use serial_test::serial;
|
|||||||
// issue #831
|
// issue #831
|
||||||
#[serial]
|
#[serial]
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn call_0() {
|
fn call_0() {
|
||||||
load_module_test(
|
load_module_test(
|
||||||
"tests-pl/issue831-call0.pl",
|
"tests-pl/issue831-call0.pl",
|
||||||
@@ -15,6 +15,7 @@ fn call_0() {
|
|||||||
// issue #2361
|
// issue #2361
|
||||||
#[serial]
|
#[serial]
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn call_qualification() {
|
fn call_qualification() {
|
||||||
load_module_test("tests-pl/issue2361-call-qualified.pl", "");
|
load_module_test("tests-pl/issue2361-call-qualified.pl", "");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ mod src_tests;
|
|||||||
/// then check that the changes are as expected e.g. by looking at the `git diff`
|
/// then check that the changes are as expected e.g. by looking at the `git diff`
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
|
||||||
#[cfg_attr(miri, ignore = "blocked on crossbeam UB")]
|
#[cfg_attr(
|
||||||
|
miri,
|
||||||
|
ignore = "miri isolation, unsupported operation: can't call foreign function"
|
||||||
|
)]
|
||||||
fn cli_tests() {
|
fn cli_tests() {
|
||||||
trycmd::TestCases::new()
|
trycmd::TestCases::new()
|
||||||
.default_bin_name("scryer-prolog")
|
.default_bin_name("scryer-prolog")
|
||||||
|
|||||||
@@ -3,35 +3,35 @@ use serial_test::serial;
|
|||||||
|
|
||||||
#[serial]
|
#[serial]
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn builtins() {
|
fn builtins() {
|
||||||
load_module_test("src/tests/builtins.pl", "");
|
load_module_test("src/tests/builtins.pl", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[serial]
|
#[serial]
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn call_with_inference_limit() {
|
fn call_with_inference_limit() {
|
||||||
load_module_test("src/tests/call_with_inference_limit.pl", "");
|
load_module_test("src/tests/call_with_inference_limit.pl", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[serial]
|
#[serial]
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn facts() {
|
fn facts() {
|
||||||
load_module_test("src/tests/facts.pl", "");
|
load_module_test("src/tests/facts.pl", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[serial]
|
#[serial]
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn hello_world() {
|
fn hello_world() {
|
||||||
load_module_test("src/tests/hello_world.pl", "Hello World!\n");
|
load_module_test("src/tests/hello_world.pl", "Hello World!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[serial]
|
#[serial]
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn syntax_error() {
|
fn syntax_error() {
|
||||||
load_module_test(
|
load_module_test(
|
||||||
"tests-pl/syntax_error.pl",
|
"tests-pl/syntax_error.pl",
|
||||||
@@ -41,21 +41,21 @@ fn syntax_error() {
|
|||||||
|
|
||||||
#[serial]
|
#[serial]
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn predicates() {
|
fn predicates() {
|
||||||
load_module_test("src/tests/predicates.pl", "");
|
load_module_test("src/tests/predicates.pl", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[serial]
|
#[serial]
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn rules() {
|
fn rules() {
|
||||||
load_module_test("src/tests/rules.pl", "");
|
load_module_test("src/tests/rules.pl", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[serial]
|
#[serial]
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn setup_call_cleanup_load() {
|
fn setup_call_cleanup_load() {
|
||||||
load_module_test(
|
load_module_test(
|
||||||
"src/tests/setup_call_cleanup.pl",
|
"src/tests/setup_call_cleanup.pl",
|
||||||
@@ -65,14 +65,14 @@ fn setup_call_cleanup_load() {
|
|||||||
|
|
||||||
#[serial]
|
#[serial]
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn clpz_load() {
|
fn clpz_load() {
|
||||||
load_module_test("src/tests/clpz/test_clpz.pl", "");
|
load_module_test("src/tests/clpz/test_clpz.pl", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[serial]
|
#[serial]
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(miri, ignore = "blocked on streams.rs UB")]
|
#[cfg_attr(miri, ignore = "it takes too long to run")]
|
||||||
fn iso_conformity_tests() {
|
fn iso_conformity_tests() {
|
||||||
load_module_test("tests-pl/iso-conformity-tests.pl", "All tests passed");
|
load_module_test("tests-pl/iso-conformity-tests.pl", "All tests passed");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user