Move the Store::signature_cache field (#847)
This commit removes the `signature_cache` field from the `Store` type and performs a few internal changes which are aimed to be a bit forward looking towards #777, making `Store` threadsafe. The changes made here are: * The `SignatureRegistry` internal type now contains the reverse map that `signature_cache` was serving to do. This is populated on calls to `register` automatically and is accompanied by a `lookup` method as well. * The `register_wasmtime_signature` and `lookup_wasmtime_signature` methods were removed from `Store` and now instead work by using the `Compiler::signatures` field. * The `SignatureRegistry` type was updated to have interior mutability. The global `Compiler` type is highly likely to get shared across many threads through `Store`, so it needs some form of lock somewhere for mutation of the registry of signatures and this commit opts to put it inside `SignatureRegistry` which will eventually allow for the removal of most `&mut self` method on `Compiler`.
This commit is contained in:
@@ -186,7 +186,7 @@ impl Instance {
|
|||||||
// HACK ensure all handles, instantiated outside Store, present in
|
// HACK ensure all handles, instantiated outside Store, present in
|
||||||
// the store's SignatureRegistry, e.g. WASI instances that are
|
// the store's SignatureRegistry, e.g. WASI instances that are
|
||||||
// imported into this store using the from_handle() method.
|
// imported into this store using the from_handle() method.
|
||||||
let _ = store.register_wasmtime_signature(signature);
|
store.compiler().signatures().register(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should support everything supported by wasmtime_runtime, or
|
// We should support everything supported by wasmtime_runtime, or
|
||||||
|
|||||||
@@ -401,11 +401,5 @@ fn compile(store: &Store, binary: &[u8], module_name: Option<&str>) -> Result<Co
|
|||||||
module_name,
|
module_name,
|
||||||
store.engine().config().debug_info,
|
store.engine().config().debug_info,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Register all module signatures
|
|
||||||
for signature in compiled_module.module().signatures.values() {
|
|
||||||
store.register_wasmtime_signature(signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(compiled_module)
|
Ok(compiled_module)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use wasmtime_environ::{
|
use wasmtime_environ::settings::{self, Configurable};
|
||||||
ir,
|
|
||||||
settings::{self, Configurable},
|
|
||||||
};
|
|
||||||
use wasmtime_jit::{native, CompilationStrategy, Compiler, Features};
|
use wasmtime_jit::{native, CompilationStrategy, Compiler, Features};
|
||||||
|
|
||||||
// Runtime Environment
|
// Runtime Environment
|
||||||
@@ -342,7 +338,6 @@ pub struct Store {
|
|||||||
struct StoreInner {
|
struct StoreInner {
|
||||||
engine: Engine,
|
engine: Engine,
|
||||||
compiler: RefCell<Compiler>,
|
compiler: RefCell<Compiler>,
|
||||||
signature_cache: RefCell<HashMap<wasmtime_runtime::VMSharedSignatureIndex, ir::Signature>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Store {
|
impl Store {
|
||||||
@@ -354,7 +349,6 @@ impl Store {
|
|||||||
inner: Rc::new(StoreInner {
|
inner: Rc::new(StoreInner {
|
||||||
engine: engine.clone(),
|
engine: engine.clone(),
|
||||||
compiler: RefCell::new(compiler),
|
compiler: RefCell::new(compiler),
|
||||||
signature_cache: RefCell::new(HashMap::new()),
|
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,36 +358,14 @@ impl Store {
|
|||||||
&self.inner.engine
|
&self.inner.engine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn compiler(&self) -> std::cell::Ref<'_, Compiler> {
|
||||||
|
self.inner.compiler.borrow()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn compiler_mut(&self) -> std::cell::RefMut<'_, Compiler> {
|
pub(crate) fn compiler_mut(&self) -> std::cell::RefMut<'_, Compiler> {
|
||||||
self.inner.compiler.borrow_mut()
|
self.inner.compiler.borrow_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn register_wasmtime_signature(
|
|
||||||
&self,
|
|
||||||
signature: &ir::Signature,
|
|
||||||
) -> wasmtime_runtime::VMSharedSignatureIndex {
|
|
||||||
use std::collections::hash_map::Entry;
|
|
||||||
let index = self.compiler_mut().signatures().register(signature);
|
|
||||||
match self.inner.signature_cache.borrow_mut().entry(index) {
|
|
||||||
Entry::Vacant(v) => {
|
|
||||||
v.insert(signature.clone());
|
|
||||||
}
|
|
||||||
Entry::Occupied(_) => (),
|
|
||||||
}
|
|
||||||
index
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn lookup_wasmtime_signature(
|
|
||||||
&self,
|
|
||||||
type_index: wasmtime_runtime::VMSharedSignatureIndex,
|
|
||||||
) -> Option<ir::Signature> {
|
|
||||||
self.inner
|
|
||||||
.signature_cache
|
|
||||||
.borrow()
|
|
||||||
.get(&type_index)
|
|
||||||
.cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the stores `a` and `b` refer to the same underlying
|
/// Returns whether the stores `a` and `b` refer to the same underlying
|
||||||
/// `Store`.
|
/// `Store`.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use wasmtime_runtime::{Imports, InstanceHandle, VMFunctionBody};
|
|||||||
|
|
||||||
pub(crate) fn create_handle(
|
pub(crate) fn create_handle(
|
||||||
module: Module,
|
module: Module,
|
||||||
signature_registry: Option<&Store>,
|
store: Option<&Store>,
|
||||||
finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody>,
|
finished_functions: PrimaryMap<DefinedFuncIndex, *const VMFunctionBody>,
|
||||||
state: Box<dyn Any>,
|
state: Box<dyn Any>,
|
||||||
) -> Result<InstanceHandle> {
|
) -> Result<InstanceHandle> {
|
||||||
@@ -26,12 +26,12 @@ pub(crate) fn create_handle(
|
|||||||
let data_initializers = Vec::new();
|
let data_initializers = Vec::new();
|
||||||
|
|
||||||
// Compute indices into the shared signature table.
|
// Compute indices into the shared signature table.
|
||||||
let signatures = signature_registry
|
let signatures = store
|
||||||
.map(|signature_registry| {
|
.map(|store| {
|
||||||
module
|
module
|
||||||
.signatures
|
.signatures
|
||||||
.values()
|
.values()
|
||||||
.map(|sig| signature_registry.register_wasmtime_signature(sig))
|
.map(|sig| store.compiler().signatures().register(sig))
|
||||||
.collect::<PrimaryMap<_, _>>()
|
.collect::<PrimaryMap<_, _>>()
|
||||||
})
|
})
|
||||||
.unwrap_or_else(PrimaryMap::new);
|
.unwrap_or_else(PrimaryMap::new);
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ pub(crate) fn into_checked_anyfunc(
|
|||||||
} => (*vmctx, *address, signature),
|
} => (*vmctx, *address, signature),
|
||||||
_ => panic!("expected function export"),
|
_ => panic!("expected function export"),
|
||||||
};
|
};
|
||||||
let type_index = store.register_wasmtime_signature(signature);
|
let type_index = store.compiler().signatures().register(signature);
|
||||||
wasmtime_runtime::VMCallerCheckedAnyfunc {
|
wasmtime_runtime::VMCallerCheckedAnyfunc {
|
||||||
func_ptr,
|
func_ptr,
|
||||||
type_index,
|
type_index,
|
||||||
@@ -224,7 +224,9 @@ pub(crate) fn from_checked_anyfunc(
|
|||||||
return Val::AnyRef(AnyRef::Null);
|
return Val::AnyRef(AnyRef::Null);
|
||||||
}
|
}
|
||||||
let signature = store
|
let signature = store
|
||||||
.lookup_wasmtime_signature(item.type_index)
|
.compiler()
|
||||||
|
.signatures()
|
||||||
|
.lookup(item.type_index)
|
||||||
.expect("signature");
|
.expect("signature");
|
||||||
let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) };
|
let instance_handle = unsafe { wasmtime_runtime::InstanceHandle::from_vmctx(item.vmctx) };
|
||||||
let export = wasmtime_runtime::Export::Function {
|
let export = wasmtime_runtime::Export::Function {
|
||||||
|
|||||||
@@ -265,8 +265,8 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shared signature registry.
|
/// Shared signature registry.
|
||||||
pub fn signatures(&mut self) -> &mut SignatureRegistry {
|
pub fn signatures(&self) -> &SignatureRegistry {
|
||||||
&mut self.signatures
|
&self.signatures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use crate::vmcontext::VMSharedSignatureIndex;
|
|||||||
use more_asserts::{assert_lt, debug_assert_lt};
|
use more_asserts::{assert_lt, debug_assert_lt};
|
||||||
use std::collections::{hash_map, HashMap};
|
use std::collections::{hash_map, HashMap};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
use std::sync::RwLock;
|
||||||
use wasmtime_environ::ir;
|
use wasmtime_environ::ir;
|
||||||
|
|
||||||
/// WebAssembly requires that the caller and callee signatures in an indirect
|
/// WebAssembly requires that the caller and callee signatures in an indirect
|
||||||
@@ -13,21 +14,33 @@ use wasmtime_environ::ir;
|
|||||||
/// index comparison.
|
/// index comparison.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SignatureRegistry {
|
pub struct SignatureRegistry {
|
||||||
signature_hash: HashMap<ir::Signature, VMSharedSignatureIndex>,
|
// This structure is stored in a `Compiler` and is intended to be shared
|
||||||
|
// across many instances. Ideally instances can themselves be sent across
|
||||||
|
// threads, and ideally we can compile across many threads. As a result we
|
||||||
|
// use interior mutability here with a lock to avoid having callers to
|
||||||
|
// externally synchronize calls to compilation.
|
||||||
|
inner: RwLock<Inner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct Inner {
|
||||||
|
signature2index: HashMap<ir::Signature, VMSharedSignatureIndex>,
|
||||||
|
index2signature: HashMap<VMSharedSignatureIndex, ir::Signature>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SignatureRegistry {
|
impl SignatureRegistry {
|
||||||
/// Create a new `SignatureRegistry`.
|
/// Create a new `SignatureRegistry`.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
signature_hash: HashMap::new(),
|
inner: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a signature and return its unique index.
|
/// Register a signature and return its unique index.
|
||||||
pub fn register(&mut self, sig: &ir::Signature) -> VMSharedSignatureIndex {
|
pub fn register(&self, sig: &ir::Signature) -> VMSharedSignatureIndex {
|
||||||
let len = self.signature_hash.len();
|
let mut inner = self.inner.write().unwrap();
|
||||||
match self.signature_hash.entry(sig.clone()) {
|
let len = inner.signature2index.len();
|
||||||
|
match inner.signature2index.entry(sig.clone()) {
|
||||||
hash_map::Entry::Occupied(entry) => *entry.get(),
|
hash_map::Entry::Occupied(entry) => *entry.get(),
|
||||||
hash_map::Entry::Vacant(entry) => {
|
hash_map::Entry::Vacant(entry) => {
|
||||||
// Keep `signature_hash` len under 2**32 -- VMSharedSignatureIndex::new(std::u32::MAX)
|
// Keep `signature_hash` len under 2**32 -- VMSharedSignatureIndex::new(std::u32::MAX)
|
||||||
@@ -39,8 +52,22 @@ impl SignatureRegistry {
|
|||||||
);
|
);
|
||||||
let sig_id = VMSharedSignatureIndex::new(u32::try_from(len).unwrap());
|
let sig_id = VMSharedSignatureIndex::new(u32::try_from(len).unwrap());
|
||||||
entry.insert(sig_id);
|
entry.insert(sig_id);
|
||||||
|
inner.index2signature.insert(sig_id, sig.clone());
|
||||||
sig_id
|
sig_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Looks up a shared signature index within this registry.
|
||||||
|
///
|
||||||
|
/// Note that for this operation to be semantically correct the `idx` must
|
||||||
|
/// have previously come from a call to `register` of this same object.
|
||||||
|
pub fn lookup(&self, idx: VMSharedSignatureIndex) -> Option<ir::Signature> {
|
||||||
|
self.inner
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.index2signature
|
||||||
|
.get(&idx)
|
||||||
|
.cloned()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user