mirror of
https://github.com/HeyPuter/gex.git
synced 2026-11-13 20:12:43 -10:00
libprov: a provider's guest calls keep its frames in the page, whatever the tier
Under `?fjit=1` the provider's guest call reached a function JIT body straight from the table, with no fallback call-out to say the provider's wasm frames were below it: a blocking syscall there (clock_nanosleep) went back to the run loop, and the guest later returned into the provider's code page (SIGSEGV; mp11, mp13). gexp_call_guest now tells the host (`gex.provider_guest_call(+1/-1)`, the shim's export bound straight in the page, a no-op natively); `provider_callout_open` counts those, and `provider_try` restores the count when a raise unwinds guest calls. tools/unity-dotnet/mono/build.sh takes PROVIDER_DIR. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_d2453757b8a84fdbb4ee7c9e093141df
This commit is contained in:
1 parent
13f1fb3a10
commit
9bbb85ee9c
6 files changed
+46
-2
No files matched your search
@@ -2791,10 +2791,13 @@ export class Gex {
|
||||
const tag = this.#providerTag();
|
||||
return {
|
||||
provider_try: (f, arg) => {
|
||||
// The guest calls a raise unwinds are no longer open.
|
||||
const calls = e.gex_libprov_guest_calls();
|
||||
try {
|
||||
ftab.get(f)(arg);
|
||||
return 0;
|
||||
} catch (err) {
|
||||
e.gex_libprov_guest_calls_set(calls);
|
||||
if (err instanceof WebAssembly.Exception && err.is(tag)) return err.getArg(tag, 0);
|
||||
throw err;
|
||||
}
|
||||
@@ -2802,6 +2805,8 @@ export class Gex {
|
||||
provider_throw: (code) => {
|
||||
throw new WebAssembly.Exception(tag, [code]);
|
||||
},
|
||||
// The shim's export itself: a wasm-to-wasm call on every guest call.
|
||||
provider_guest_call: e.gex_libprov_guest_call,
|
||||
provider_missing: (addr) => e.gex_libprov_missing(addr),
|
||||
provider_thread_entry: () => e.gex_libprov_thread_entry(),
|
||||
provider_code_page: () => e.gex_libprov_thread_entry(),
|
||||
|
||||
@@ -1430,7 +1430,7 @@ thread_local! {
|
||||
/// code running now has the provider's wasm frames below it, which a trap to
|
||||
/// JS's run loop would abandon.
|
||||
pub(crate) fn provider_callout_open() -> bool {
|
||||
CALLOUTS.with(|c| c.borrow().iter().any(|&(r, _, _)| libprov::provider_return(r)))
|
||||
libprov::guest_call_open() || CALLOUTS.with(|c| c.borrow().iter().any(|&(r, _, _)| libprov::provider_return(r)))
|
||||
}
|
||||
|
||||
/// [`LOOKUP_RING`], for a diagnostic.
|
||||
|
||||
@@ -562,6 +562,36 @@ pub extern "C" fn gex_libprov_thread_register(in_guest: u64, stop: u64) {
|
||||
}
|
||||
|
||||
/// `gex.provider_park()`.
|
||||
thread_local! {
|
||||
/// The provider's guest calls open on this Worker (`gexp_call_guest`):
|
||||
/// while any is, the guest code running has the provider's wasm frames
|
||||
/// below it, whichever tier runs it (a function JIT or unit function is
|
||||
/// called straight from the provider, with no call-out of the fallback's).
|
||||
static GUEST_CALLS: std::cell::Cell<i32> = const { std::cell::Cell::new(0) };
|
||||
}
|
||||
|
||||
/// `gex.provider_guest_call(delta)`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn gex_libprov_guest_call(delta: i32) {
|
||||
GUEST_CALLS.with(|c| c.set(c.get() + delta));
|
||||
}
|
||||
|
||||
/// The count, which `provider_try` restores when a raise unwinds guest calls.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn gex_libprov_guest_calls() -> i32 {
|
||||
GUEST_CALLS.with(|c| c.get())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn gex_libprov_guest_calls_set(v: i32) {
|
||||
GUEST_CALLS.with(|c| c.set(v));
|
||||
}
|
||||
|
||||
/// Whether a provider's guest call is open on this Worker.
|
||||
pub(crate) fn guest_call_open() -> bool {
|
||||
GUEST_CALLS.with(|c| c.get()) > 0
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn gex_libprov_park() {
|
||||
park_if_stopping(this_tid(), true);
|
||||
|
||||
@@ -632,6 +632,9 @@ fn register64(linker: &mut Linker<HostState>) -> Result<()> {
|
||||
linker.func_wrap(IMPORT_MODULE, "provider_thread_register", |caller: Caller<'_, HostState>, in_guest: i64, stop: i64| {
|
||||
guest_stop().register(caller.data().thread.tid as u32, in_guest as u64, stop as u64);
|
||||
})?;
|
||||
// A provider's guest call begins or ends: natively a blocking call waits
|
||||
// in place anyway (the browser keeps the count: cgen/libprov.rs).
|
||||
linker.func_wrap(IMPORT_MODULE, "provider_guest_call", |_caller: Caller<'_, HostState>, _delta: i32| {})?;
|
||||
linker.func_wrap(IMPORT_MODULE, "provider_park", |caller: Caller<'_, HostState>| {
|
||||
let mem = caller.data().memory.clone();
|
||||
if let Some(m) = mem {
|
||||
|
||||
@@ -104,7 +104,7 @@ step_corelib() {
|
||||
|
||||
step_provider() {
|
||||
. "$UD/sysroot/flags.sh"
|
||||
local P="$OUT/src/src/native/public" N="$OUT/src/src/native" L="$OUT/obj/mono" W="$OUT/provider"
|
||||
local P="$OUT/src/src/native/public" N="$OUT/src/src/native" L="$OUT/obj/mono" W="${PROVIDER_DIR:-$OUT/provider}"
|
||||
mkdir -p "$W"; cd "$W"
|
||||
local INC="-nostdinc -isystem $SR/include -isystem $(clang -print-resource-dir)/include -I$P -I$OUT/src/src/mono -I$UD/provider -I$HERE/host -I$OUT/obj -I$OUT/src/src/mono/mono/eglib -I$OUT/obj/mono/eglib -I$OUT/src/src/native"
|
||||
llvm-nm --defined-only -j "$L"/mini/libmonosgen-2.0.a "$L"/mini/libmono-ee-interp.a "$L"/mini/libmono-component-*-stub-static.a \
|
||||
|
||||
@@ -14,6 +14,10 @@ static __thread volatile int gexp_in_guest;
|
||||
static volatile int gexp_stop;
|
||||
__attribute__((import_module("gex"), import_name("provider_thread_register"))) void gexp_host_thread_register(volatile int *in_guest, volatile int *stop);
|
||||
__attribute__((import_module("gex"), import_name("provider_park"))) void gexp_host_park(void);
|
||||
/* A guest call by the provider begins (+1) or ends (-1) on this thread: in the
|
||||
* browser, guest code with the provider's frames below it must not leave them
|
||||
* for the run loop (a blocking syscall waits in place), whatever tier runs it. */
|
||||
__attribute__((import_module("gex"), import_name("provider_guest_call"))) void gexp_host_guest_call(int delta);
|
||||
__attribute__((import_module("gex"), import_name("provider_stop_guest"))) void gexp_host_stop_guest(void);
|
||||
__attribute__((import_module("gex"), import_name("provider_start_guest"))) void gexp_host_start_guest(void);
|
||||
__attribute__((import_module("gex"), import_name("provider_guest_stack_end"))) uint64_t gexp_host_guest_stack_end(uint64_t sp);
|
||||
@@ -137,6 +141,7 @@ void gexp_call_guest(uint64_t fn, gexp_call *c)
|
||||
*(volatile uint64_t *)((uintptr_t)(uint32_t)ctx + GEXP_CTX_V + 16 * i) = c->xmm[i];
|
||||
GEXP_X(ctx, 4) = sp;
|
||||
void *cookie = gexp_hook_call_guest();
|
||||
gexp_host_guest_call(1);
|
||||
in_guest(1);
|
||||
/* Run the guest until it returns to `ra`. A function body returns 1 when
|
||||
* its return went to the address its caller predicted; otherwise 0, with
|
||||
@@ -158,6 +163,7 @@ void gexp_call_guest(uint64_t fn, gexp_call *c)
|
||||
gexp_depth = depth;
|
||||
__gex_set_ctx((uint32_t)ctx);
|
||||
in_guest(0);
|
||||
gexp_host_guest_call(-1);
|
||||
gexp_hook_call_return(cookie);
|
||||
GEXP_X(ctx, 4) = rsp0;
|
||||
c->rax = GEXP_X(ctx, 0);
|
||||
|
||||
Reference in new issue
Block a user