Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed iterators to use correct IteratorPrototype chain
- Fixed a latent ABI layout vulnerability in `JS_NewPromiseCapability` FFI boundary by replacing tuple with strictly compatible array
- `#[rquickjs::class]` now rejects fields whose type implements `JsClass` with a clear compile error; such fields silently dropped nested mutations because the generated getter cloned the value. Wrap the field in `Class<'js, T>` instead #[532](https://github.com/DelSkayn/rquickjs/issues/532)
- Made `AsyncRuntime`, `AsyncContext`, and `WithFuture` unconditionally `Send`/`Sync` so `async_with` works with multi-threaded executors without requiring the `parallel` feature #[495](https://github.com/DelSkayn/rquickjs/issues/495)

## [0.11.0] - 2025-12-16

Expand Down
7 changes: 0 additions & 7 deletions core/src/context/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,13 +241,6 @@ impl AsyncContext {
}
}

// Since the reference to runtime is behind a Arc this object is send
#[cfg(feature = "parallel")]
unsafe impl Send for AsyncContext {}

// Since all functions lock the global runtime lock access is synchronized so
// this object is sync
#[cfg(feature = "parallel")]
unsafe impl Sync for AsyncContext {}

#[cfg(test)]
Expand Down
1 change: 0 additions & 1 deletion core/src/context/async/future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,4 @@ where
}
}

#[cfg(feature = "parallel")]
unsafe impl<F, R> Send for WithFuture<'_, F, R> {}
1 change: 0 additions & 1 deletion core/src/context/owner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ pub(crate) trait DropContext: Clone {
unsafe fn drop_context(&self, ctx: NonNull<qjs::JSContext>);
}

#[cfg(feature = "parallel")]
unsafe impl<R: Send + DropContext> Send for ContextOwner<R> {}

/// Struct in charge of dropping contexts when they go out of scope
Expand Down
23 changes: 10 additions & 13 deletions core/src/runtime/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ impl Drop for InnerRuntime {
}
}

#[cfg(feature = "parallel")]
unsafe impl Send for InnerRuntime {}
unsafe impl Sync for InnerRuntime {}

/// A weak handle to the async runtime.
///
Expand Down Expand Up @@ -83,17 +83,13 @@ pub struct AsyncRuntime {

// Since all functions which use runtime are behind a mutex
// sending the runtime to other threads should be fine.
#[cfg(feature = "parallel")]
unsafe impl Send for AsyncRuntime {}
#[cfg(feature = "parallel")]
unsafe impl Send for AsyncWeakRuntime {}

// Since a global lock needs to be locked for safe use
// using runtime in a sync way should be safe as
// simultaneous accesses is synchronized behind a lock.
#[cfg(feature = "parallel")]
unsafe impl Sync for AsyncRuntime {}
#[cfg(feature = "parallel")]
unsafe impl Sync for AsyncWeakRuntime {}

impl AsyncRuntime {
Expand Down Expand Up @@ -640,27 +636,28 @@ mod test {
rt.idle().await;
});

#[cfg(feature = "parallel")]
fn assert_is_send<T: Send>(t: T) -> T {
t
}

#[cfg(feature = "parallel")]
fn assert_is_sync<T: Send>(t: T) -> T {
fn assert_is_sync<T: Sync>(t: T) -> T {
t
}

#[cfg(feature = "parallel")]
#[tokio::test]
async fn ensure_types_are_send_sync() {
let rt = AsyncRuntime::new().unwrap();

std::mem::drop(assert_is_sync(rt.idle()));
std::mem::drop(assert_is_sync(rt.execute_pending_job()));
std::mem::drop(assert_is_sync(rt.drive()));

std::mem::drop(assert_is_send(rt.idle()));
std::mem::drop(assert_is_send(rt.execute_pending_job()));
std::mem::drop(assert_is_send(rt.drive()));

std::mem::drop(assert_is_send(rt.clone()));
std::mem::drop(assert_is_sync(rt.clone()));

let ctx = crate::AsyncContext::full(&rt).await.unwrap();
std::mem::drop(assert_is_send(ctx.clone()));
std::mem::drop(assert_is_sync(ctx.clone()));
std::mem::drop(assert_is_send(ctx.async_with(async |_| {})));
}
}
Loading