Add feature flag for opt-in thread-safety
The `atomic` dependency is necessary because Rusts's Atomic type doesn't support generic inner values.
This commit is contained in:
parent
7546d3026d
commit
99b0e03e4e
|
@ -17,6 +17,7 @@ memory_units = "0.3.0"
|
|||
libm = { version = "0.1.2", optional = true }
|
||||
num-rational = "0.2.2"
|
||||
num-traits = "0.2.8"
|
||||
atomic = { version = "0.4", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.1"
|
||||
|
@ -24,6 +25,7 @@ rand = "0.4.2"
|
|||
wabt = "0.6"
|
||||
|
||||
[features]
|
||||
threadsafe = ["atomic"]
|
||||
default = ["std"]
|
||||
# Disable for no_std support
|
||||
std = [
|
||||
|
|
23
src/func.rs
23
src/func.rs
|
@ -1,6 +1,5 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::v1::*;
|
||||
use alloc::rc::{Rc, Weak};
|
||||
use core::fmt;
|
||||
use host::Externals;
|
||||
use isa;
|
||||
|
@ -17,7 +16,7 @@ use {Signature, Trap};
|
|||
///
|
||||
/// [`FuncInstance`]: struct.FuncInstance.html
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FuncRef(Rc<FuncInstance>);
|
||||
pub struct FuncRef(::MyRc<FuncInstance>);
|
||||
|
||||
impl ::core::ops::Deref for FuncRef {
|
||||
type Target = FuncInstance;
|
||||
|
@ -45,9 +44,9 @@ pub struct FuncInstance(FuncInstanceInternal);
|
|||
#[derive(Clone)]
|
||||
pub(crate) enum FuncInstanceInternal {
|
||||
Internal {
|
||||
signature: Rc<Signature>,
|
||||
module: Weak<ModuleInstance>,
|
||||
body: Rc<FuncBody>,
|
||||
signature: ::MyRc<Signature>,
|
||||
module: ::MyWeak<ModuleInstance>,
|
||||
body: ::MyRc<FuncBody>,
|
||||
},
|
||||
Host {
|
||||
signature: Signature,
|
||||
|
@ -84,7 +83,7 @@ impl FuncInstance {
|
|||
signature,
|
||||
host_func_index,
|
||||
};
|
||||
FuncRef(Rc::new(FuncInstance(func)))
|
||||
FuncRef(::MyRc::new(FuncInstance(func)))
|
||||
}
|
||||
|
||||
/// Returns [signature] of this function instance.
|
||||
|
@ -104,21 +103,21 @@ impl FuncInstance {
|
|||
}
|
||||
|
||||
pub(crate) fn alloc_internal(
|
||||
module: Weak<ModuleInstance>,
|
||||
signature: Rc<Signature>,
|
||||
module: ::MyWeak<ModuleInstance>,
|
||||
signature: ::MyRc<Signature>,
|
||||
body: FuncBody,
|
||||
) -> FuncRef {
|
||||
let func = FuncInstanceInternal::Internal {
|
||||
signature,
|
||||
module: module,
|
||||
body: Rc::new(body),
|
||||
body: ::MyRc::new(body),
|
||||
};
|
||||
FuncRef(Rc::new(FuncInstance(func)))
|
||||
FuncRef(::MyRc::new(FuncInstance(func)))
|
||||
}
|
||||
|
||||
pub(crate) fn body(&self) -> Option<Rc<FuncBody>> {
|
||||
pub(crate) fn body(&self) -> Option<::MyRc<FuncBody>> {
|
||||
match *self.as_internal() {
|
||||
FuncInstanceInternal::Internal { ref body, .. } => Some(Rc::clone(body)),
|
||||
FuncInstanceInternal::Internal { ref body, .. } => Some(::MyRc::clone(body)),
|
||||
FuncInstanceInternal::Host { .. } => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use alloc::rc::Rc;
|
||||
use core::cell::Cell;
|
||||
use parity_wasm::elements::ValueType as EValueType;
|
||||
use types::ValueType;
|
||||
use value::RuntimeValue;
|
||||
|
@ -11,7 +9,7 @@ use Error;
|
|||
///
|
||||
/// [`GlobalInstance`]: struct.GlobalInstance.html
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GlobalRef(Rc<GlobalInstance>);
|
||||
pub struct GlobalRef(::MyRc<GlobalInstance>);
|
||||
|
||||
impl ::core::ops::Deref for GlobalRef {
|
||||
type Target = GlobalInstance;
|
||||
|
@ -33,7 +31,7 @@ impl ::core::ops::Deref for GlobalRef {
|
|||
/// [`I64`]: enum.RuntimeValue.html#variant.I64
|
||||
#[derive(Debug)]
|
||||
pub struct GlobalInstance {
|
||||
val: Cell<RuntimeValue>,
|
||||
val: ::MyCell<RuntimeValue>,
|
||||
mutable: bool,
|
||||
}
|
||||
|
||||
|
@ -43,8 +41,8 @@ impl GlobalInstance {
|
|||
/// Since it is possible to export only immutable globals,
|
||||
/// users likely want to set `mutable` to `false`.
|
||||
pub fn alloc(val: RuntimeValue, mutable: bool) -> GlobalRef {
|
||||
GlobalRef(Rc::new(GlobalInstance {
|
||||
val: Cell::new(val),
|
||||
GlobalRef(::MyRc::new(GlobalInstance {
|
||||
val: ::MyCell::new(val),
|
||||
mutable,
|
||||
}))
|
||||
}
|
||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -396,6 +396,12 @@ mod table;
|
|||
mod types;
|
||||
mod value;
|
||||
|
||||
#[cfg(feature = "threadsafe")]
|
||||
mod threadsafe;
|
||||
|
||||
#[cfg(not(feature = "threadsafe"))]
|
||||
mod not_threadsafe;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
|
@ -410,6 +416,12 @@ pub use self::table::{TableInstance, TableRef};
|
|||
pub use self::types::{GlobalDescriptor, MemoryDescriptor, Signature, TableDescriptor, ValueType};
|
||||
pub use self::value::{Error as ValueError, FromRuntimeValue, LittleEndianConvert, RuntimeValue};
|
||||
|
||||
#[cfg(feature = "threadsafe")]
|
||||
pub use self::threadsafe::*;
|
||||
|
||||
#[cfg(not(feature = "threadsafe"))]
|
||||
pub use self::not_threadsafe::*;
|
||||
|
||||
/// WebAssembly-specific sizes and units.
|
||||
pub mod memory_units {
|
||||
pub use memory_units_crate::wasm32::*;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::v1::*;
|
||||
use alloc::rc::Rc;
|
||||
use core::{
|
||||
cell::{Cell, RefCell},
|
||||
cmp, fmt,
|
||||
ops::Range,
|
||||
u32,
|
||||
|
@ -26,7 +24,7 @@ pub const LINEAR_MEMORY_PAGE_SIZE: Bytes = Bytes(65536);
|
|||
/// [`MemoryInstance`]: struct.MemoryInstance.html
|
||||
///
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MemoryRef(Rc<MemoryInstance>);
|
||||
pub struct MemoryRef(::MyRc<MemoryInstance>);
|
||||
|
||||
impl ::core::ops::Deref for MemoryRef {
|
||||
type Target = MemoryInstance;
|
||||
|
@ -52,11 +50,11 @@ pub struct MemoryInstance {
|
|||
/// Memory limits.
|
||||
limits: ResizableLimits,
|
||||
/// Linear memory buffer with lazy allocation.
|
||||
buffer: RefCell<Vec<u8>>,
|
||||
buffer: ::MyRefCell<Vec<u8>>,
|
||||
initial: Pages,
|
||||
current_size: Cell<usize>,
|
||||
current_size: ::MyCell<usize>,
|
||||
maximum: Option<Pages>,
|
||||
lowest_used: Cell<u32>,
|
||||
lowest_used: ::MyCell<u32>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for MemoryInstance {
|
||||
|
@ -127,7 +125,7 @@ impl MemoryInstance {
|
|||
}
|
||||
|
||||
let memory = MemoryInstance::new(initial, maximum);
|
||||
Ok(MemoryRef(Rc::new(memory)))
|
||||
Ok(MemoryRef(::MyRc::new(memory)))
|
||||
}
|
||||
|
||||
/// Create new linear memory instance.
|
||||
|
@ -137,11 +135,11 @@ impl MemoryInstance {
|
|||
let initial_size: Bytes = initial.into();
|
||||
MemoryInstance {
|
||||
limits: limits,
|
||||
buffer: RefCell::new(Vec::with_capacity(4096)),
|
||||
buffer: ::MyRefCell::new(Vec::with_capacity(4096)),
|
||||
initial: initial,
|
||||
current_size: Cell::new(initial_size.0),
|
||||
current_size: ::MyCell::new(initial_size.0),
|
||||
maximum: maximum,
|
||||
lowest_used: Cell::new(u32::max_value()),
|
||||
lowest_used: ::MyCell::new(u32::max_value()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -558,7 +556,6 @@ mod tests {
|
|||
|
||||
use super::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
|
||||
use memory_units::Pages;
|
||||
use std::rc::Rc;
|
||||
use Error;
|
||||
|
||||
#[test]
|
||||
|
@ -660,8 +657,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn transfer_works() {
|
||||
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
let dst = MemoryRef(Rc::new(create_memory(&[
|
||||
let src = MemoryRef(::MyRc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
let dst = MemoryRef(::MyRc::new(create_memory(&[
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
])));
|
||||
|
||||
|
@ -676,7 +673,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn transfer_still_works_with_same_memory() {
|
||||
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
let src = MemoryRef(::MyRc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
|
||||
MemoryInstance::transfer(&src, 4, &src, 0, 3).unwrap();
|
||||
|
||||
|
@ -685,7 +682,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn transfer_oob_with_same_memory_errors() {
|
||||
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
let src = MemoryRef(::MyRc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
assert!(MemoryInstance::transfer(&src, 65535, &src, 0, 3).is_err());
|
||||
|
||||
// Check that memories content left untouched
|
||||
|
@ -694,8 +691,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn transfer_oob_errors() {
|
||||
let src = MemoryRef(Rc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
let dst = MemoryRef(Rc::new(create_memory(&[
|
||||
let src = MemoryRef(::MyRc::new(create_memory(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9])));
|
||||
let dst = MemoryRef(::MyRc::new(create_memory(&[
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
])));
|
||||
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::v1::*;
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::RefCell;
|
||||
use core::fmt;
|
||||
use Trap;
|
||||
|
||||
use alloc::collections::BTreeMap;
|
||||
|
||||
use core::cell::Ref;
|
||||
use func::{FuncBody, FuncInstance, FuncRef};
|
||||
use global::{GlobalInstance, GlobalRef};
|
||||
use host::Externals;
|
||||
|
@ -35,7 +32,7 @@ use {Error, MemoryInstance, Module, RuntimeValue, Signature, TableInstance};
|
|||
///
|
||||
/// [`ModuleInstance`]: struct.ModuleInstance.html
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ModuleRef(pub(crate) Rc<ModuleInstance>);
|
||||
pub struct ModuleRef(pub(crate) ::MyRc<ModuleInstance>);
|
||||
|
||||
impl ::core::ops::Deref for ModuleRef {
|
||||
type Target = ModuleInstance;
|
||||
|
@ -154,23 +151,23 @@ impl ExternVal {
|
|||
/// [`invoke_export`]: #method.invoke_export
|
||||
#[derive(Debug)]
|
||||
pub struct ModuleInstance {
|
||||
signatures: RefCell<Vec<Rc<Signature>>>,
|
||||
tables: RefCell<Vec<TableRef>>,
|
||||
funcs: RefCell<Vec<FuncRef>>,
|
||||
memories: RefCell<Vec<MemoryRef>>,
|
||||
globals: RefCell<Vec<GlobalRef>>,
|
||||
exports: RefCell<BTreeMap<String, ExternVal>>,
|
||||
signatures: ::MyRefCell<Vec<::MyRc<Signature>>>,
|
||||
tables: ::MyRefCell<Vec<TableRef>>,
|
||||
funcs: ::MyRefCell<Vec<FuncRef>>,
|
||||
memories: ::MyRefCell<Vec<MemoryRef>>,
|
||||
globals: ::MyRefCell<Vec<GlobalRef>>,
|
||||
exports: ::MyRefCell<BTreeMap<String, ExternVal>>,
|
||||
}
|
||||
|
||||
impl ModuleInstance {
|
||||
fn default() -> Self {
|
||||
ModuleInstance {
|
||||
funcs: RefCell::new(Vec::new()),
|
||||
signatures: RefCell::new(Vec::new()),
|
||||
tables: RefCell::new(Vec::new()),
|
||||
memories: RefCell::new(Vec::new()),
|
||||
globals: RefCell::new(Vec::new()),
|
||||
exports: RefCell::new(BTreeMap::new()),
|
||||
funcs: ::MyRefCell::new(Vec::new()),
|
||||
signatures: ::MyRefCell::new(Vec::new()),
|
||||
tables: ::MyRefCell::new(Vec::new()),
|
||||
memories: ::MyRefCell::new(Vec::new()),
|
||||
globals: ::MyRefCell::new(Vec::new()),
|
||||
exports: ::MyRefCell::new(BTreeMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,7 +187,7 @@ impl ModuleInstance {
|
|||
self.funcs.borrow().get(idx as usize).cloned()
|
||||
}
|
||||
|
||||
pub(crate) fn signature_by_index(&self, idx: u32) -> Option<Rc<Signature>> {
|
||||
pub(crate) fn signature_by_index(&self, idx: u32) -> Option<::MyRc<Signature>> {
|
||||
self.signatures.borrow().get(idx as usize).cloned()
|
||||
}
|
||||
|
||||
|
@ -198,7 +195,7 @@ impl ModuleInstance {
|
|||
self.funcs.borrow_mut().push(func);
|
||||
}
|
||||
|
||||
fn push_signature(&self, signature: Rc<Signature>) {
|
||||
fn push_signature(&self, signature: ::MyRc<Signature>) {
|
||||
self.signatures.borrow_mut().push(signature)
|
||||
}
|
||||
|
||||
|
@ -216,7 +213,7 @@ impl ModuleInstance {
|
|||
|
||||
/// Access all globals. This is a non-standard API so it's unlikely to be
|
||||
/// portable to other engines.
|
||||
pub fn globals<'a>(&self) -> Ref<Vec<GlobalRef>> {
|
||||
pub fn globals<'a>(&self) -> ::MyRef<Vec<GlobalRef>> {
|
||||
self.globals.borrow()
|
||||
}
|
||||
|
||||
|
@ -229,10 +226,10 @@ impl ModuleInstance {
|
|||
extern_vals: I,
|
||||
) -> Result<ModuleRef, Error> {
|
||||
let module = loaded_module.module();
|
||||
let instance = ModuleRef(Rc::new(ModuleInstance::default()));
|
||||
let instance = ModuleRef(::MyRc::new(ModuleInstance::default()));
|
||||
|
||||
for &Type::Function(ref ty) in module.type_section().map(|ts| ts.types()).unwrap_or(&[]) {
|
||||
let signature = Rc::new(Signature::from_elements(ty));
|
||||
let signature = ::MyRc::new(Signature::from_elements(ty));
|
||||
instance.push_signature(signature);
|
||||
}
|
||||
|
||||
|
@ -327,7 +324,7 @@ impl ModuleInstance {
|
|||
code: code,
|
||||
};
|
||||
let func_instance =
|
||||
FuncInstance::alloc_internal(Rc::downgrade(&instance.0), signature, func_body);
|
||||
FuncInstance::alloc_internal(::MyRc::downgrade(&instance.0), signature, func_body);
|
||||
instance.push_func(func_instance);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
pub use alloc::rc::Rc as MyRc;
|
||||
pub use alloc::rc::Weak as MyWeak;
|
||||
pub use core::cell::Cell as MyCell;
|
||||
pub use core::cell::RefCell as MyRefCell;
|
||||
pub use core::cell::Ref as MyRef;
|
10
src/table.rs
10
src/table.rs
|
@ -1,7 +1,5 @@
|
|||
#[allow(unused_imports)]
|
||||
use alloc::prelude::v1::*;
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::RefCell;
|
||||
use core::fmt;
|
||||
use core::u32;
|
||||
use func::FuncRef;
|
||||
|
@ -16,7 +14,7 @@ use Error;
|
|||
/// [`TableInstance`]: struct.TableInstance.html
|
||||
///
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TableRef(Rc<TableInstance>);
|
||||
pub struct TableRef(::MyRc<TableInstance>);
|
||||
|
||||
impl ::core::ops::Deref for TableRef {
|
||||
type Target = TableInstance;
|
||||
|
@ -42,7 +40,7 @@ pub struct TableInstance {
|
|||
/// Table limits.
|
||||
limits: ResizableLimits,
|
||||
/// Table memory buffer.
|
||||
buffer: RefCell<Vec<Option<FuncRef>>>,
|
||||
buffer: ::MyRefCell<Vec<Option<FuncRef>>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for TableInstance {
|
||||
|
@ -67,13 +65,13 @@ impl TableInstance {
|
|||
/// Returns `Err` if `initial_size` is greater than `maximum_size`.
|
||||
pub fn alloc(initial_size: u32, maximum_size: Option<u32>) -> Result<TableRef, Error> {
|
||||
let table = TableInstance::new(ResizableLimits::new(initial_size, maximum_size))?;
|
||||
Ok(TableRef(Rc::new(table)))
|
||||
Ok(TableRef(::MyRc::new(table)))
|
||||
}
|
||||
|
||||
fn new(limits: ResizableLimits) -> Result<TableInstance, Error> {
|
||||
check_limits(&limits)?;
|
||||
Ok(TableInstance {
|
||||
buffer: RefCell::new(vec![None; limits.initial() as usize]),
|
||||
buffer: ::MyRefCell::new(vec![None; limits.initial() as usize]),
|
||||
limits: limits,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
extern crate atomic;
|
||||
|
||||
use alloc::sync::{Arc, Mutex};
|
||||
|
||||
pub use alloc::sync::{
|
||||
Arc as MyRc, Weak as MyWeak, MutexGuard as MyRef,
|
||||
};
|
||||
pub use self::atomic::{
|
||||
Atomic, Ordering::Relaxed as Ordering,
|
||||
};
|
||||
|
||||
/// Thread-safe wrapper which can be used in place of a `RefCell`.
|
||||
#[derive(Debug)]
|
||||
pub struct MyRefCell<T>(Arc<Mutex<T>>);
|
||||
|
||||
impl<T> MyRefCell<T> {
|
||||
/// Create new wrapper object.
|
||||
pub fn new(obj: T) -> MyRefCell<T> {
|
||||
MyRefCell(Arc::new(Mutex::new(obj)))
|
||||
}
|
||||
|
||||
/// Borrow a `MyRef` to the inner value.
|
||||
pub fn borrow(&self) -> ::MyRef<T> {
|
||||
self.0.lock().expect("bar")
|
||||
}
|
||||
|
||||
/// Borrow a mutable `MyRef` to the inner value.
|
||||
pub fn borrow_mut(&self) -> ::MyRef<T> {
|
||||
self.0.lock().expect("bar")
|
||||
}
|
||||
}
|
||||
|
||||
/// Thread-safe wrapper which can be used in place of a `Cell`.
|
||||
#[derive(Debug)]
|
||||
pub struct MyCell<T>(Atomic<T>) where T: Copy;
|
||||
|
||||
impl<T> MyCell<T> where T: Copy {
|
||||
/// Create new wrapper object.
|
||||
pub fn new(obj: T) -> MyCell<T> {
|
||||
MyCell(Atomic::new(obj))
|
||||
}
|
||||
|
||||
/// Returns the inner value.
|
||||
pub fn get(&self) -> T {
|
||||
self.0.load(::Ordering)
|
||||
}
|
||||
|
||||
/// Sets the inner value.
|
||||
pub fn set(&self, val: T) {
|
||||
self.0.store(val, ::Ordering);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue