Skip to content
This repository was archived by the owner on Mar 24, 2022. It is now read-only.

Commit 65e2e01

Browse files
committed
Refactor run_async to custom future implementation
1 parent 62ad436 commit 65e2e01

File tree

10 files changed

+559
-245
lines changed

10 files changed

+559
-245
lines changed

lucet-runtime/lucet-runtime-internals/src/future.rs

Lines changed: 296 additions & 183 deletions
Large diffs are not rendered by default.

lucet-runtime/lucet-runtime-internals/src/instance.rs

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ pub use crate::instance::execution::{KillError, KillState, KillSuccess, KillSwit
77
pub use crate::instance::signals::{signal_handler_none, SignalBehavior, SignalHandler};
88
pub use crate::instance::state::State;
99

10-
use crate::alloc::Alloc;
1110
use crate::context::Context;
1211
use crate::embed_ctx::CtxMap;
1312
use crate::error::Error;
@@ -18,13 +17,13 @@ use crate::region::RegionInternal;
1817
use crate::sysdeps::HOST_PAGE_SIZE_EXPECTED;
1918
use crate::val::{UntypedRetVal, Val};
2019
use crate::WASM_PAGE_SIZE;
20+
use crate::{alloc::Alloc, future::AsyncContext};
2121
use libc::{c_void, pthread_self, siginfo_t, uintptr_t};
2222
use lucet_module::InstanceRuntimeData;
2323
use memoffset::offset_of;
2424
use std::any::Any;
2525
use std::cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut, UnsafeCell};
2626
use std::convert::TryFrom;
27-
use std::marker::PhantomData;
2827
use std::mem;
2928
use std::ops::{Deref, DerefMut};
3029
use std::ptr::{self, NonNull};
@@ -228,6 +227,10 @@ pub struct Instance {
228227
/// Small mutexed state used for remote kill switch functionality
229228
pub(crate) kill_state: Arc<KillState>,
230229

230+
/// Indicates whether the instance is running in an async context (`Instance::run_async`)
231+
/// or not. Needed by `Vmctx::block_on`.
232+
pub(crate) async_ctx: Option<std::sync::Arc<AsyncContext>>,
233+
231234
#[cfg(feature = "concurrent_testpoints")]
232235
/// Conditionally-present helpers to force permutations of possible races in testing.
233236
pub lock_testpoints: Arc<LockTestpoints>,
@@ -515,7 +518,7 @@ impl Instance {
515518
/// in the future.
516519
pub fn run(&mut self, entrypoint: &str, args: &[Val]) -> Result<RunResult, Error> {
517520
let func = self.module.get_export_func(entrypoint)?;
518-
Ok(self.run_func(func, &args, false, None)?.unwrap())
521+
Ok(self.run_func(func, &args, None, None)?.unwrap())
519522
}
520523

521524
/// Run a function with arguments in the guest context from the [WebAssembly function
@@ -531,7 +534,7 @@ impl Instance {
531534
args: &[Val],
532535
) -> Result<RunResult, Error> {
533536
let func = self.module.get_func_from_idx(table_idx, func_idx)?;
534-
Ok(self.run_func(func, &args, false, None)?.unwrap())
537+
Ok(self.run_func(func, &args, None, None)?.unwrap())
535538
}
536539

537540
/// Resume execution of an instance that has yielded without providing a value to the guest.
@@ -562,19 +565,21 @@ impl Instance {
562565
/// The foreign code safety caveat of [`Instance::run()`](struct.Instance.html#method.run)
563566
/// applies.
564567
pub fn resume_with_val<A: Any + 'static>(&mut self, val: A) -> Result<RunResult, Error> {
565-
Ok(self.resume_with_val_impl(val, false, None)?.unwrap())
568+
Ok(self
569+
.resume_with_val_impl(Box::new(val), None, None)?
570+
.unwrap())
566571
}
567572

568-
pub(crate) fn resume_with_val_impl<A: Any + 'static>(
573+
pub(crate) fn resume_with_val_impl(
569574
&mut self,
570-
val: A,
571-
async_context: bool,
575+
val: Box<dyn Any + 'static>,
576+
async_context: Option<AsyncContext>,
572577
max_insn_count: Option<u64>,
573578
) -> Result<InternalRunResult, Error> {
574579
match &self.state {
575580
State::Yielded { expecting, .. } => {
576581
// make sure the resumed value is of the right type
577-
if !expecting.is::<PhantomData<A>>() {
582+
if &(*val).type_id() != expecting {
578583
return Err(Error::InvalidArgument(
579584
"type mismatch between yielded instance expected value and resumed value",
580585
));
@@ -583,7 +588,7 @@ impl Instance {
583588
_ => return Err(Error::InvalidArgument("can only resume a yielded instance")),
584589
}
585590

586-
self.resumed_val = Some(Box::new(val) as Box<dyn Any + 'static>);
591+
self.resumed_val = Some(val);
587592

588593
self.set_instruction_bound_delta(max_insn_count);
589594
self.swap_and_return(async_context)
@@ -602,6 +607,7 @@ impl Instance {
602607
/// applies.
603608
pub(crate) fn resume_bounded(
604609
&mut self,
610+
async_context: AsyncContext,
605611
max_insn_count: u64,
606612
) -> Result<InternalRunResult, Error> {
607613
if !self.state.is_bound_expired() {
@@ -610,7 +616,7 @@ impl Instance {
610616
));
611617
}
612618
self.set_instruction_bound_delta(Some(max_insn_count));
613-
self.swap_and_return(true)
619+
self.swap_and_return(Some(async_context))
614620
}
615621

616622
/// Run the module's [start function][start], if one exists.
@@ -648,7 +654,7 @@ impl Instance {
648654
if !self.is_not_started() {
649655
return Err(Error::StartAlreadyRun);
650656
}
651-
self.run_func(start, &[], false, None)?;
657+
self.run_func(start, &[], None, None)?;
652658
}
653659
Ok(())
654660
}
@@ -1021,6 +1027,7 @@ impl Instance {
10211027
entrypoint: None,
10221028
resumed_val: None,
10231029
terminate_on_heap_oom: false,
1030+
async_ctx: None,
10241031
_padding: (),
10251032
};
10261033
inst.set_globals_ptr(globals_ptr);
@@ -1090,7 +1097,7 @@ impl Instance {
10901097
&mut self,
10911098
func: FunctionHandle,
10921099
args: &[Val],
1093-
async_context: bool,
1100+
async_context: Option<AsyncContext>,
10941101
inst_count_bound: Option<u64>,
10951102
) -> Result<InternalRunResult, Error> {
10961103
let needs_start = self.state.is_not_started() && !func.is_start_func;
@@ -1191,7 +1198,10 @@ impl Instance {
11911198
/// This must only be called for an instance in a ready, non-fatally faulted, or yielded state,
11921199
/// or in the not-started state on the start function. The public wrappers around this function
11931200
/// should make sure the state is appropriate.
1194-
fn swap_and_return(&mut self, async_context: bool) -> Result<InternalRunResult, Error> {
1201+
fn swap_and_return<'a>(
1202+
&mut self,
1203+
async_context: Option<AsyncContext>,
1204+
) -> Result<InternalRunResult, Error> {
11951205
let is_start_func = self
11961206
.entrypoint
11971207
.expect("we always have an entrypoint by now")
@@ -1203,7 +1213,10 @@ impl Instance {
12031213
|| self.state.is_yielded()
12041214
|| self.state.is_bound_expired()
12051215
);
1206-
self.state = State::Running { async_context };
1216+
1217+
self.async_ctx = async_context.map(|cx| Arc::new(cx));
1218+
1219+
self.state = State::Running;
12071220

12081221
let res = self.with_current_instance(|i| {
12091222
i.with_signals_on(|i| {
@@ -1217,6 +1230,9 @@ impl Instance {
12171230
})
12181231
});
12191232

1233+
// remove async ctx
1234+
self.async_ctx.take();
1235+
12201236
#[cfg(feature = "concurrent_testpoints")]
12211237
self.lock_testpoints
12221238
.instance_after_clearing_current_instance

lucet-runtime/lucet-runtime-internals/src/instance/state.rs

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::instance::siginfo_ext::SiginfoExt;
22
use crate::instance::{FaultDetails, TerminationDetails, YieldedVal};
33
use crate::sysdeps::UContext;
44
use libc::{SIGBUS, SIGSEGV};
5-
use std::any::Any;
5+
use std::any::TypeId;
66
use std::ffi::{CStr, CString};
77

88
/// The representation of a Lucet instance's state machine.
@@ -22,11 +22,7 @@ pub enum State {
2222
/// Transitions to `Ready` when the guest function returns normally, or to `Faulted`,
2323
/// `Terminating`, or `Yielding` if the instance faults, terminates, or yields, or to
2424
/// `BoundExpired` if the instance is run with an instruction bound and reaches it.
25-
Running {
26-
/// Indicates whether the instance is running in an async context (`Instance::run_async`)
27-
/// or not. Needed by `Vmctx::block_on`.
28-
async_context: bool,
29-
},
25+
Running,
3026

3127
/// The instance has faulted, potentially fatally.
3228
///
@@ -56,11 +52,8 @@ pub enum State {
5652
/// `RunResult` before anything else happens to the instance.
5753
Yielding {
5854
val: YieldedVal,
59-
/// A phantom value carrying the type of the expected resumption value.
60-
///
61-
/// Concretely, this should only ever be `Box<PhantomData<R>>` where `R` is the type
62-
/// the guest expects upon resumption.
63-
expecting: Box<dyn Any>,
55+
/// The type of the expected resumption value
56+
expecting: TypeId,
6457
},
6558

6659
/// The instance has yielded.
@@ -69,10 +62,7 @@ pub enum State {
6962
/// instance is reset.
7063
Yielded {
7164
/// A phantom value carrying the type of the expected resumption value.
72-
///
73-
/// Concretely, this should only ever be `Box<PhantomData<R>>` where `R` is the type
74-
/// the guest expects upon resumption.
75-
expecting: Box<dyn Any>,
65+
expecting: TypeId,
7666
},
7767

7868
/// The instance has reached an instruction-count bound.
@@ -96,12 +86,7 @@ impl std::fmt::Display for State {
9686
match self {
9787
State::NotStarted => write!(f, "not started"),
9888
State::Ready => write!(f, "ready"),
99-
State::Running {
100-
async_context: false,
101-
} => write!(f, "running"),
102-
State::Running {
103-
async_context: true,
104-
} => write!(f, "running (in async context)"),
89+
State::Running => write!(f, "running"),
10590
State::Faulted {
10691
details, siginfo, ..
10792
} => {
@@ -162,14 +147,6 @@ impl State {
162147
}
163148
}
164149

165-
pub fn is_running_async(&self) -> bool {
166-
if let State::Running { async_context } = self {
167-
*async_context
168-
} else {
169-
false
170-
}
171-
}
172-
173150
pub fn is_faulted(&self) -> bool {
174151
if let State::Faulted { .. } = self {
175152
true

lucet-runtime/lucet-runtime-internals/src/vmctx.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@ use crate::instance::{
1313
CURRENT_INSTANCE, HOST_CTX,
1414
};
1515
use lucet_module::{FunctionHandle, GlobalValue};
16-
use std::any::Any;
16+
use std::any::{Any, TypeId};
1717
use std::borrow::{Borrow, BorrowMut};
1818
use std::cell::{Ref, RefCell, RefMut};
19-
use std::marker::PhantomData;
2019

2120
/// An opaque handle to a running instance's context.
2221
#[derive(Debug)]
@@ -436,10 +435,9 @@ impl Vmctx {
436435
if is_bound_expiration {
437436
inst.state = State::BoundExpired;
438437
} else {
439-
let expecting: Box<PhantomData<R>> = Box::new(PhantomData);
440438
inst.state = State::Yielding {
441439
val: YieldedVal::new(val),
442-
expecting: expecting as Box<dyn Any>,
440+
expecting: TypeId::of::<R>(),
443441
};
444442
}
445443

lucet-runtime/lucet-runtime-macros/src/lib.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,17 @@ pub fn lucet_hostcall(_attr: TokenStream, item: TokenStream) -> TokenStream {
101101
quote! { lucet_runtime::TerminationDetails }
102102
};
103103

104+
let res_ident = quote::format_ident!("res");
105+
106+
let block_if_async = match raw_sig.asyncness.take() {
107+
Some(_) => {
108+
quote! { let #res_ident = vmctx.block_on(#res_ident); }
109+
}
110+
None => {
111+
quote! {}
112+
}
113+
};
114+
104115
let raw_hostcall = quote! {
105116
#(#attrs)*
106117
#vis
@@ -111,7 +122,13 @@ pub fn lucet_hostcall(_attr: TokenStream, item: TokenStream) -> TokenStream {
111122
let vmctx = #vmctx_mod::Vmctx::from_raw(vmctx_raw);
112123
#vmctx_mod::VmctxInternal::instance_mut(&vmctx).uninterruptable(|| {
113124
let res = std::panic::catch_unwind(move || {
114-
#hostcall_ident(&#vmctx_mod::Vmctx::from_raw(vmctx_raw), #(#impl_args),*)
125+
let vmctx = #vmctx_mod::Vmctx::from_raw(vmctx_raw);
126+
127+
let #res_ident = #hostcall_ident(&vmctx, #(#impl_args),*);
128+
129+
#block_if_async
130+
131+
#res_ident
115132
});
116133
match res {
117134
Ok(res) => res,
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"env": {
3-
"hostcall_containing_block_on": "hostcall_containing_block_on"
3+
"hostcall_containing_block_on": "hostcall_containing_block_on",
4+
"hostcall_containing_yielding_block_on": "hostcall_containing_yielding_block_on",
5+
"hostcall_async_containing_yielding_block_on": "hostcall_async_containing_yielding_block_on",
6+
"await_manual_future": "await_manual_future"
47
}
58
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
11
#include <stddef.h>
22

33
extern void hostcall_containing_block_on(int);
4+
extern void hostcall_containing_yielding_block_on(int);
5+
extern int hostcall_async_containing_yielding_block_on(int, int);
46

57
int main(void)
68
{
79
hostcall_containing_block_on(1312);
810
return 0;
911
}
12+
13+
int yielding()
14+
{
15+
hostcall_containing_yielding_block_on(0);
16+
hostcall_containing_yielding_block_on(1);
17+
hostcall_containing_yielding_block_on(2);
18+
hostcall_containing_yielding_block_on(3);
19+
20+
int six = hostcall_async_containing_yielding_block_on(3, 6);
21+
hostcall_async_containing_yielding_block_on(3, six);
22+
23+
return 0;
24+
}
25+
26+
int manual_future()
27+
{
28+
await_manual_future();
29+
return 0;
30+
}

0 commit comments

Comments
 (0)