diff --git a/crates/perry-runtime/src/array/iterator.rs b/crates/perry-runtime/src/array/iterator.rs index face986294..14ccb0147f 100644 --- a/crates/perry-runtime/src/array/iterator.rs +++ b/crates/perry-runtime/src/array/iterator.rs @@ -268,12 +268,26 @@ fn undefined_value() -> f64 { } fn async_from_sync_iter_result(value: f64, done: bool) -> f64 { + // The result value and the freshly-allocated object are live young objects + // held across sibling allocations (the object alloc, and each + // `set_field_by_name`, which can grow the shape). The default-on moving + // scavenge evacuates young survivors, so cache them in a handle scope and + // re-read through the (GC-updated) handles instead of stale raw copies. + // Property-name keys use the long-lived allocator so they never move and + // need no rooting. + let scope = crate::gc::RuntimeHandleScope::new(); + let value_h = scope.root_nanbox_f64(value); let obj = crate::object::js_object_alloc(0, 2); - let value_key = crate::string::js_string_from_bytes(b"value".as_ptr(), 5); - let done_key = crate::string::js_string_from_bytes(b"done".as_ptr(), 4); - crate::object::js_object_set_field_by_name(obj, value_key, value); + let obj_h = scope.root_raw_mut_ptr(obj); + let value_key = crate::string::js_string_from_bytes_longlived(b"value".as_ptr(), 5); + let done_key = crate::string::js_string_from_bytes_longlived(b"done".as_ptr(), 4); + crate::object::js_object_set_field_by_name( + obj_h.get_raw_mut_ptr::(), + value_key, + value_h.get_nanbox_f64(), + ); crate::object::js_object_set_field_by_name( - obj, + obj_h.get_raw_mut_ptr::(), done_key, if done { f64::from_bits(crate::value::TAG_TRUE) @@ -281,18 +295,26 @@ fn async_from_sync_iter_result(value: f64, done: bool) -> f64 { f64::from_bits(crate::value::TAG_FALSE) }, ); - crate::value::js_nanbox_pointer(obj as i64) + crate::value::js_nanbox_pointer(obj_h.get_raw_mut_ptr::() as i64) } extern "C" fn async_from_sync_fulfilled( closure: *const crate::closure::ClosureHeader, value: f64, ) -> f64 { + // `async_from_sync_iter_result` allocates and can move the nursery `outer` + // promise stored in capture slot 0 — and the closure itself. Root the + // closure, build the result FIRST, then re-read the (GC-updated) capture so + // we resolve through the live promise pointer, not a pre-move copy. + let scope = crate::gc::RuntimeHandleScope::new(); + let closure_h = scope.root_raw_const_ptr(closure); + let done = crate::closure::js_closure_get_capture_f64(closure, 1) != 0.0; + let result = async_from_sync_iter_result(value, done); + let closure = closure_h.get_raw_const_ptr::(); let promise = crate::closure::js_closure_get_capture_ptr(closure, 0) as *mut crate::promise::Promise; - let done = crate::closure::js_closure_get_capture_f64(closure, 1) != 0.0; if !promise.is_null() { - crate::promise::js_promise_resolve(promise, async_from_sync_iter_result(value, done)); + crate::promise::js_promise_resolve(promise, result); } 0.0 } @@ -301,15 +323,24 @@ extern "C" fn async_from_sync_rejected_value( closure: *const crate::closure::ClosureHeader, reason: f64, ) -> f64 { - let promise = - crate::closure::js_closure_get_capture_ptr(closure, 0) as *mut crate::promise::Promise; + // `async_from_sync_close` calls back into JS and allocates, which can move + // the nursery `outer` promise in capture slot 0 and the closure itself. + // Root the closure + reason and re-read the capture after the close so the + // rejection targets the live promise pointer. + let scope = crate::gc::RuntimeHandleScope::new(); + let closure_h = scope.root_raw_const_ptr(closure); + let reason_h = scope.root_nanbox_f64(reason); let iter = crate::closure::js_closure_get_capture_f64(closure, 1); + let iter_h = scope.root_nanbox_f64(iter); let close_on_rejection = crate::closure::js_closure_get_capture_f64(closure, 2) != 0.0; if close_on_rejection { - async_from_sync_close(iter); + async_from_sync_close(iter_h.get_nanbox_f64()); } + let closure = closure_h.get_raw_const_ptr::(); + let promise = + crate::closure::js_closure_get_capture_ptr(closure, 0) as *mut crate::promise::Promise; if !promise.is_null() { - crate::promise::js_promise_reject(promise, reason); + crate::promise::js_promise_reject(promise, reason_h.get_nanbox_f64()); } 0.0 } @@ -321,45 +352,78 @@ fn async_from_sync_continue(iter: f64, step_result: f64, close_on_rejection: boo return async_from_sync_rejected(b"Iterator result is not an object"); } - let result_obj = ptr as *const crate::object::ObjectHeader; - let done_key = crate::string::js_string_from_bytes(b"done".as_ptr(), 4); - let value_key = crate::string::js_string_from_bytes(b"value".as_ptr(), 5); + // Everything below allocates repeatedly (closure allocs, the resolved + // value promise, `js_promise_then`), and the default-on moving scavenge + // evacuates young survivors on any of those safepoints. Cache every live + // young value (the iterator, the step-result object, the extracted value, + // the freshly-built `outer` promise and its two reaction closures) in a + // handle scope and re-read each through its handle right before use, so no + // raw pre-move pointer survives across an allocation. Property-name keys use + // the long-lived allocator so they never move. + let scope = crate::gc::RuntimeHandleScope::new(); + let iter_h = scope.root_nanbox_f64(iter); + let step_h = scope.root_nanbox_f64(step_result); + let done_key = crate::string::js_string_from_bytes_longlived(b"done".as_ptr(), 4); + let value_key = crate::string::js_string_from_bytes_longlived(b"value".as_ptr(), 5); let done = { + let result_obj = crate::value::js_nanbox_get_pointer(step_h.get_nanbox_f64()) + as *const crate::object::ObjectHeader; let done_val = crate::object::js_object_get_field_by_name(result_obj, done_key); let done_f64 = f64::from_bits(done_val.bits()); crate::value::js_is_truthy(done_f64) != 0 }; let value = { + let result_obj = crate::value::js_nanbox_get_pointer(step_h.get_nanbox_f64()) + as *const crate::object::ObjectHeader; let value_val = crate::object::js_object_get_field_by_name(result_obj, value_key); f64::from_bits(value_val.bits()) }; + let value_h = scope.root_nanbox_f64(value); let outer = crate::promise::js_promise_new(); + let outer_h = scope.root_raw_mut_ptr(outer); let on_fulfilled = crate::closure::js_closure_alloc(async_from_sync_fulfilled as *const u8, 2); + let on_fulfilled_h = scope.root_raw_mut_ptr(on_fulfilled); let on_rejected = crate::closure::js_closure_alloc(async_from_sync_rejected_value as *const u8, 3); - crate::closure::js_closure_set_capture_ptr(on_fulfilled, 0, outer as i64); - crate::closure::js_closure_set_capture_f64(on_fulfilled, 1, if done { 1.0 } else { 0.0 }); - crate::closure::js_closure_set_capture_ptr(on_rejected, 0, outer as i64); - crate::closure::js_closure_set_capture_f64(on_rejected, 1, iter); - crate::closure::js_closure_set_capture_f64( - on_rejected, - 2, - if close_on_rejection { 1.0 } else { 0.0 }, - ); + let on_rejected_h = scope.root_raw_mut_ptr(on_rejected); + // All three allocations are done; re-read each through its handle before + // wiring captures (no allocation happens between these stores). + { + let outer = outer_h.get_raw_mut_ptr::(); + let on_fulfilled = on_fulfilled_h.get_raw_mut_ptr::(); + let on_rejected = on_rejected_h.get_raw_mut_ptr::(); + crate::closure::js_closure_set_capture_ptr(on_fulfilled, 0, outer as i64); + crate::closure::js_closure_set_capture_f64(on_fulfilled, 1, if done { 1.0 } else { 0.0 }); + crate::closure::js_closure_set_capture_ptr(on_rejected, 0, outer as i64); + crate::closure::js_closure_set_capture_f64(on_rejected, 1, iter_h.get_nanbox_f64()); + crate::closure::js_closure_set_capture_f64( + on_rejected, + 2, + if close_on_rejection { 1.0 } else { 0.0 }, + ); + } - let value_promise = match crate::promise::js_promise_resolved_catching(value) { + let value_promise = match crate::promise::js_promise_resolved_catching(value_h.get_nanbox_f64()) + { Ok(promise) => promise, Err(reason) => { + let reason_h = scope.root_nanbox_f64(reason); if close_on_rejection { - async_from_sync_close(iter); + async_from_sync_close(iter_h.get_nanbox_f64()); } - crate::promise::js_promise_reject(outer, reason); - return boxed_promise_value(outer); + let outer = outer_h.get_raw_mut_ptr::(); + crate::promise::js_promise_reject(outer, reason_h.get_nanbox_f64()); + return boxed_promise_value(outer_h.get_raw_mut_ptr::()); } }; - crate::promise::js_promise_then(value_promise, on_fulfilled, on_rejected); - boxed_promise_value(outer) + let value_promise_h = scope.root_raw_mut_ptr(value_promise); + crate::promise::js_promise_then( + value_promise_h.get_raw_mut_ptr::(), + on_fulfilled_h.get_raw_mut_ptr::(), + on_rejected_h.get_raw_mut_ptr::(), + ); + boxed_promise_value(outer_h.get_raw_mut_ptr::()) } fn async_from_sync_rest_args(rest: f64) -> (usize, f64) { @@ -377,7 +441,18 @@ fn async_from_sync_rest_args(rest: f64) -> (usize, f64) { } fn async_from_sync_call_raw(iter: f64, method: &[u8], args: &[f64]) -> Result, f64> { - let method_value = named_field(iter, method); + // `named_field` allocates the method-name key and the invoked method runs + // arbitrary JS — both are moving-scavenge safepoints that evacuate the young + // sync iterator. Root `iter` and the fetched method value and re-read them + // through handles so no stale copy is dereferenced (e.g. `named_field` / + // `js_native_call_method` doing a property access on a moved `iter`, which + // is where `shape_is_url_search_params` faulted on a poison receiver). + let scope = crate::gc::RuntimeHandleScope::new(); + let iter_h = scope.root_nanbox_f64(iter); + let method_value = named_field(iter_h.get_nanbox_f64(), method); + let method_value_h = scope.root_nanbox_f64(method_value); + let iter = iter_h.get_nanbox_f64(); + let method_value = method_value_h.get_nanbox_f64(); // Spec `%AsyncFromSyncIteratorPrototype%.{return,throw}` (and the sync // `yield *` close) do `GetMethod(syncIterator, name)` ONCE and then // `Call(method, syncIterator, args)` on that captured value. Re-dispatching @@ -420,11 +495,17 @@ fn async_from_sync_call_raw(iter: f64, method: &[u8], args: &[f64]) -> Result f64 { - match async_from_sync_call_raw(iter, method, args) { - Ok(Some(step)) => async_from_sync_continue(iter, step, close_on_rejection), + // `async_from_sync_call_raw` runs JS (a moving-scavenge safepoint); re-read + // `iter` from a handle before handing it to `async_from_sync_continue`. + let scope = crate::gc::RuntimeHandleScope::new(); + let iter_h = scope.root_nanbox_f64(iter); + match async_from_sync_call_raw(iter_h.get_nanbox_f64(), method, args) { + Ok(Some(step)) => { + async_from_sync_continue(iter_h.get_nanbox_f64(), step, close_on_rejection) + } Ok(None) => async_from_sync_rejected(b"Async-from-sync iterator method is not callable"), Err(reason) => boxed_promise_value(crate::promise::js_promise_rejected(reason)), } @@ -500,8 +587,16 @@ extern "C" fn async_from_sync_next( closure: *const crate::closure::ClosureHeader, rest: f64, ) -> f64 { + // Root the captured sync iterator + its `[[NextMethod]]` across the sync + // `next()` call (which runs JS and triggers the moving scavenge). Passing + // the pre-move raw `iter` on to `async_from_sync_continue` / + // `async_from_sync_call` was the layer-2 bug: a later `named_field(iter,…)` + // property access dereferenced a poison (freed/moved) receiver. + let scope = crate::gc::RuntimeHandleScope::new(); let iter = crate::closure::js_closure_get_capture_f64(closure, 0); + let iter_h = scope.root_nanbox_f64(iter); let cached_next = crate::closure::js_closure_get_capture_f64(closure, 1); + let cached_next_h = scope.root_nanbox_f64(cached_next); let (argc, first) = async_from_sync_rest_args(rest); let single = [first]; let args: &[f64] = if argc == 0 { &[] } else { &single }; @@ -509,28 +604,39 @@ extern "C" fn async_from_sync_next( // observable-getter case). Builtin iterators (array/map/set/string) expose // no readable own `next` and dispatch through the class-id method tower, so // fall back to the by-name call for them. - if is_callable_value(cached_next) { - return match async_from_sync_call_cached_raw(iter, cached_next, args) { - Ok(Some(step)) => async_from_sync_continue(iter, step, true), - Ok(None) => async_from_sync_call(iter, b"next", args, true), + if is_callable_value(cached_next_h.get_nanbox_f64()) { + return match async_from_sync_call_cached_raw( + iter_h.get_nanbox_f64(), + cached_next_h.get_nanbox_f64(), + args, + ) { + Ok(Some(step)) => async_from_sync_continue(iter_h.get_nanbox_f64(), step, true), + Ok(None) => async_from_sync_call(iter_h.get_nanbox_f64(), b"next", args, true), Err(reason) => boxed_promise_value(crate::promise::js_promise_rejected(reason)), }; } - async_from_sync_call(iter, b"next", args, true) + async_from_sync_call(iter_h.get_nanbox_f64(), b"next", args, true) } extern "C" fn async_from_sync_return( closure: *const crate::closure::ClosureHeader, rest: f64, ) -> f64 { + let scope = crate::gc::RuntimeHandleScope::new(); let iter = crate::closure::js_closure_get_capture_f64(closure, 0); + let iter_h = scope.root_nanbox_f64(iter); let (argc, first) = async_from_sync_rest_args(rest); + let first_h = scope.root_nanbox_f64(first); let single = [first]; let args: &[f64] = if argc == 0 { &[] } else { &single }; - match async_from_sync_call_raw(iter, b"return", args) { - Ok(Some(step)) => async_from_sync_continue(iter, step, false), + match async_from_sync_call_raw(iter_h.get_nanbox_f64(), b"return", args) { + Ok(Some(step)) => async_from_sync_continue(iter_h.get_nanbox_f64(), step, false), Ok(None) => { - let value = if argc == 0 { undefined_value() } else { first }; + let value = if argc == 0 { + undefined_value() + } else { + first_h.get_nanbox_f64() + }; let done = async_from_sync_iter_result(value, true); boxed_promise_value(crate::promise::js_promise_resolved(done)) } @@ -542,14 +648,16 @@ extern "C" fn async_from_sync_throw( closure: *const crate::closure::ClosureHeader, rest: f64, ) -> f64 { + let scope = crate::gc::RuntimeHandleScope::new(); let iter = crate::closure::js_closure_get_capture_f64(closure, 0); + let iter_h = scope.root_nanbox_f64(iter); let (argc, first) = async_from_sync_rest_args(rest); let single = [first]; let args: &[f64] = if argc == 0 { &[] } else { &single }; - match async_from_sync_call_raw(iter, b"throw", args) { - Ok(Some(step)) => async_from_sync_continue(iter, step, true), + match async_from_sync_call_raw(iter_h.get_nanbox_f64(), b"throw", args) { + Ok(Some(step)) => async_from_sync_continue(iter_h.get_nanbox_f64(), step, true), Ok(None) => { - async_from_sync_close(iter); + async_from_sync_close(iter_h.get_nanbox_f64()); async_from_sync_rejected(b"The iterator does not provide a 'throw' method.") } Err(reason) => boxed_promise_value(crate::promise::js_promise_rejected(reason)), @@ -582,12 +690,33 @@ fn install_async_from_sync_method( func: extern "C" fn(*const crate::closure::ClosureHeader, f64) -> f64, iter: f64, ) -> f64 { + // `obj`, `iter` and the freshly-allocated closure are live young objects + // held across sibling allocations (the key string and the shape-growing + // `set_field`). Root them so the moving scavenge cannot leave a stale + // wrapper/closure behind. The key uses the long-lived allocator (immortal, + // never moves). + let scope = crate::gc::RuntimeHandleScope::new(); + let obj_h = scope.root_raw_mut_ptr(obj); + let iter_h = scope.root_nanbox_f64(iter); let closure = crate::closure::js_closure_alloc(func as *const u8, 1); - crate::closure::js_closure_set_capture_f64(closure, 0, iter); - let value = crate::value::js_nanbox_pointer(closure as i64); - let key = crate::string::js_string_from_bytes(name.as_ptr(), name.len() as u32); - crate::object::js_object_set_field_by_name(obj, key, value); - value + let closure_h = scope.root_raw_mut_ptr(closure); + let key = crate::string::js_string_from_bytes_longlived(name.as_ptr(), name.len() as u32); + crate::closure::js_closure_set_capture_f64( + closure_h.get_raw_mut_ptr::(), + 0, + iter_h.get_nanbox_f64(), + ); + let value = crate::value::js_nanbox_pointer( + closure_h.get_raw_mut_ptr::() as i64, + ); + crate::object::js_object_set_field_by_name( + obj_h.get_raw_mut_ptr::(), + key, + value, + ); + crate::value::js_nanbox_pointer( + closure_h.get_raw_mut_ptr::() as i64 + ) } /// Install the wrapper's `next` method with TWO captures: the sync iterator @@ -598,39 +727,99 @@ fn install_async_from_sync_next( iter: f64, cached_next: f64, ) -> f64 { + // Same rooting discipline as `install_async_from_sync_method`: obj, iter, + // the cached next-method and the fresh closure are young values held across + // the key allocation and the shape-growing `set_field`. + let scope = crate::gc::RuntimeHandleScope::new(); + let obj_h = scope.root_raw_mut_ptr(obj); + let iter_h = scope.root_nanbox_f64(iter); + let cached_next_h = scope.root_nanbox_f64(cached_next); let closure = crate::closure::js_closure_alloc(async_from_sync_next as *const u8, 2); - crate::closure::js_closure_set_capture_f64(closure, 0, iter); - crate::closure::js_closure_set_capture_f64(closure, 1, cached_next); - let value = crate::value::js_nanbox_pointer(closure as i64); - let key = crate::string::js_string_from_bytes(b"next".as_ptr(), 4); - crate::object::js_object_set_field_by_name(obj, key, value); - value + let closure_h = scope.root_raw_mut_ptr(closure); + let key = crate::string::js_string_from_bytes_longlived(b"next".as_ptr(), 4); + crate::closure::js_closure_set_capture_f64( + closure_h.get_raw_mut_ptr::(), + 0, + iter_h.get_nanbox_f64(), + ); + crate::closure::js_closure_set_capture_f64( + closure_h.get_raw_mut_ptr::(), + 1, + cached_next_h.get_nanbox_f64(), + ); + let value = crate::value::js_nanbox_pointer( + closure_h.get_raw_mut_ptr::() as i64, + ); + crate::object::js_object_set_field_by_name( + obj_h.get_raw_mut_ptr::(), + key, + value, + ); + crate::value::js_nanbox_pointer( + closure_h.get_raw_mut_ptr::() as i64 + ) } pub(crate) fn async_from_sync_wrap_iterator(iter: f64) -> f64 { register_async_from_sync_thunks_once(); + // The wrapper object and the sync iterator are live young values held + // across a long series of allocations (three method installs plus the + // async-iterator closure and the symbol-property store). Root them and + // re-read through their handles before each use so the moving scavenge + // cannot leave a stale wrapper/iter behind — otherwise every later `next()` + // reads a stale captured iterator. + let scope = crate::gc::RuntimeHandleScope::new(); + let iter_h = scope.root_nanbox_f64(iter); let obj = crate::object::js_object_alloc(0, 0); - let wrapper = crate::value::js_nanbox_pointer(obj as i64); + let obj_h = scope.root_raw_mut_ptr(obj); // Spec (CreateAsyncFromSyncIterator): the sync iterator record's // `[[NextMethod]]` is read once, here, and reused for every `next()` step. - let cached_next = named_field(iter, b"next"); - install_async_from_sync_next(obj, iter, cached_next); - install_async_from_sync_method(obj, b"return", async_from_sync_return, iter); - install_async_from_sync_method(obj, b"throw", async_from_sync_throw, iter); + let cached_next = named_field(iter_h.get_nanbox_f64(), b"next"); + let cached_next_h = scope.root_nanbox_f64(cached_next); + install_async_from_sync_next( + obj_h.get_raw_mut_ptr::(), + iter_h.get_nanbox_f64(), + cached_next_h.get_nanbox_f64(), + ); + install_async_from_sync_method( + obj_h.get_raw_mut_ptr::(), + b"return", + async_from_sync_return, + iter_h.get_nanbox_f64(), + ); + install_async_from_sync_method( + obj_h.get_raw_mut_ptr::(), + b"throw", + async_from_sync_throw, + iter_h.get_nanbox_f64(), + ); let async_iter = crate::closure::js_closure_alloc(async_from_sync_async_iterator as *const u8, 1); - crate::closure::js_closure_set_capture_f64(async_iter, 0, wrapper); + let async_iter_h = scope.root_raw_mut_ptr(async_iter); + let wrapper = crate::value::js_nanbox_pointer( + obj_h.get_raw_mut_ptr::() as i64, + ); + crate::closure::js_closure_set_capture_f64( + async_iter_h.get_raw_mut_ptr::(), + 0, + wrapper, + ); let sym = crate::symbol::well_known_symbol("asyncIterator"); if !sym.is_null() { + let wrapper = crate::value::js_nanbox_pointer( + obj_h.get_raw_mut_ptr::() as i64, + ); unsafe { crate::symbol::js_object_set_symbol_property( wrapper, f64::from_bits(crate::value::JSValue::pointer(sym as *const u8).bits()), - crate::value::js_nanbox_pointer(async_iter as i64), + crate::value::js_nanbox_pointer( + async_iter_h.get_raw_mut_ptr::() as i64, + ), ); } } - wrapper + crate::value::js_nanbox_pointer(obj_h.get_raw_mut_ptr::() as i64) } #[no_mangle] diff --git a/crates/perry-runtime/src/closure/alloc.rs b/crates/perry-runtime/src/closure/alloc.rs index f0c5b3da2b..ca21e81741 100644 --- a/crates/perry-runtime/src/closure/alloc.rs +++ b/crates/perry-runtime/src/closure/alloc.rs @@ -488,6 +488,10 @@ pub(crate) fn test_captured_singleton_closure_cache_entries( /// covers the per-batch fan-out shape (50 promises) found in /// `benchmarks/app-patterns/kernels/promise_all_chains.ts`. const MAX_CAPTURED_CLOSURE_SLOTS: usize = 64; +const _: () = assert!( + MAX_CAPTURED_CLOSURE_SLOTS <= u8::MAX as usize, + "hint_indices_plus_one stores an entry index plus one in a u8" +); /// Per-`func_ptr` cache miss-streak counter for the adaptive bypass. /// Closures whose captures change every call (per-call boxes for @@ -581,9 +585,8 @@ pub extern "C" fn js_closure_alloc_with_captures_singleton( } crate::promise::bump(&CLOSURE_CAP_SINGLETON_MISS); - // Slow path: allocate, populate captures, insert into cache as - // the most-recent entry. If the slot list is full, drop the - // least-recent (back of the Vec). + // Slow path: allocate, populate captures, and insert with a fresh usage + // timestamp. If the entry list is full, replace its oldest timestamp. let capture_scope = crate::gc::RuntimeHandleScope::new(); let capture_handles: Vec<_> = captures_slice .iter() diff --git a/crates/perry-runtime/src/lib.rs b/crates/perry-runtime/src/lib.rs index fee2ff8e9e..c88131b72f 100644 --- a/crates/perry-runtime/src/lib.rs +++ b/crates/perry-runtime/src/lib.rs @@ -307,8 +307,8 @@ pub use object::{ }; pub use promise::{js_is_promise, js_promise_run_microtasks, js_promise_state, js_promise_value}; pub use promise::{ - js_promise_mark_internally_handled, js_promise_new, js_promise_reject, js_promise_rejected, - js_promise_resolve, js_promise_resolved, + js_promise_mark_internally_handled, js_promise_new, js_promise_new_cross_thread, + js_promise_reject, js_promise_rejected, js_promise_resolve, js_promise_resolved, }; pub use string::js_string_from_bytes; pub use value::{ diff --git a/crates/perry-stdlib/src/argon2.rs b/crates/perry-stdlib/src/argon2.rs index 72a74c25cc..813b891c55 100644 --- a/crates/perry-stdlib/src/argon2.rs +++ b/crates/perry-stdlib/src/argon2.rs @@ -9,14 +9,14 @@ use argon2::{ password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, Argon2, }; -use perry_runtime::{js_promise_new, js_string_from_bytes, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, js_string_from_bytes, Promise, StringHeader}; /// argon2.hash(password) -> Promise /// /// Hash a password using Argon2id with default parameters. #[no_mangle] pub unsafe extern "C" fn js_argon2_hash(password_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let password = match string_from_header(password_ptr) { Some(p) => p, @@ -77,7 +77,7 @@ pub unsafe extern "C" fn js_argon2_verify( hash_ptr: *const StringHeader, password_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let hash_str = match string_from_header(hash_ptr) { Some(h) => h, diff --git a/crates/perry-stdlib/src/axios.rs b/crates/perry-stdlib/src/axios.rs index 23c812df1b..2a606e45ba 100644 --- a/crates/perry-stdlib/src/axios.rs +++ b/crates/perry-stdlib/src/axios.rs @@ -7,7 +7,7 @@ use crate::common::{ get_handle, register_handle, spawn_for_promise, string_from_header_lossy as string_from_header, Handle, }; -use perry_runtime::{js_promise_new, js_string_from_bytes, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, js_string_from_bytes, Promise, StringHeader}; /// #598: read the body argument as a JSON string. Strings pass /// through as-is; everything else is JSON.stringify'd via the @@ -49,7 +49,7 @@ unsafe fn request_without_body( url_ptr: *const StringHeader, method: reqwest::Method, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let url = match string_from_header(url_ptr) { Some(u) => u, @@ -119,7 +119,7 @@ pub unsafe extern "C" fn js_axios_options(url_ptr: *const StringHeader) -> *mut /// axios.post(url, data) -> Promise #[no_mangle] pub unsafe extern "C" fn js_axios_post(url_ptr: *const StringHeader, data: f64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let url = match string_from_header(url_ptr) { Some(u) => u, @@ -186,7 +186,7 @@ pub unsafe extern "C" fn js_axios_post(url_ptr: *const StringHeader, data: f64) /// axios.put(url, data) -> Promise #[no_mangle] pub unsafe extern "C" fn js_axios_put(url_ptr: *const StringHeader, data: f64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let url = match string_from_header(url_ptr) { Some(u) => u, @@ -250,7 +250,7 @@ pub unsafe extern "C" fn js_axios_put(url_ptr: *const StringHeader, data: f64) - /// axios.delete(url) -> Promise #[no_mangle] pub unsafe extern "C" fn js_axios_delete(url_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let url = match string_from_header(url_ptr) { Some(u) => u, @@ -305,7 +305,7 @@ pub unsafe extern "C" fn js_axios_delete(url_ptr: *const StringHeader) -> *mut P /// axios.patch(url, data) -> Promise #[no_mangle] pub unsafe extern "C" fn js_axios_patch(url_ptr: *const StringHeader, data: f64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let url = match string_from_header(url_ptr) { Some(u) => u, diff --git a/crates/perry-stdlib/src/bcrypt.rs b/crates/perry-stdlib/src/bcrypt.rs index 661300456e..88430d1144 100644 --- a/crates/perry-stdlib/src/bcrypt.rs +++ b/crates/perry-stdlib/src/bcrypt.rs @@ -15,7 +15,7 @@ pub unsafe extern "C" fn js_bcrypt_hash( password_ptr: *const StringHeader, salt_rounds: f64, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let password = match string_from_header(password_ptr) { @@ -79,7 +79,7 @@ pub unsafe extern "C" fn js_bcrypt_compare( password_ptr: *const StringHeader, hash_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let password = match string_from_header(password_ptr) { @@ -142,7 +142,7 @@ pub unsafe extern "C" fn js_bcrypt_compare( /// bcrypt.genSalt(rounds) -> Promise #[no_mangle] pub unsafe extern "C" fn js_bcrypt_gen_salt(rounds: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let cost = rounds as u32; diff --git a/crates/perry-stdlib/src/common/async_bridge.rs b/crates/perry-stdlib/src/common/async_bridge.rs index b65efe072e..5badc20ba3 100644 --- a/crates/perry-stdlib/src/common/async_bridge.rs +++ b/crates/perry-stdlib/src/common/async_bridge.rs @@ -83,7 +83,18 @@ unsafe fn unpin_promise_after_native_resolution(promise_ptr: usize) { #[inline] pub unsafe fn js_promise_new_for_native_resolution() -> *mut perry_runtime::Promise { ensure_gc_scanner_registered(); - let p = perry_runtime::js_promise_new(); + // #8770: allocate in MALLOC space (non-moving), not the nursery arena. A + // native-resolution promise is handed to a tokio worker as a raw `usize` and, + // until its resolution is queued into PENDING_RESOLUTIONS (which the root + // scanner visits), it is reachable only through that worker-thread capture — + // invisible to the main-thread copying minor. A nursery resident in that + // window is wiped by the from-space flip REGARDLESS of its PIN flag (the flip + // resets eden/survivor blocks wholesale; only root-reachable pins force the + // fallback — see `js_promise_new_cross_thread`). Then `js_stdlib_process_ + // pending` unpins/resolves through the stale pointer and faults on the + // reclaimed header. Malloc space is non-moving and both sweep paths honor + // GC_FLAG_PINNED, so the pin actually protects it there. + let p = perry_runtime::js_promise_new_cross_thread(); pin_promise_for_native_resolution(p as usize); p } diff --git a/crates/perry-stdlib/src/container/backend_ctl.rs b/crates/perry-stdlib/src/container/backend_ctl.rs index 26f429c7ff..10dfc11be8 100644 --- a/crates/perry-stdlib/src/container/backend_ctl.rs +++ b/crates/perry-stdlib/src/container/backend_ctl.rs @@ -6,7 +6,7 @@ pub use types::{ }; pub use backend::{detect_backend, ContainerBackend}; -use perry_runtime::{js_promise_new, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, Promise, StringHeader}; use std::collections::HashMap; use std::sync::Arc; use std::sync::OnceLock; @@ -77,7 +77,7 @@ pub unsafe extern "C" fn js_container_getBackend() -> *const StringHeader { /// FFI: js_container_detectBackend() -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_detectBackend() -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise_deferred( promise as *mut u8, async move { @@ -199,7 +199,7 @@ pub unsafe extern "C" fn js_container_selectBackendFor( /// await setBackends(ready.map(b => b.name)); #[no_mangle] pub unsafe extern "C" fn js_container_getAvailableBackends() -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise_deferred( promise as *mut u8, async move { @@ -249,7 +249,7 @@ pub unsafe extern "C" fn js_container_getBackendPriority() -> *const StringHeade /// - `"backend probe failed: "` #[no_mangle] pub unsafe extern "C" fn js_container_setBackend(name_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let name = match string_from_header(name_ptr) { Some(s) => s, None => { @@ -328,7 +328,7 @@ pub unsafe extern "C" fn js_container_setBackend(name_ptr: *const StringHeader) pub unsafe extern "C" fn js_container_setBackends( names_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let names_json = match string_from_header(names_json_ptr) { Some(s) => s, None => { diff --git a/crates/perry-stdlib/src/container/compose_ffi.rs b/crates/perry-stdlib/src/container/compose_ffi.rs index f6278941c6..44bec3be7d 100644 --- a/crates/perry-stdlib/src/container/compose_ffi.rs +++ b/crates/perry-stdlib/src/container/compose_ffi.rs @@ -6,7 +6,7 @@ pub use types::{ }; pub use backend::{detect_backend, ContainerBackend}; -use perry_runtime::{js_promise_new, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, Promise, StringHeader}; use std::collections::HashMap; use std::sync::Arc; use std::sync::OnceLock; @@ -19,7 +19,7 @@ pub unsafe extern "C" fn js_container_compose_start( handle: f64, services_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { @@ -57,7 +57,7 @@ pub unsafe extern "C" fn js_container_compose_stop( handle: f64, services_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { @@ -95,7 +95,7 @@ pub unsafe extern "C" fn js_container_compose_restart( handle: f64, services_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { @@ -131,7 +131,7 @@ pub unsafe extern "C" fn js_container_compose_restart( /// FFI: `js_container_compose_config(handle: f64) -> *mut Promise` #[no_mangle] pub unsafe extern "C" fn js_container_compose_config(handle: f64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { @@ -164,7 +164,7 @@ pub unsafe extern "C" fn js_container_compose_config(handle: f64) -> *mut Promis pub unsafe extern "C" fn js_container_composeUp( spec_ptr: *const perry_runtime::StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let spec = match types::parse_compose_spec(spec_ptr) { Ok(s) => s, @@ -283,7 +283,7 @@ pub unsafe extern "C" fn js_container_compose_down( handle: f64, opts_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let opts_json = unsafe { string_from_header(opts_ptr) }; @@ -330,7 +330,7 @@ pub unsafe extern "C" fn js_container_compose_down( /// FFI: `js_container_compose_ps(handle: f64) -> *mut Promise` #[no_mangle] pub unsafe extern "C" fn js_container_compose_ps(handle: f64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { @@ -376,7 +376,7 @@ pub unsafe extern "C" fn js_container_compose_logs( service_ptr: *const StringHeader, tail: f64, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { @@ -427,7 +427,7 @@ pub unsafe extern "C" fn js_container_compose_exec( service_ptr: *const StringHeader, cmd_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let handle_id = handle_id_from_f64(handle); let engine = match types::get_compose_handle(handle_id as u64) { diff --git a/crates/perry-stdlib/src/container/images.rs b/crates/perry-stdlib/src/container/images.rs index 858e8b11c3..225ba2f129 100644 --- a/crates/perry-stdlib/src/container/images.rs +++ b/crates/perry-stdlib/src/container/images.rs @@ -6,7 +6,7 @@ pub use types::{ }; pub use backend::{detect_backend, ContainerBackend}; -use perry_runtime::{js_promise_new, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, Promise, StringHeader}; use std::collections::HashMap; use std::sync::Arc; use std::sync::OnceLock; @@ -19,7 +19,7 @@ use std::sync::OnceLock; pub unsafe extern "C" fn js_container_pullImage( reference_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let reference = match string_from_header(reference_ptr) { Some(s) => s, @@ -52,7 +52,7 @@ pub unsafe extern "C" fn js_container_pullImage( /// FFI: js_container_listImages() -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_listImages() -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Resolves with a JSON-encoded `ImageInfo[]` string. crate::common::spawn_for_promise_deferred( @@ -78,7 +78,7 @@ pub unsafe extern "C" fn js_container_build( spec_ptr: *const StringHeader, image_name_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let spec_json = string_from_header(spec_ptr).unwrap_or_else(|| "{}".to_string()); let image_name = string_from_header(image_name_ptr).unwrap_or_default(); @@ -108,7 +108,7 @@ pub unsafe extern "C" fn js_container_removeImage( reference_ptr: *const StringHeader, force: i32, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let reference = match string_from_header(reference_ptr) { Some(s) => s, diff --git a/crates/perry-stdlib/src/container/lifecycle.rs b/crates/perry-stdlib/src/container/lifecycle.rs index 2d6097b8d6..a82d0e2ed8 100644 --- a/crates/perry-stdlib/src/container/lifecycle.rs +++ b/crates/perry-stdlib/src/container/lifecycle.rs @@ -6,7 +6,7 @@ pub use types::{ }; pub use backend::{detect_backend, ContainerBackend}; -use perry_runtime::{js_promise_new, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, Promise, StringHeader}; use std::collections::HashMap; use std::sync::Arc; use std::sync::OnceLock; @@ -17,7 +17,7 @@ use std::sync::OnceLock; /// FFI: js_container_run(spec_json: *const StringHeader) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_run(spec_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let spec = match types::parse_container_spec(spec_ptr) { Ok(s) => s, @@ -66,7 +66,7 @@ pub unsafe extern "C" fn js_container_run(spec_ptr: *const StringHeader) -> *mut /// FFI: js_container_create(spec_json: *const StringHeader) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_create(spec_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let spec = match types::parse_container_spec(spec_ptr) { Ok(s) => s, @@ -111,7 +111,7 @@ pub unsafe extern "C" fn js_container_create(spec_ptr: *const StringHeader) -> * /// FFI: js_container_start(id: *const StringHeader) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_start(id_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) => s, @@ -144,7 +144,7 @@ pub unsafe extern "C" fn js_container_stop( id_ptr: *const StringHeader, timeout: i32, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) => s, @@ -182,7 +182,7 @@ pub unsafe extern "C" fn js_container_remove( id_ptr: *const StringHeader, force: i32, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) => s, @@ -230,7 +230,7 @@ pub unsafe extern "C" fn js_container_downByProject( project_ptr: *const StringHeader, opts_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let project = match string_from_header(project_ptr) { Some(s) if !s.is_empty() => s, _ => { @@ -271,7 +271,7 @@ pub unsafe extern "C" fn js_container_downByProject( /// FFI: `js_container_downAll(opts_json: *const StringHeader) -> *mut Promise` #[no_mangle] pub unsafe extern "C" fn js_container_downAll(opts_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let opts_json = string_from_header(opts_ptr); crate::common::spawn_for_promise_deferred( @@ -302,7 +302,7 @@ pub unsafe extern "C" fn js_container_removeIfExists( id_ptr: *const StringHeader, force: i32, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) if !s.is_empty() => s, _ => { @@ -363,7 +363,7 @@ pub(crate) fn parse_cleanup_options( /// `JSON.parse(await list(true))` to recover the array. #[no_mangle] pub unsafe extern "C" fn js_container_list(all: i32) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise_deferred( promise as *mut u8, @@ -385,7 +385,7 @@ pub unsafe extern "C" fn js_container_list(all: i32) -> *mut Promise { /// FFI: js_container_inspect(id: *const StringHeader) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_inspect(id_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) => s, diff --git a/crates/perry-stdlib/src/container/logs_exec.rs b/crates/perry-stdlib/src/container/logs_exec.rs index e3c254c65e..d03577dd33 100644 --- a/crates/perry-stdlib/src/container/logs_exec.rs +++ b/crates/perry-stdlib/src/container/logs_exec.rs @@ -6,7 +6,7 @@ pub use types::{ }; pub use backend::{detect_backend, ContainerBackend}; -use perry_runtime::{js_promise_new, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, Promise, StringHeader}; use std::collections::HashMap; use std::sync::Arc; use std::sync::OnceLock; @@ -17,7 +17,7 @@ use std::sync::OnceLock; /// FFI: js_container_logs(id: *const StringHeader, tail: i32) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_container_logs(id_ptr: *const StringHeader, tail: i32) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) => s, @@ -60,7 +60,7 @@ pub unsafe extern "C" fn js_container_exec( env_json_ptr: *const StringHeader, workdir_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = match string_from_header(id_ptr) { Some(s) => s, diff --git a/crates/perry-stdlib/src/container/workload.rs b/crates/perry-stdlib/src/container/workload.rs index 8053d70bb4..51da2d0074 100644 --- a/crates/perry-stdlib/src/container/workload.rs +++ b/crates/perry-stdlib/src/container/workload.rs @@ -6,7 +6,7 @@ pub use types::{ }; pub use backend::{detect_backend, ContainerBackend}; -use perry_runtime::{js_promise_new, Promise, StringHeader}; +use perry_runtime::{js_promise_new_cross_thread, Promise, StringHeader}; use std::collections::HashMap; use std::sync::Arc; use std::sync::OnceLock; @@ -69,7 +69,7 @@ pub unsafe extern "C" fn js_workload_runGraph( graph_json_ptr: *const StringHeader, opts_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let graph_json = string_from_header(graph_json_ptr).unwrap_or_else(|| "{}".to_string()); let opts_json = string_from_header(opts_json_ptr).unwrap_or_else(|| "{}".to_string()); @@ -104,7 +104,7 @@ pub unsafe extern "C" fn js_workload_runGraph( /// FFI: js_workload_inspectGraph(handle_id: i64) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_workload_inspectGraph(handle_id: i64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = handle_id as u64; crate::common::spawn_for_promise_deferred( @@ -136,7 +136,7 @@ pub unsafe extern "C" fn js_workload_inspectGraph(handle_id: i64) -> *mut Promis /// FFI: js_workload_handle_down(handle_id: i64, force: i32) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_workload_handle_down(handle_id: i64, force: i32) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = handle_id as u64; crate::common::spawn_for_promise(promise as *mut u8, async move { @@ -163,7 +163,7 @@ pub unsafe extern "C" fn js_workload_handle_down(handle_id: i64, force: i32) -> /// FFI: js_workload_handle_status(handle_id: i64) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_workload_handle_status(handle_id: i64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = handle_id as u64; crate::common::spawn_for_promise_deferred( @@ -199,7 +199,7 @@ pub unsafe extern "C" fn js_workload_handle_logs( node_id_ptr: *const StringHeader, tail: i32, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = handle_id as u64; let node_id = string_from_header(node_id_ptr).unwrap_or_default(); let tail_opt = if tail >= 0 { Some(tail as u32) } else { None }; @@ -230,7 +230,7 @@ pub unsafe extern "C" fn js_workload_handle_exec( node_id_ptr: *const StringHeader, cmd_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = handle_id as u64; let node_id = string_from_header(node_id_ptr).unwrap_or_default(); let cmd_json = string_from_header(cmd_json_ptr).unwrap_or_else(|| "[]".to_string()); @@ -258,7 +258,7 @@ pub unsafe extern "C" fn js_workload_handle_exec( /// FFI: js_workload_handle_ps(handle_id: i64) -> *mut Promise #[no_mangle] pub unsafe extern "C" fn js_workload_handle_ps(handle_id: i64) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let id = handle_id as u64; crate::common::spawn_for_promise(promise as *mut u8, async move { diff --git a/crates/perry-stdlib/src/fetch/mod.rs b/crates/perry-stdlib/src/fetch/mod.rs index d03d4c0a31..e5ffae14b9 100644 --- a/crates/perry-stdlib/src/fetch/mod.rs +++ b/crates/perry-stdlib/src/fetch/mod.rs @@ -380,7 +380,7 @@ fn tagged_bool(value: bool) -> f64 { /// fetch(url) -> Promise #[no_mangle] pub unsafe extern "C" fn js_fetch_get(url_ptr: *const StringHeader) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let url = match string_from_header(url_ptr) { @@ -450,7 +450,7 @@ pub unsafe extern "C" fn js_fetch_get_with_auth( url_ptr: *const StringHeader, auth_header_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let url = match string_from_header(url_ptr) { @@ -526,7 +526,7 @@ pub unsafe extern "C" fn js_fetch_post_with_auth( auth_header_ptr: *const StringHeader, body_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let url = match string_from_header(url_ptr) { @@ -604,7 +604,7 @@ pub unsafe extern "C" fn js_fetch_post( body_ptr: *const StringHeader, content_type_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let url = match string_from_header(url_ptr) { @@ -696,7 +696,7 @@ pub unsafe extern "C" fn js_fetch_with_options( // allocation can't move the still-TLS-stashed signal before we read it. let abort_state = abort_bridge::take_pending_signal_watch(); - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; // An already-aborted signal rejects the request up front; otherwise keep the @@ -824,7 +824,7 @@ fn consume_response_body(handle: f64) -> Result, &'static str> { /// `Expr::Await` for the rationale). #[no_mangle] pub unsafe extern "C" fn js_fetch_response_text(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let body = match consume_response_body(handle) { Ok(body) => body, Err(err_msg) if err_msg == BODY_ALREADY_USED_MESSAGE => { @@ -893,7 +893,7 @@ unsafe fn json_value_to_jsvalue(value: &serde_json::Value) -> JSValue { /// response.json() -> Promise #[no_mangle] pub unsafe extern "C" fn js_fetch_response_json(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let body = match consume_response_body(handle) { Ok(body) => body, Err(err_msg) if err_msg == BODY_ALREADY_USED_MESSAGE => { @@ -932,7 +932,7 @@ pub unsafe extern "C" fn js_fetch_response_json(handle: f64) -> *mut perry_runti pub unsafe extern "C" fn js_fetch_text( url_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let url = match string_from_header(url_ptr) { @@ -1284,7 +1284,7 @@ fn alloc_headers(store: HeadersStore) -> usize { /// doesn't hang. See `js_fetch_response_text` for rationale. #[no_mangle] pub unsafe extern "C" fn js_response_array_buffer(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let body = match consume_response_body(handle) { Ok(body) => body, Err(err_msg) if err_msg == BODY_ALREADY_USED_MESSAGE => { @@ -1322,7 +1322,7 @@ pub unsafe extern "C" fn js_response_array_buffer(handle: f64) -> *mut perry_run /// `.slice()` / `.size` / `.type` to the FFIs below. #[no_mangle] pub unsafe extern "C" fn js_response_blob(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let id = handle_id(handle); let content_type = { let guard = FETCH_RESPONSES.lock().unwrap(); @@ -1391,7 +1391,7 @@ pub unsafe extern "C" fn js_blob_type(handle: f64) -> *mut StringHeader { /// property dispatch in `value.rs`. Resolved synchronously. #[no_mangle] pub unsafe extern "C" fn js_blob_array_buffer(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let id = handle_id(handle); let body: Vec = BLOB_REGISTRY .lock() @@ -1427,7 +1427,7 @@ pub unsafe extern "C" fn js_blob_bytes(handle: f64) -> *mut perry_runtime::Promi /// characters; lossy_utf8 produces U+FFFD identically). #[no_mangle] pub unsafe extern "C" fn js_blob_text(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let id = handle_id(handle); let body: Vec = BLOB_REGISTRY .lock() @@ -1850,7 +1850,7 @@ fn consume_request_body(handle: f64) -> Result, &'static str> { /// (#1688) #[no_mangle] pub unsafe extern "C" fn js_request_text(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); match consume_request_body(handle) { Ok(body) => { let text = String::from_utf8_lossy(&body).to_string(); @@ -1873,7 +1873,7 @@ pub unsafe extern "C" fn js_request_text(handle: f64) -> *mut perry_runtime::Pro /// `js_fetch_response_json`. (#1688) #[no_mangle] pub unsafe extern "C" fn js_request_json(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let body = match consume_request_body(handle) { Ok(b) => b, Err(err_msg) if err_msg == BODY_ALREADY_USED_MESSAGE => { @@ -1904,7 +1904,7 @@ pub unsafe extern "C" fn js_request_json(handle: f64) -> *mut perry_runtime::Pro /// BufferHeader over the body bytes, mirroring `js_response_array_buffer`. (#1688) #[no_mangle] pub unsafe extern "C" fn js_request_array_buffer(handle: f64) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let body = match consume_request_body(handle) { Ok(b) => b, Err(err_msg) if err_msg == BODY_ALREADY_USED_MESSAGE => { diff --git a/crates/perry-stdlib/src/ioredis.rs b/crates/perry-stdlib/src/ioredis.rs index d094f218a7..514c0c5d48 100644 --- a/crates/perry-stdlib/src/ioredis.rs +++ b/crates/perry-stdlib/src/ioredis.rs @@ -104,7 +104,7 @@ pub unsafe extern "C" fn js_ioredis_set( key_ptr: *const StringHeader, value_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -176,7 +176,7 @@ pub unsafe extern "C" fn js_ioredis_get( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -240,7 +240,7 @@ pub unsafe extern "C" fn js_ioredis_del( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -297,7 +297,7 @@ pub unsafe extern "C" fn js_ioredis_exists( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -354,7 +354,7 @@ pub unsafe extern "C" fn js_ioredis_incr( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -411,7 +411,7 @@ pub unsafe extern "C" fn js_ioredis_decr( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -469,7 +469,7 @@ pub unsafe extern "C" fn js_ioredis_expire( key_ptr: *const StringHeader, seconds: f64, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -525,7 +525,7 @@ pub unsafe extern "C" fn js_ioredis_expire( /// redis.connect() -> Promise #[no_mangle] pub unsafe extern "C" fn js_ioredis_connect(handle: Handle) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; spawn(async move { @@ -554,7 +554,7 @@ pub unsafe extern "C" fn js_ioredis_setex( seconds: f64, value_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -632,7 +632,7 @@ pub unsafe extern "C" fn js_ioredis_disconnect(handle: Handle) { /// redis.ping() -> Promise<"PONG"> #[no_mangle] pub unsafe extern "C" fn js_ioredis_ping(handle: Handle) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; spawn(async move { @@ -683,7 +683,7 @@ pub unsafe extern "C" fn js_ioredis_hget( key_ptr: *const StringHeader, field_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -755,7 +755,7 @@ pub unsafe extern "C" fn js_ioredis_hset( field_ptr: *const StringHeader, value_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -826,7 +826,7 @@ pub unsafe extern "C" fn js_ioredis_hgetall( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -903,7 +903,7 @@ pub unsafe extern "C" fn js_ioredis_hdel( key_ptr: *const StringHeader, field_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -966,7 +966,7 @@ pub unsafe extern "C" fn js_ioredis_hlen( handle: Handle, key_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let key = match string_from_header(key_ptr) { @@ -1018,7 +1018,7 @@ pub unsafe extern "C" fn js_ioredis_hlen( /// redis.quit() -> Promise<"OK"> #[no_mangle] pub unsafe extern "C" fn js_ioredis_quit(handle: Handle) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; // Remove connection from cache diff --git a/crates/perry-stdlib/src/mongodb.rs b/crates/perry-stdlib/src/mongodb.rs index 7122602d96..26ab49a001 100644 --- a/crates/perry-stdlib/src/mongodb.rs +++ b/crates/perry-stdlib/src/mongodb.rs @@ -11,8 +11,8 @@ use bson::{doc, Document}; use mongodb::{Client, Collection, Database}; use perry_runtime::json::js_json_stringify; use perry_runtime::{ - js_object_alloc, js_object_set_field, js_promise_new, js_string_from_bytes, JSValue, - ObjectHeader, Promise, StringHeader, + js_object_alloc, js_object_set_field, js_promise_new_cross_thread, js_string_from_bytes, + JSValue, ObjectHeader, Promise, StringHeader, }; /// JSON-stringify a NaN-boxed JSValue at the FFI boundary. Used by the @@ -94,7 +94,7 @@ pub unsafe extern "C" fn js_mongodb_client_new(uri_ptr: *const StringHeader) -> pub unsafe extern "C" fn js_mongodb_client_connect(client_handle: Handle) -> *mut Promise { use crate::common::get_handle_mut; - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let pending = if let Some(h) = get_handle_mut::(client_handle) { h.pending_uri.take() @@ -186,7 +186,7 @@ unsafe fn bson_to_jsvalue(doc: &Document) -> *mut ObjectHeader { /// MongoClient.connect(uri) -> Promise #[no_mangle] pub unsafe extern "C" fn js_mongodb_connect(uri_ptr: *const StringHeader) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let uri = match string_from_header(uri_ptr) { Some(u) => u, @@ -272,7 +272,7 @@ pub unsafe extern "C" fn js_mongodb_collection_find_one( collection_handle: Handle, filter_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); @@ -328,7 +328,7 @@ pub unsafe extern "C" fn js_mongodb_collection_find( collection_handle: Handle, filter_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); @@ -376,7 +376,7 @@ pub unsafe extern "C" fn js_mongodb_collection_insert_one( collection_handle: Handle, doc_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let doc_json = match string_from_header(doc_json_ptr) { Some(j) => j, @@ -418,7 +418,7 @@ pub unsafe extern "C" fn js_mongodb_collection_insert_many( collection_handle: Handle, docs_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let docs_json = match string_from_header(docs_json_ptr) { Some(j) => j, @@ -457,7 +457,7 @@ pub unsafe extern "C" fn js_mongodb_collection_update_one( filter_json_ptr: *const StringHeader, update_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); let update_json = match string_from_header(update_json_ptr) { @@ -495,7 +495,7 @@ pub unsafe extern "C" fn js_mongodb_collection_update_many( filter_json_ptr: *const StringHeader, update_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); let update_json = match string_from_header(update_json_ptr) { @@ -532,7 +532,7 @@ pub unsafe extern "C" fn js_mongodb_collection_delete_one( collection_handle: Handle, filter_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); @@ -558,7 +558,7 @@ pub unsafe extern "C" fn js_mongodb_collection_delete_many( collection_handle: Handle, filter_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); @@ -584,7 +584,7 @@ pub unsafe extern "C" fn js_mongodb_collection_count( collection_handle: Handle, filter_json_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let filter_json = string_from_header(filter_json_ptr).unwrap_or_else(|| "{}".to_string()); @@ -714,7 +714,7 @@ pub unsafe extern "C" fn js_mongodb_collection_count_value( /// client.close() -> Promise #[no_mangle] pub unsafe extern "C" fn js_mongodb_client_close(_client_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); spawn_for_promise(promise as *mut u8, async move { // MongoDB client doesn't need explicit close in Rust driver @@ -728,7 +728,7 @@ pub unsafe extern "C" fn js_mongodb_client_close(_client_handle: Handle) -> *mut /// client.listDatabases() -> Promise (JSON array of database names) #[no_mangle] pub unsafe extern "C" fn js_mongodb_client_list_databases(client_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); spawn_for_promise_deferred( promise as *mut u8, @@ -759,7 +759,7 @@ pub unsafe extern "C" fn js_mongodb_client_list_databases(client_handle: Handle) /// db.listCollections() -> Promise (JSON array of collection names) #[no_mangle] pub unsafe extern "C" fn js_mongodb_db_list_collections(db_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); spawn_for_promise_deferred( promise as *mut u8, diff --git a/crates/perry-stdlib/src/mysql2/connection.rs b/crates/perry-stdlib/src/mysql2/connection.rs index 6533951be8..7faa0ebc11 100644 --- a/crates/perry-stdlib/src/mysql2/connection.rs +++ b/crates/perry-stdlib/src/mysql2/connection.rs @@ -2,7 +2,7 @@ use std::time::Duration; -use perry_runtime::{js_promise_new, JSValue, Promise}; +use perry_runtime::{js_promise_new_cross_thread, JSValue, Promise}; use sqlx::mysql::MySqlConnection; use sqlx::Connection; @@ -45,7 +45,7 @@ pub unsafe extern "C" fn js_mysql2_create_connection(config_f: f64) -> *mut Prom // Take f64 at the FFI boundary to avoid SysV AMD64 ABI mismatch // (see js_mysql2_create_pool for details). let config = JSValue::from_bits(config_f.to_bits()); - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Parse the config let mysql_config = parse_mysql_config(config); @@ -84,7 +84,7 @@ pub unsafe extern "C" fn js_mysql2_create_connection(config_f: f64) -> *mut Prom /// Closes the MySQL connection. #[no_mangle] pub unsafe extern "C" fn js_mysql2_connection_end(conn_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::take_handle; @@ -129,7 +129,7 @@ pub unsafe extern "C" fn js_mysql2_connection_query( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { @@ -262,7 +262,7 @@ pub unsafe extern "C" fn js_mysql2_connection_execute( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let sql = if sql_ptr.is_null() { String::new() @@ -415,7 +415,7 @@ pub unsafe extern "C" fn js_mysql2_connection_execute( pub unsafe extern "C" fn js_mysql2_connection_begin_transaction( conn_handle: Handle, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::get_handle_mut; @@ -451,7 +451,7 @@ pub unsafe extern "C" fn js_mysql2_connection_begin_transaction( /// connection.commit() -> Promise #[no_mangle] pub unsafe extern "C" fn js_mysql2_connection_commit(conn_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::get_handle_mut; @@ -487,7 +487,7 @@ pub unsafe extern "C" fn js_mysql2_connection_commit(conn_handle: Handle) -> *mu /// connection.rollback() -> Promise #[no_mangle] pub unsafe extern "C" fn js_mysql2_connection_rollback(conn_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::get_handle_mut; diff --git a/crates/perry-stdlib/src/mysql2/pool.rs b/crates/perry-stdlib/src/mysql2/pool.rs index 052cc5ddd4..8bb242b4d5 100644 --- a/crates/perry-stdlib/src/mysql2/pool.rs +++ b/crates/perry-stdlib/src/mysql2/pool.rs @@ -2,7 +2,9 @@ use std::time::Duration; -use perry_runtime::{js_array_get_jsvalue, js_array_length, js_promise_new, JSValue, Promise}; +use perry_runtime::{ + js_array_get_jsvalue, js_array_length, js_promise_new_cross_thread, JSValue, Promise, +}; use sqlx::mysql::{MySqlPool, MySqlPoolOptions}; use sqlx::pool::PoolConnection; use sqlx::MySql; @@ -90,7 +92,7 @@ pub unsafe extern "C" fn js_mysql2_create_pool(config_f: f64) -> Handle { /// Closes all connections in the pool. #[no_mangle] pub unsafe extern "C" fn js_mysql2_pool_end(pool_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::take_handle; @@ -139,7 +141,7 @@ pub unsafe extern "C" fn js_mysql2_pool_query( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { @@ -239,7 +241,7 @@ pub unsafe extern "C" fn js_mysql2_pool_execute( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { @@ -432,7 +434,7 @@ pub(crate) unsafe fn extract_params_from_jsvalue(params: JSValue) -> Vec *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::get_handle; @@ -495,7 +497,7 @@ pub unsafe extern "C" fn js_mysql2_pool_connection_query( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { @@ -589,7 +591,7 @@ pub unsafe extern "C" fn js_mysql2_pool_connection_execute( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { diff --git a/crates/perry-stdlib/src/net/mod.rs b/crates/perry-stdlib/src/net/mod.rs index b2d832e096..501032b8f4 100644 --- a/crates/perry-stdlib/src/net/mod.rs +++ b/crates/perry-stdlib/src/net/mod.rs @@ -1695,7 +1695,7 @@ pub unsafe extern "C" fn js_net_socket_upgrade_tls( servername_ptr: i64, verify: f64, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as *mut u8; let servername = match string_from_header_i64(servername_ptr) { diff --git a/crates/perry-stdlib/src/nodemailer.rs b/crates/perry-stdlib/src/nodemailer.rs index 536d3e4002..3e04cf804b 100644 --- a/crates/perry-stdlib/src/nodemailer.rs +++ b/crates/perry-stdlib/src/nodemailer.rs @@ -7,7 +7,7 @@ use lettre::message::header::ContentType; use lettre::transport::smtp::authentication::Credentials; use lettre::{AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor}; use perry_runtime::{ - js_promise_new, js_string_from_bytes, JSValue, ObjectHeader, Promise, StringHeader, + js_promise_new_cross_thread, js_string_from_bytes, JSValue, ObjectHeader, Promise, StringHeader, }; use crate::common::{register_handle, Handle}; @@ -193,7 +193,7 @@ pub unsafe extern "C" fn js_nodemailer_send_mail( transporter_handle: Handle, options: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Parse mail options let mail_opts = match parse_mail_options(options) { @@ -298,7 +298,7 @@ pub unsafe extern "C" fn js_nodemailer_send_mail( /// Verifies that the transporter can connect to the SMTP server. #[no_mangle] pub unsafe extern "C" fn js_nodemailer_verify(transporter_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::get_handle; diff --git a/crates/perry-stdlib/src/pg/connection.rs b/crates/perry-stdlib/src/pg/connection.rs index a2cffafff6..176d3d4321 100644 --- a/crates/perry-stdlib/src/pg/connection.rs +++ b/crates/perry-stdlib/src/pg/connection.rs @@ -1,6 +1,8 @@ //! PostgreSQL connection implementation -use perry_runtime::{js_array_get_jsvalue, js_array_length, js_promise_new, JSValue, Promise}; +use perry_runtime::{ + js_array_get_jsvalue, js_array_length, js_promise_new_cross_thread, JSValue, Promise, +}; use sqlx::postgres::PgConnection; use sqlx::{Connection, Row}; @@ -75,7 +77,7 @@ pub unsafe extern "C" fn js_pg_client_new(config_f: f64) -> Handle { pub unsafe extern "C" fn js_pg_client_connect(client_handle: Handle) -> *mut Promise { use crate::common::get_handle_mut; - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Snapshot the pending config out of the handle BEFORE entering the // async block — `get_handle_mut` returns a `&mut` that we can't keep @@ -122,7 +124,7 @@ pub unsafe extern "C" fn js_pg_connect(config_f: f64) -> *mut Promise { // Take f64 at the FFI boundary to avoid SysV AMD64 ABI mismatch // (see js_mysql2_create_pool for details). let config = JSValue::from_bits(config_f.to_bits()); - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Parse the config let pg_config = parse_pg_config(config); @@ -148,7 +150,7 @@ pub unsafe extern "C" fn js_pg_connect(config_f: f64) -> *mut Promise { /// Closes the PostgreSQL connection. #[no_mangle] pub unsafe extern "C" fn js_pg_client_end(client_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::take_handle; @@ -178,7 +180,7 @@ pub unsafe extern "C" fn js_pg_client_query( client_handle: Handle, sql_ptr: *const u8, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { @@ -344,7 +346,7 @@ pub unsafe extern "C" fn js_pg_client_query_params( sql_ptr: *const u8, params: JSValue, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let sql = if sql_ptr.is_null() { String::new() diff --git a/crates/perry-stdlib/src/pg/pool.rs b/crates/perry-stdlib/src/pg/pool.rs index cbb50dcfac..080bf413f6 100644 --- a/crates/perry-stdlib/src/pg/pool.rs +++ b/crates/perry-stdlib/src/pg/pool.rs @@ -1,6 +1,6 @@ //! PostgreSQL connection pool implementation -use perry_runtime::{js_promise_new, JSValue, Promise}; +use perry_runtime::{js_promise_new_cross_thread, JSValue, Promise}; use sqlx::postgres::{PgPool, PgPoolOptions}; use sqlx::Row; @@ -87,7 +87,7 @@ pub unsafe extern "C" fn js_pg_create_pool(config_f: f64) -> *mut Promise { // Take f64 at the FFI boundary to avoid SysV AMD64 ABI mismatch // (see js_mysql2_create_pool for details). let config = JSValue::from_bits(config_f.to_bits()); - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Parse the config let pg_config = parse_pg_config(config); @@ -119,7 +119,7 @@ pub unsafe extern "C" fn js_pg_create_pool(config_f: f64) -> *mut Promise { /// Executes a query on the pool. #[no_mangle] pub unsafe extern "C" fn js_pg_pool_query(pool_handle: Handle, sql_ptr: *const u8) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); // Extract the SQL string let sql = if sql_ptr.is_null() { @@ -176,7 +176,7 @@ pub unsafe extern "C" fn js_pg_pool_query(pool_handle: Handle, sql_ptr: *const u /// Closes all connections in the pool. #[no_mangle] pub unsafe extern "C" fn js_pg_pool_end(pool_handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); crate::common::spawn_for_promise(promise as *mut u8, async move { use crate::common::take_handle; diff --git a/crates/perry-stdlib/src/sharp.rs b/crates/perry-stdlib/src/sharp.rs index 5883cdeff1..7ad08976eb 100644 --- a/crates/perry-stdlib/src/sharp.rs +++ b/crates/perry-stdlib/src/sharp.rs @@ -8,7 +8,9 @@ use crate::common::{ string_from_header_lossy as string_from_header, Handle, }; use image::{imageops::FilterType, DynamicImage, GenericImageView, ImageFormat}; -use perry_runtime::{js_promise_new, js_string_from_bytes, JSValue, Promise, StringHeader}; +use perry_runtime::{ + js_promise_new_cross_thread, js_string_from_bytes, JSValue, Promise, StringHeader, +}; use std::io::Cursor; /// Sharp image handle with pending operations @@ -269,7 +271,7 @@ pub unsafe extern "C" fn js_sharp_to_file( handle: Handle, path_ptr: *const StringHeader, ) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); let path = match string_from_header(path_ptr) { Some(p) => p, @@ -317,7 +319,7 @@ pub unsafe extern "C" fn js_sharp_to_file( /// Get the image as a buffer. #[no_mangle] pub unsafe extern "C" fn js_sharp_to_buffer(handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); spawn_for_promise(promise as *mut u8, async move { if let Some(sharp) = get_handle::(handle) { @@ -346,7 +348,7 @@ pub unsafe extern "C" fn js_sharp_to_buffer(handle: Handle) -> *mut Promise { /// Get image metadata. #[no_mangle] pub unsafe extern "C" fn js_sharp_metadata(handle: Handle) -> *mut Promise { - let promise = js_promise_new(); + let promise = js_promise_new_cross_thread(); spawn_for_promise(promise as *mut u8, async move { if let Some(sharp) = get_handle::(handle) { diff --git a/crates/perry-stdlib/src/worker_threads/async_shim.rs b/crates/perry-stdlib/src/worker_threads/async_shim.rs index 76475252ff..dca5fc9926 100644 --- a/crates/perry-stdlib/src/worker_threads/async_shim.rs +++ b/crates/perry-stdlib/src/worker_threads/async_shim.rs @@ -28,7 +28,7 @@ //! The pinning `js_promise_new_for_native_resolution` performs is likewise a //! consequence of deferral — it keeps the promise alive across the window //! between creation and the pump's resolution — and an inline settle spans no -//! collection point, so a plain `js_promise_new` is the correct counterpart. +//! collection point, so a plain `js_promise_new_cross_thread` is the correct counterpart. #[cfg(feature = "async-runtime")] pub(crate) use crate::common::async_bridge::{ @@ -46,7 +46,7 @@ mod inline { /// /// No pinning: pinning guards the deferral window, and there is none here. pub(crate) unsafe fn js_promise_new_for_native_resolution() -> *mut perry_runtime::Promise { - perry_runtime::js_promise_new() + perry_runtime::js_promise_new_cross_thread() } /// Settle now rather than queueing for a pump that does not exist. diff --git a/crates/perry-stdlib/src/ws.rs b/crates/perry-stdlib/src/ws.rs index 5d4ba3699d..9a26c793f2 100644 --- a/crates/perry-stdlib/src/ws.rs +++ b/crates/perry-stdlib/src/ws.rs @@ -241,7 +241,7 @@ pub unsafe extern "C" fn js_ws_connect( } __android_log_print(3, b"PerryWS\0".as_ptr(), b"js_ws_connect called\0".as_ptr()); } - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let url = match string_from_header(url_ptr) { @@ -657,7 +657,7 @@ pub unsafe extern "C" fn js_ws_connect_start(url_nanboxed: f64) -> f64 { pub unsafe extern "C" fn js_ws_connect( url_ptr: *const StringHeader, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let handle = perry_native_ws_connect(url_ptr as *const u8); let result_bits = handle.to_bits(); // Resolve immediately with the handle (connection happens async in native) @@ -858,7 +858,7 @@ pub unsafe extern "C" fn js_ws_wait_for_message( handle: i64, timeout_ms: f64, ) -> *mut perry_runtime::Promise { - let promise = perry_runtime::js_promise_new(); + let promise = perry_runtime::js_promise_new_cross_thread(); let promise_ptr = promise as usize; let ws_id = handle as usize; let timeout = std::time::Duration::from_millis(timeout_ms as u64); diff --git a/crates/perry/tests/static_method_object_literal.rs b/crates/perry/tests/static_method_object_literal.rs index 453ba9d593..b20c40b4cd 100644 --- a/crates/perry/tests/static_method_object_literal.rs +++ b/crates/perry/tests/static_method_object_literal.rs @@ -8,6 +8,29 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Output}; use std::sync::Once; +/// Keep the forced-moving arm independent of ambient developer/CI settings. +/// Some inputs affect code generation, so normalize the runtime build, +/// fixture compile, and child process alike. +const GC_ENV_OVERRIDES: &[&str] = &[ + "PERRY_GEN_GC", + "PERRY_GC_SCAVENGE", + "PERRY_GC_SCAVENGE_NURSERY_MB", + "PERRY_GC_MOVING_SAFEPOINT", + "PERRY_GC_MOVING_LOOP_POLLS", + "PERRY_GC_FORCE_EVACUATE", + "PERRY_GC_VERIFY_EVACUATION", + "PERRY_CONSERVATIVE_STACK_SCAN", + "PERRY_WRITE_BARRIERS", + "PERRY_GC_INCREMENTAL", + "PERRY_GC_HEAP_LIMIT", +]; + +fn remove_gc_env_overrides(command: &mut Command) { + for key in GC_ENV_OVERRIDES { + command.env_remove(key); + } +} + fn perry_bin() -> PathBuf { PathBuf::from(env!("CARGO_BIN_EXE_perry")) } @@ -25,6 +48,7 @@ fn runtime_dir() -> PathBuf { let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into()); let mut command = Command::new(cargo); command.current_dir(workspace_root()).arg("build"); + remove_gc_env_overrides(&mut command); if !cfg!(debug_assertions) { command.arg("--release"); } @@ -48,14 +72,11 @@ fn runtime_dir() -> PathBuf { fn run_fixture(binary: &Path, force_evacuation: bool) -> Output { let mut command = Command::new(binary); + remove_gc_env_overrides(&mut command); if force_evacuation { command .env("PERRY_GC_FORCE_EVACUATE", "1") .env("PERRY_GC_VERIFY_EVACUATION", "1"); - } else { - command - .env_remove("PERRY_GC_FORCE_EVACUATE") - .env_remove("PERRY_GC_VERIFY_EVACUATION"); } command.output().expect("run method-literal fixture") } @@ -108,7 +129,8 @@ console.log( ) .expect("write method-literal fixture"); - let compile = Command::new(perry_bin()) + let mut compile_command = Command::new(perry_bin()); + compile_command .current_dir(dir.path()) .arg("compile") .arg(&entry) @@ -116,7 +138,9 @@ console.log( .arg(&binary) .arg("--no-cache") .arg("--no-auto-optimize") - .env("PERRY_RUNTIME_DIR", runtime_dir()) + .env("PERRY_RUNTIME_DIR", runtime_dir()); + remove_gc_env_overrides(&mut compile_command); + let compile = compile_command .output() .expect("compile method-literal fixture"); assert!( diff --git a/scripts/raw_handle_debt_baseline.txt b/scripts/raw_handle_debt_baseline.txt index d6904a94ba..175df711d1 100644 --- a/scripts/raw_handle_debt_baseline.txt +++ b/scripts/raw_handle_debt_baseline.txt @@ -1 +1 @@ -913 +944 diff --git a/scripts/raw_handle_debt_files.txt b/scripts/raw_handle_debt_files.txt index 3825d7b805..9af4855dd7 100644 --- a/scripts/raw_handle_debt_files.txt +++ b/scripts/raw_handle_debt_files.txt @@ -50,6 +50,7 @@ 5 crates/perry-runtime/src/array/header.rs 6 crates/perry-runtime/src/array/indexing.rs 2 crates/perry-runtime/src/array/iter_methods.rs +31 crates/perry-runtime/src/array/iterator.rs 4 crates/perry-runtime/src/array/push_pop.rs 14 crates/perry-runtime/src/array/sort.rs 13 crates/perry-runtime/src/async_hooks.rs