Skip to content

Commit 4d77cf2

Browse files
authored
Simplify handling of caller-vmctx and dyn VMStore pointers (#10542)
* Simplify handling of caller-vmctx and `dyn VMStore` pointers Just keep a copy of the trait object pointer in `StoreOpaque`, rather than trying to pluck it out of the default caller. Clean up `CallThreadState` so that it doesn't need a caller-vmctx. * preserve comment that accidentally got removed
1 parent 94ec88e commit 4d77cf2

File tree

4 files changed

+95
-79
lines changed

4 files changed

+95
-79
lines changed

crates/wasmtime/src/runtime/store.rs

Lines changed: 78 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ pub struct StoreOpaque {
346346
/// `rooted_host_funcs` below. This structure contains pointers which are
347347
/// otherwise kept alive by the `Arc` references in `rooted_host_funcs`.
348348
store_data: ManuallyDrop<StoreData>,
349+
traitobj: StorePtr,
349350
default_caller: InstanceHandle,
350351

351352
/// Used to optimized wasm->host calls when the host function is defined with
@@ -517,67 +518,83 @@ impl<T> Store<T> {
517518
/// tables created to 10,000. This can be overridden with the
518519
/// [`Store::limiter`] configuration method.
519520
pub fn new(engine: &Engine, data: T) -> Self {
521+
let store_data = StoreData::new();
522+
log::trace!("creating new store {:?}", store_data.id());
523+
520524
let pkey = engine.allocator().next_available_pkey();
521525

522-
let mut inner = Box::new(StoreInner {
523-
inner: StoreOpaque {
524-
_marker: marker::PhantomPinned,
525-
engine: engine.clone(),
526-
vm_store_context: Default::default(),
527-
instances: Vec::new(),
528-
#[cfg(feature = "component-model")]
529-
num_component_instances: 0,
530-
signal_handler: None,
531-
gc_store: None,
532-
gc_roots: RootSet::default(),
533-
#[cfg(feature = "gc")]
534-
gc_roots_list: GcRootsList::default(),
535-
#[cfg(feature = "gc")]
536-
gc_host_alloc_types: Default::default(),
537-
modules: ModuleRegistry::default(),
538-
func_refs: FuncRefs::default(),
539-
host_globals: Vec::new(),
540-
instance_count: 0,
541-
instance_limit: crate::DEFAULT_INSTANCE_LIMIT,
542-
memory_count: 0,
543-
memory_limit: crate::DEFAULT_MEMORY_LIMIT,
544-
table_count: 0,
545-
table_limit: crate::DEFAULT_TABLE_LIMIT,
546-
#[cfg(feature = "async")]
547-
async_state: AsyncState::default(),
548-
fuel_reserve: 0,
549-
fuel_yield_interval: None,
550-
store_data: ManuallyDrop::new(StoreData::new()),
551-
default_caller: InstanceHandle::null(),
552-
hostcall_val_storage: Vec::new(),
553-
wasm_val_raw_storage: Vec::new(),
554-
rooted_host_funcs: ManuallyDrop::new(Vec::new()),
555-
pkey,
556-
#[cfg(feature = "component-model")]
557-
component_host_table: Default::default(),
558-
#[cfg(feature = "component-model")]
559-
component_calls: Default::default(),
560-
#[cfg(feature = "component-model")]
561-
host_resource_data: Default::default(),
562-
#[cfg(has_host_compiler_backend)]
563-
executor: if cfg!(feature = "pulley") && engine.target().is_pulley() {
564-
Executor::Interpreter(Interpreter::new(engine))
565-
} else {
566-
Executor::Native
567-
},
568-
#[cfg(not(has_host_compiler_backend))]
569-
executor: {
570-
debug_assert!(engine.target().is_pulley());
571-
Executor::Interpreter(Interpreter::new(engine))
572-
},
526+
let inner = StoreOpaque {
527+
_marker: marker::PhantomPinned,
528+
engine: engine.clone(),
529+
vm_store_context: Default::default(),
530+
instances: Vec::new(),
531+
#[cfg(feature = "component-model")]
532+
num_component_instances: 0,
533+
signal_handler: None,
534+
gc_store: None,
535+
gc_roots: RootSet::default(),
536+
#[cfg(feature = "gc")]
537+
gc_roots_list: GcRootsList::default(),
538+
#[cfg(feature = "gc")]
539+
gc_host_alloc_types: Default::default(),
540+
modules: ModuleRegistry::default(),
541+
func_refs: FuncRefs::default(),
542+
host_globals: Vec::new(),
543+
instance_count: 0,
544+
instance_limit: crate::DEFAULT_INSTANCE_LIMIT,
545+
memory_count: 0,
546+
memory_limit: crate::DEFAULT_MEMORY_LIMIT,
547+
table_count: 0,
548+
table_limit: crate::DEFAULT_TABLE_LIMIT,
549+
#[cfg(feature = "async")]
550+
async_state: AsyncState::default(),
551+
fuel_reserve: 0,
552+
fuel_yield_interval: None,
553+
store_data: ManuallyDrop::new(store_data),
554+
traitobj: StorePtr::empty(),
555+
default_caller: InstanceHandle::null(),
556+
hostcall_val_storage: Vec::new(),
557+
wasm_val_raw_storage: Vec::new(),
558+
rooted_host_funcs: ManuallyDrop::new(Vec::new()),
559+
pkey,
560+
#[cfg(feature = "component-model")]
561+
component_host_table: Default::default(),
562+
#[cfg(feature = "component-model")]
563+
component_calls: Default::default(),
564+
#[cfg(feature = "component-model")]
565+
host_resource_data: Default::default(),
566+
#[cfg(has_host_compiler_backend)]
567+
executor: if cfg!(feature = "pulley") && engine.target().is_pulley() {
568+
Executor::Interpreter(Interpreter::new(engine))
569+
} else {
570+
Executor::Native
573571
},
572+
#[cfg(not(has_host_compiler_backend))]
573+
executor: {
574+
debug_assert!(engine.target().is_pulley());
575+
Executor::Interpreter(Interpreter::new(engine))
576+
},
577+
};
578+
let mut inner = Box::new(StoreInner {
579+
inner,
574580
limiter: None,
575581
call_hook: None,
576582
#[cfg(target_has_atomic = "64")]
577583
epoch_deadline_behavior: None,
578584
data: ManuallyDrop::new(data),
579585
});
580586

587+
// Note the erasure of the lifetime here into `'static`, so in general
588+
// usage of this trait object must be strictly bounded to the `Store`
589+
// itself, and this is an invariant that we have to maintain throughout
590+
// Wasmtime.
591+
inner.traitobj = StorePtr::new(unsafe {
592+
mem::transmute::<
593+
NonNull<dyn crate::runtime::vm::VMStore + '_>,
594+
NonNull<dyn crate::runtime::vm::VMStore + 'static>,
595+
>(NonNull::from(&mut *inner))
596+
});
597+
581598
// Wasmtime uses the callee argument to host functions to learn about
582599
// the original pointer to the `Store` itself, allowing it to
583600
// reconstruct a `StoreContextMut<T>`. When we initially call a `Func`,
@@ -589,9 +606,11 @@ impl<T> Store<T> {
589606
let module = Arc::new(wasmtime_environ::Module::default());
590607
let shim = ModuleRuntimeInfo::bare(module);
591608
let allocator = OnDemandInstanceAllocator::default();
609+
592610
allocator
593611
.validate_module(shim.env_module(), shim.offsets())
594612
.unwrap();
613+
595614
let mut instance = unsafe {
596615
allocator
597616
.allocate_module(InstanceAllocationRequest {
@@ -605,19 +624,10 @@ impl<T> Store<T> {
605624
})
606625
.expect("failed to allocate default callee")
607626
};
608-
609-
// Note the erasure of the lifetime here into `'static`, so in
610-
// general usage of this trait object must be strictly bounded to
611-
// the `Store` itself, and this is an invariant that we have to
612-
// maintain throughout Wasmtime.
613627
unsafe {
614-
let traitobj = mem::transmute::<
615-
NonNull<dyn crate::runtime::vm::VMStore + '_>,
616-
NonNull<dyn crate::runtime::vm::VMStore + 'static>,
617-
>(NonNull::from(&mut *inner));
618-
instance.set_store(traitobj);
619-
instance
628+
instance.set_store(Some(inner.traitobj()));
620629
}
630+
instance
621631
};
622632

623633
Self {
@@ -1705,7 +1715,12 @@ impl StoreOpaque {
17051715

17061716
#[inline]
17071717
pub fn traitobj(&self) -> NonNull<dyn crate::runtime::vm::VMStore> {
1708-
self.default_caller.traitobj(self)
1718+
self.traitobj.as_raw().unwrap()
1719+
}
1720+
1721+
#[inline]
1722+
pub fn traitobj_mut(&mut self) -> &mut dyn crate::runtime::vm::VMStore {
1723+
unsafe { self.traitobj().as_mut() }
17091724
}
17101725

17111726
/// Takes the cached `Vec<Val>` stored internally across hostcalls to get

crates/wasmtime/src/runtime/vm/instance.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,8 +1712,8 @@ impl InstanceHandle {
17121712
///
17131713
/// This is provided for the original `Store` itself to configure the first
17141714
/// self-pointer after the original `Box` has been initialized.
1715-
pub unsafe fn set_store(&mut self, store: NonNull<dyn VMStore>) {
1716-
self.instance_mut().set_store(Some(store));
1715+
pub unsafe fn set_store(&mut self, store: Option<NonNull<dyn VMStore>>) {
1716+
self.instance_mut().set_store(store);
17171717
}
17181718

17191719
/// Returns a clone of this instance.

crates/wasmtime/src/runtime/vm/instance/allocator.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ pub struct InstanceAllocationRequest<'a> {
9494
/// InstanceAllocationRequest.
9595
pub struct StorePtr(Option<NonNull<dyn VMStore>>);
9696

97+
// We can't make `VMStore: Send + Sync` because that requires making all of
98+
// Wastime's internals generic over the `Store`'s `T`. So instead, we take care
99+
// in the whole VM layer to only use the `VMStore` in ways that are `Send`- and
100+
// `Sync`-safe and we have to have these unsafe impls.
101+
unsafe impl Send for StorePtr {}
102+
unsafe impl Sync for StorePtr {}
103+
97104
impl StorePtr {
98105
/// A pointer to no Store.
99106
pub fn empty() -> Self {

crates/wasmtime/src/runtime/vm/traphandlers.rs

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::prelude::*;
1919
use crate::runtime::module::lookup_code;
2020
use crate::runtime::store::{ExecutorRef, StoreOpaque};
2121
use crate::runtime::vm::sys::traphandlers;
22-
use crate::runtime::vm::{Instance, InterpreterRef, VMContext, VMStoreContext};
22+
use crate::runtime::vm::{InterpreterRef, VMContext, VMStoreContext};
2323
use crate::{StoreContextMut, WasmBacktrace};
2424
use core::cell::Cell;
2525
use core::num::NonZeroU32;
@@ -371,7 +371,8 @@ where
371371
F: FnMut(NonNull<VMContext>, Option<InterpreterRef<'_>>) -> bool,
372372
{
373373
let caller = store.0.default_caller();
374-
let result = CallThreadState::new(store.0, caller).with(|cx| match store.0.executor() {
374+
375+
let result = CallThreadState::new(store.0).with(|cx| match store.0.executor() {
375376
// In interpreted mode directly invoke the host closure since we won't
376377
// be using host-based `setjmp`/`longjmp` as that's not going to save
377378
// the context we want.
@@ -474,14 +475,7 @@ mod call_thread_state {
474475
pub const JMP_BUF_INTERPRETER_SENTINEL: *mut u8 = 1 as *mut u8;
475476

476477
#[inline]
477-
pub(super) fn new(store: &mut StoreOpaque, caller: NonNull<VMContext>) -> CallThreadState {
478-
let vm_store_context = unsafe {
479-
Instance::from_vmctx(caller, |i| i.vm_store_context())
480-
.read()
481-
.unwrap()
482-
.as_non_null()
483-
};
484-
478+
pub(super) fn new(store: &mut StoreOpaque) -> CallThreadState {
485479
// Don't try to plumb #[cfg] everywhere for this field, just pretend
486480
// we're using it on miri/windows to silence compiler warnings.
487481
let _: Range<_> = store.async_guard_range();
@@ -495,18 +489,18 @@ mod call_thread_state {
495489
capture_backtrace: store.engine().config().wasm_backtrace,
496490
#[cfg(feature = "coredump")]
497491
capture_coredump: store.engine().config().coredump_on_trap,
498-
vm_store_context,
492+
vm_store_context: store.vm_store_context_ptr(),
499493
#[cfg(all(has_native_signals, unix))]
500494
async_guard_range: store.async_guard_range(),
501495
prev: Cell::new(ptr::null()),
502496
old_last_wasm_exit_fp: Cell::new(unsafe {
503-
*vm_store_context.as_ref().last_wasm_exit_fp.get()
497+
*store.vm_store_context().last_wasm_exit_fp.get()
504498
}),
505499
old_last_wasm_exit_pc: Cell::new(unsafe {
506-
*vm_store_context.as_ref().last_wasm_exit_pc.get()
500+
*store.vm_store_context().last_wasm_exit_pc.get()
507501
}),
508502
old_last_wasm_entry_fp: Cell::new(unsafe {
509-
*vm_store_context.as_ref().last_wasm_entry_fp.get()
503+
*store.vm_store_context().last_wasm_entry_fp.get()
510504
}),
511505
}
512506
}

0 commit comments

Comments
 (0)