Skip to content

Commit e5234e6

Browse files
committed
review feedback and fix tests
1 parent e74cc29 commit e5234e6

File tree

8 files changed

+166
-75
lines changed

8 files changed

+166
-75
lines changed

crates/wasmtime/src/runtime/gc/enabled/arrayref.rs

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -306,11 +306,6 @@ impl ArrayRef {
306306
elem: &Val,
307307
len: u32,
308308
) -> Result<Rooted<ArrayRef>> {
309-
assert_eq!(
310-
store.id(),
311-
allocator.store_id,
312-
"attempted to use a `ArrayRefPre` with the wrong store"
313-
);
314309
assert!(
315310
!store.async_support(),
316311
" use `ArrayRef::new_async` with asynchronous stores"
@@ -374,11 +369,6 @@ impl ArrayRef {
374369
elem: &Val,
375370
len: u32,
376371
) -> Result<Rooted<ArrayRef>> {
377-
assert_eq!(
378-
store.id(),
379-
allocator.store_id,
380-
"attempted to use a `ArrayRefPre` with the wrong store"
381-
);
382372
assert!(
383373
store.async_support(),
384374
"use `ArrayRef::new` with synchronous stores"
@@ -395,13 +385,38 @@ impl ArrayRef {
395385
.await
396386
}
397387

388+
/// Like `ArrayRef::new` but when async is configured must only ever be
389+
/// called from on a fiber stack.
390+
pub(crate) unsafe fn new_maybe_async(
391+
store: &mut StoreOpaque,
392+
allocator: &ArrayRefPre,
393+
elem: &Val,
394+
len: u32,
395+
) -> Result<Rooted<ArrayRef>> {
396+
// Type check the initial element value against the element type.
397+
elem.ensure_matches_ty(store, allocator.ty.element_type().unpack())
398+
.context("element type mismatch")?;
399+
400+
unsafe {
401+
store.retry_after_gc_maybe_async((), |store, ()| {
402+
Self::_new_unchecked(store, allocator, RepeatN(elem, len))
403+
})
404+
}
405+
}
406+
398407
/// Allocate a new array of the given elements, without checking that the
399408
/// elements' types match the array's element type.
400409
fn _new_unchecked<'a>(
401410
store: &mut StoreOpaque,
402411
allocator: &ArrayRefPre,
403412
elems: impl ExactSizeIterator<Item = &'a Val>,
404413
) -> Result<Rooted<ArrayRef>> {
414+
assert_eq!(
415+
store.id(),
416+
allocator.store_id,
417+
"attempted to use a `ArrayRefPre` with the wrong store"
418+
);
419+
405420
let len = u32::try_from(elems.len()).unwrap();
406421

407422
// Allocate the array and write each field value into the appropriate
@@ -572,6 +587,34 @@ impl ArrayRef {
572587
.await
573588
}
574589

590+
/// Like `ArrayRef::new_fixed[_async]` but it is the caller's responsibility
591+
/// to ensure that when async is enabled, this is only called from on a
592+
/// fiber stack.
593+
#[cfg(feature = "async")]
594+
pub(crate) unsafe fn _new_fixed_maybe_async(
595+
store: &mut StoreOpaque,
596+
allocator: &ArrayRefPre,
597+
elems: &[Val],
598+
) -> Result<Rooted<ArrayRef>> {
599+
assert_eq!(
600+
store.id(),
601+
allocator.store_id,
602+
"attempted to use a `ArrayRefPre` with the wrong store"
603+
);
604+
605+
// Type check the elements against the element type.
606+
for elem in elems {
607+
elem.ensure_matches_ty(store, allocator.ty.element_type().unpack())
608+
.context("element type mismatch")?;
609+
}
610+
611+
unsafe {
612+
store.retry_after_gc_maybe_async((), |store, ()| {
613+
Self::_new_unchecked(store, allocator, elems.iter())
614+
})
615+
}
616+
}
617+
575618
#[inline]
576619
pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
577620
self.inner.comes_from_same_store(store)

crates/wasmtime/src/runtime/gc/enabled/structref.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -240,11 +240,6 @@ impl StructRef {
240240
allocator: &StructRefPre,
241241
fields: &[Val],
242242
) -> Result<Rooted<StructRef>> {
243-
assert_eq!(
244-
store.id(),
245-
allocator.store_id,
246-
"attempted to use a `StructRefPre` with the wrong store"
247-
);
248243
assert!(
249244
!store.async_support(),
250245
"use `StructRef::new_async` with asynchronous stores"
@@ -296,11 +291,6 @@ impl StructRef {
296291
allocator: &StructRefPre,
297292
fields: &[Val],
298293
) -> Result<Rooted<StructRef>> {
299-
assert_eq!(
300-
store.id(),
301-
allocator.store_id,
302-
"attempted to use a `StructRefPre` with the wrong store"
303-
);
304294
assert!(
305295
store.async_support(),
306296
"use `StructRef::new` with synchronous stores"
@@ -313,6 +303,25 @@ impl StructRef {
313303
.await
314304
}
315305

306+
/// Like `Self::new` but caller's must ensure that if the store is
307+
/// configured for async, this is only ever called from on a fiber stack.
308+
pub(crate) unsafe fn new_maybe_async(
309+
store: &mut StoreOpaque,
310+
allocator: &StructRefPre,
311+
fields: &[Val],
312+
) -> Result<Rooted<StructRef>> {
313+
assert!(
314+
store.async_support(),
315+
"use `StructRef::new` with synchronous stores"
316+
);
317+
Self::type_check_fields(store, allocator, fields)?;
318+
unsafe {
319+
store.retry_after_gc_maybe_async((), |store, ()| {
320+
Self::new_unchecked(store, allocator, fields)
321+
})
322+
}
323+
}
324+
316325
/// Type check the field values before allocating a new struct.
317326
fn type_check_fields(
318327
store: &mut StoreOpaque,
@@ -346,6 +355,12 @@ impl StructRef {
346355
allocator: &StructRefPre,
347356
fields: &[Val],
348357
) -> Result<Rooted<StructRef>> {
358+
assert_eq!(
359+
store.id(),
360+
allocator.store_id,
361+
"attempted to use a `StructRefPre` with the wrong store"
362+
);
363+
349364
// Allocate the struct and write each field value into the appropriate
350365
// offset.
351366
let structref = store

crates/wasmtime/src/runtime/store.rs

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1543,6 +1543,64 @@ impl StoreOpaque {
15431543
}
15441544
}
15451545

1546+
/// Like `retry_after_gc` but must be called from a fiber stack if this is
1547+
/// an async store.
1548+
#[cfg(feature = "gc")]
1549+
pub(crate) unsafe fn retry_after_gc_maybe_async<T, U>(
1550+
&mut self,
1551+
value: T,
1552+
alloc_func: impl Fn(&mut Self, T) -> Result<U>,
1553+
) -> Result<U>
1554+
where
1555+
T: Send + Sync + 'static,
1556+
{
1557+
match alloc_func(self, value) {
1558+
Ok(x) => Ok(x),
1559+
Err(e) => match e.downcast::<crate::GcHeapOutOfMemory<T>>() {
1560+
Ok(oom) => {
1561+
let value = oom.into_inner();
1562+
self.maybe_async_gc(None)?;
1563+
alloc_func(self, value)
1564+
}
1565+
Err(e) => Err(e),
1566+
},
1567+
}
1568+
}
1569+
1570+
/// Like `gc` but must be called from a fiber stack if this is an async
1571+
/// store.
1572+
unsafe fn maybe_async_gc(&mut self, root: Option<VMGcRef>) -> Result<Option<VMGcRef>> {
1573+
let mut scope = crate::OpaqueRootScope::new(self);
1574+
let store_id = scope.id();
1575+
let root = root.map(|r| scope.gc_roots_mut().push_lifo_root(store_id, r));
1576+
1577+
if scope.async_support() {
1578+
#[cfg(feature = "async")]
1579+
unsafe {
1580+
let async_cx = scope.async_cx();
1581+
let future = scope.gc_async();
1582+
async_cx
1583+
.expect("attempted to pull async context during shutdown")
1584+
.block_on(future)?;
1585+
}
1586+
} else {
1587+
scope.gc();
1588+
}
1589+
1590+
let root = match root {
1591+
None => None,
1592+
Some(r) => {
1593+
let r = r
1594+
.get_gc_ref(&scope)
1595+
.expect("still in scope")
1596+
.unchecked_copy();
1597+
Some(scope.gc_store_mut()?.clone_gc_ref(&r))
1598+
}
1599+
};
1600+
1601+
Ok(root)
1602+
}
1603+
15461604
#[cfg(feature = "gc")]
15471605
pub fn gc(&mut self) {
15481606
// If the GC heap hasn't been initialized, there is nothing to collect.
@@ -2096,41 +2154,12 @@ unsafe impl<T> crate::runtime::vm::VMStore for StoreInner<T> {
20962154
}
20972155

20982156
#[cfg(feature = "gc")]
2099-
fn maybe_async_gc(&mut self, root: Option<VMGcRef>) -> Result<Option<VMGcRef>> {
2100-
let mut scope = crate::RootScope::new(self);
2101-
let store = scope.as_context_mut().0;
2102-
let store_id = store.id();
2103-
let root = root.map(|r| store.gc_roots_mut().push_lifo_root(store_id, r));
2104-
2105-
if store.async_support() {
2106-
#[cfg(feature = "async")]
2107-
unsafe {
2108-
let async_cx = store.async_cx();
2109-
let future = store.gc_async();
2110-
async_cx
2111-
.expect("attempted to pull async context during shutdown")
2112-
.block_on(future)?;
2113-
}
2114-
} else {
2115-
(**store).gc();
2116-
}
2117-
2118-
let root = match root {
2119-
None => None,
2120-
Some(r) => {
2121-
let r = r
2122-
.get_gc_ref(store)
2123-
.expect("still in scope")
2124-
.unchecked_copy();
2125-
Some(store.gc_store_mut()?.clone_gc_ref(&r))
2126-
}
2127-
};
2128-
2129-
Ok(root)
2157+
unsafe fn maybe_async_gc(&mut self, root: Option<VMGcRef>) -> Result<Option<VMGcRef>> {
2158+
(**self).maybe_async_gc(root)
21302159
}
21312160

21322161
#[cfg(not(feature = "gc"))]
2133-
fn maybe_async_gc(&mut self, root: Option<VMGcRef>) -> Result<Option<VMGcRef>> {
2162+
unsafe fn maybe_async_gc(&mut self, root: Option<VMGcRef>) -> Result<Option<VMGcRef>> {
21342163
Ok(root)
21352164
}
21362165

crates/wasmtime/src/runtime/vm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ pub unsafe trait VMStore {
200200
///
201201
/// If the async GC was cancelled, returns an error. This should be raised
202202
/// as a trap to clean up Wasm execution.
203-
fn maybe_async_gc(&mut self, root: Option<VMGcRef>) -> Result<Option<VMGcRef>>;
203+
unsafe fn maybe_async_gc(&mut self, root: Option<VMGcRef>) -> Result<Option<VMGcRef>>;
204204

205205
/// Metadata required for resources for the component model.
206206
#[cfg(feature = "component-model")]

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ impl<'a> ConstEvalContext<'a> {
7373
.collect::<Vec<_>>();
7474

7575
let allocator = StructRefPre::_new(store, struct_ty);
76-
let struct_ref = StructRef::_new(store, &allocator, &fields)?;
76+
let struct_ref = unsafe { StructRef::new_maybe_async(store, &allocator, &fields)? };
7777
let raw = struct_ref.to_anyref()._to_raw(store)?;
7878
Ok(ValRaw::anyref(raw))
7979
}
@@ -141,6 +141,9 @@ impl ConstExprEvaluator {
141141
/// global values of the expected types. This evaluator operates directly on
142142
/// untyped `ValRaw`s and does not and cannot check that its operands are of
143143
/// the correct type.
144+
///
145+
/// If given async store, then this must be called from on an async fiber
146+
/// stack.
144147
pub unsafe fn eval(
145148
&mut self,
146149
store: &mut StoreOpaque,
@@ -269,7 +272,7 @@ impl ConstExprEvaluator {
269272
let elem = Val::_from_raw(&mut store, self.pop()?, ty.element_type().unpack());
270273

271274
let pre = ArrayRefPre::_new(&mut store, ty);
272-
let array = ArrayRef::_new(&mut store, &pre, &elem, len)?;
275+
let array = unsafe { ArrayRef::new_maybe_async(&mut store, &pre, &elem, len)? };
273276

274277
self.stack
275278
.push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?));
@@ -288,7 +291,7 @@ impl ConstExprEvaluator {
288291
.expect("type should have a default value");
289292

290293
let pre = ArrayRefPre::_new(&mut store, ty);
291-
let array = ArrayRef::_new(&mut store, &pre, &elem, len)?;
294+
let array = unsafe { ArrayRef::new_maybe_async(&mut store, &pre, &elem, len)? };
292295

293296
self.stack
294297
.push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?));
@@ -323,7 +326,8 @@ impl ConstExprEvaluator {
323326
.collect::<SmallVec<[_; 8]>>();
324327

325328
let pre = ArrayRefPre::_new(&mut store, ty);
326-
let array = ArrayRef::_new_fixed(&mut store, &pre, &elems)?;
329+
let array =
330+
unsafe { ArrayRef::_new_fixed_maybe_async(&mut store, &pre, &elems)? };
327331

328332
self.stack
329333
.push(ValRaw::anyref(array.to_anyref()._to_raw(&mut store)?));

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

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,7 @@ unsafe fn array_new_elem(
772772
use crate::{
773773
store::AutoAssertNoGc,
774774
vm::const_expr::{ConstEvalContext, ConstExprEvaluator},
775-
ArrayRef, ArrayRefPre, ArrayType, Func, GcHeapOutOfMemory, RootSet, RootedGcRefImpl, Val,
775+
ArrayRef, ArrayRefPre, ArrayType, Func, RootSet, RootedGcRefImpl, Val,
776776
};
777777
use wasmtime_environ::{ModuleInternedTypeIndex, TableSegmentElements};
778778

@@ -827,16 +827,7 @@ unsafe fn array_new_elem(
827827
}
828828
}
829829

830-
let array = match ArrayRef::_new_fixed(store, &pre, &vals) {
831-
Ok(a) => a,
832-
Err(e) if e.is::<GcHeapOutOfMemory<()>>() => {
833-
// Collect garbage to hopefully free up space, then try the
834-
// allocation again.
835-
store.maybe_async_gc(None)?;
836-
ArrayRef::_new_fixed(store, &pre, &vals)?
837-
}
838-
Err(e) => return Err(e),
839-
};
830+
let array = unsafe { ArrayRef::_new_fixed_maybe_async(store, &pre, &vals)? };
840831

841832
let mut store = AutoAssertNoGc::new(store);
842833
let gc_ref = array.try_clone_gc_ref(&mut store)?;

crates/wast/src/core.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
use crate::WastContext;
12
use anyhow::{anyhow, bail, Context, Result};
23
use std::fmt::{Display, LowerHex};
34
use wasmtime::{AnyRef, ExternRef, Store, Val};
45
use wast::core::{AbstractHeapType, HeapType, NanPattern, V128Pattern, WastArgCore, WastRetCore};
56
use wast::token::{F32, F64};
67

78
/// Translate from a `script::Value` to a `RuntimeValue`.
8-
pub fn val<T>(store: &mut Store<T>, v: &WastArgCore<'_>) -> Result<Val> {
9+
pub fn val<T>(ctx: &mut WastContext<T>, v: &WastArgCore<'_>) -> Result<Val> {
910
use wast::core::WastArgCore::*;
1011

1112
Ok(match v {
@@ -30,10 +31,18 @@ pub fn val<T>(store: &mut Store<T>, v: &WastArgCore<'_>) -> Result<Val> {
3031
shared: false,
3132
ty: AbstractHeapType::None,
3233
}) => Val::AnyRef(None),
33-
RefExtern(x) => Val::ExternRef(Some(ExternRef::new(store, *x)?)),
34+
RefExtern(x) => Val::ExternRef(if let Some(rt) = ctx.async_runtime.as_ref() {
35+
Some(rt.block_on(ExternRef::new_async(&mut ctx.store, *x))?)
36+
} else {
37+
Some(ExternRef::new(&mut ctx.store, *x)?)
38+
}),
3439
RefHost(x) => {
35-
let x = ExternRef::new(&mut *store, *x)?;
36-
let x = AnyRef::convert_extern(&mut *store, x)?;
40+
let x = if let Some(rt) = ctx.async_runtime.as_ref() {
41+
rt.block_on(ExternRef::new_async(&mut ctx.store, *x))?
42+
} else {
43+
ExternRef::new(&mut ctx.store, *x)?
44+
};
45+
let x = AnyRef::convert_extern(&mut ctx.store, x)?;
3746
Val::AnyRef(Some(x))
3847
}
3948
other => bail!("couldn't convert {:?} to a runtime value", other),

0 commit comments

Comments
 (0)