Skip to content

Commit a40c012

Browse files
authored
Merge pull request #979 from neon-bindings/kv/unnecessary-send
Relax Send constraints
2 parents a4c17d8 + 36a57a3 commit a40c012

File tree

10 files changed

+107
-40
lines changed

10 files changed

+107
-40
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/neon/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ neon-macros = { version = "=1.0.0-alpha.2", path = "../neon-macros" }
3131
aquamarine = { version = "0.1.11", optional = true }
3232
easy-cast = { version = "0.5.1", optional = true }
3333
doc-comment = { version = "0.3.3", optional = true }
34+
send_wrapper = "0.6"
3435

3536
[dependencies.tokio]
3637
version = "1.24.2"

crates/neon/src/context/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ pub trait Context<'a>: ContextInternal<'a> {
423423
/// Ok(point)
424424
/// }
425425
/// ```
426-
fn boxed<U: Finalize + Send + 'static>(&mut self, v: U) -> Handle<'a, JsBox<U>> {
426+
fn boxed<U: Finalize + 'static>(&mut self, v: U) -> Handle<'a, JsBox<U>> {
427427
JsBox::new(self, v)
428428
}
429429

crates/neon/src/event/task.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ where
4848
/// of the `execute` callback
4949
pub fn and_then<F>(self, complete: F)
5050
where
51-
F: FnOnce(TaskContext, O) -> NeonResult<()> + Send + 'static,
51+
F: FnOnce(TaskContext, O) -> NeonResult<()> + 'static,
5252
{
5353
let env = self.cx.env();
5454
let execute = self.execute;
@@ -65,7 +65,7 @@ where
6565
pub fn promise<V, F>(self, complete: F) -> Handle<'a, JsPromise>
6666
where
6767
V: Value,
68-
F: FnOnce(TaskContext, O) -> JsResult<V> + Send + 'static,
68+
F: FnOnce(TaskContext, O) -> JsResult<V> + 'static,
6969
{
7070
let env = self.cx.env();
7171
let (deferred, promise) = JsPromise::new(self.cx);
@@ -82,7 +82,7 @@ fn schedule<I, O, D>(env: Env, input: I, data: D)
8282
where
8383
I: FnOnce() -> O + Send + 'static,
8484
O: Send + 'static,
85-
D: FnOnce(TaskContext, O) -> NeonResult<()> + Send + 'static,
85+
D: FnOnce(TaskContext, O) -> NeonResult<()> + 'static,
8686
{
8787
unsafe {
8888
async_work::schedule(env.to_raw(), input, execute::<I, O>, complete::<O, D>, data);
@@ -100,7 +100,7 @@ where
100100
fn complete<O, D>(env: raw::Env, output: thread::Result<O>, callback: D)
101101
where
102102
O: Send + 'static,
103-
D: FnOnce(TaskContext, O) -> NeonResult<()> + Send + 'static,
103+
D: FnOnce(TaskContext, O) -> NeonResult<()> + 'static,
104104
{
105105
let output = output.unwrap_or_else(|panic| {
106106
// If a panic was caught while executing the task on the Node Worker
@@ -118,7 +118,7 @@ fn schedule_promise<I, O, D, V>(env: Env, input: I, complete: D, deferred: Defer
118118
where
119119
I: FnOnce() -> O + Send + 'static,
120120
O: Send + 'static,
121-
D: FnOnce(TaskContext, O) -> JsResult<V> + Send + 'static,
121+
D: FnOnce(TaskContext, O) -> JsResult<V> + 'static,
122122
V: Value,
123123
{
124124
unsafe {
@@ -138,7 +138,7 @@ fn complete_promise<O, D, V>(
138138
(complete, deferred): (D, Deferred),
139139
) where
140140
O: Send + 'static,
141-
D: FnOnce(TaskContext, O) -> JsResult<V> + Send + 'static,
141+
D: FnOnce(TaskContext, O) -> JsResult<V> + 'static,
142142
V: Value,
143143
{
144144
let env = env.into();

crates/neon/src/sys/async_work.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use std::{
1414
ptr, thread,
1515
};
1616

17-
use super::{bindings as napi, no_panic::FailureBoundary, raw::Env};
17+
use super::{
18+
bindings as napi, debug_send_wrapper::DebugSendWrapper, no_panic::FailureBoundary, raw::Env,
19+
};
1820

1921
const BOUNDARY: FailureBoundary = FailureBoundary {
2022
both: "A panic and exception occurred while executing a `neon::event::TaskBuilder` task",
@@ -40,13 +42,13 @@ pub unsafe fn schedule<I, O, D>(
4042
) where
4143
I: Send + 'static,
4244
O: Send + 'static,
43-
D: Send + 'static,
45+
D: 'static,
4446
{
4547
let mut data = Box::new(Data {
4648
state: State::Input(input),
4749
execute,
4850
complete,
49-
data,
51+
data: DebugSendWrapper::new(data),
5052
// Work is initialized as a null pointer, but set by `create_async_work`
5153
// `data` must not be used until this value has been set.
5254
work: ptr::null_mut(),
@@ -85,7 +87,7 @@ struct Data<I, O, D> {
8587
state: State<I, O>,
8688
execute: Execute<I, O>,
8789
complete: Complete<O, D>,
88-
data: D,
90+
data: DebugSendWrapper<D>,
8991
work: napi::AsyncWork,
9092
}
9193

@@ -170,7 +172,7 @@ unsafe extern "C" fn call_complete<I, O, D>(env: Env, status: napi::Status, data
170172
};
171173

172174
match status {
173-
napi::Status::Ok => complete(env, output, data),
175+
napi::Status::Ok => complete(env, output, data.take()),
174176
napi::Status::Cancelled => {}
175177
_ => assert_eq!(status, napi::Status::Ok),
176178
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//! Wrapper that ensures types are always used from the same thread
2+
//! in debug builds. It is a zero-cost in release builds.
3+
4+
pub(super) use wrapper::DebugSendWrapper;
5+
6+
#[cfg(debug_assertions)]
7+
mod wrapper {
8+
use std::ops::Deref;
9+
10+
#[repr(transparent)]
11+
pub struct DebugSendWrapper<T>(send_wrapper::SendWrapper<T>);
12+
13+
impl<T> DebugSendWrapper<T> {
14+
pub fn new(value: T) -> Self {
15+
Self(send_wrapper::SendWrapper::new(value))
16+
}
17+
18+
pub fn take(self) -> T {
19+
self.0.take()
20+
}
21+
}
22+
23+
impl<T> Deref for DebugSendWrapper<T> {
24+
type Target = T;
25+
26+
fn deref(&self) -> &Self::Target {
27+
&self.0
28+
}
29+
}
30+
}
31+
32+
#[cfg(not(debug_assertions))]
33+
mod wrapper {
34+
use std::ops::Deref;
35+
36+
#[repr(transparent)]
37+
pub struct DebugSendWrapper<T>(T);
38+
39+
impl<T> DebugSendWrapper<T> {
40+
pub fn new(value: T) -> Self {
41+
Self(value)
42+
}
43+
44+
pub fn take(self) -> T {
45+
self.0
46+
}
47+
}
48+
49+
impl<T> Deref for DebugSendWrapper<T> {
50+
type Target = T;
51+
52+
fn deref(&self) -> &Self::Target {
53+
&self.0
54+
}
55+
}
56+
}

crates/neon/src/sys/external.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ use std::mem::MaybeUninit;
22

33
use super::{
44
bindings as napi,
5+
debug_send_wrapper::DebugSendWrapper,
56
raw::{Env, Local},
67
};
78

89
/// `finalize_external` is invoked immediately before a `napi_external` is garbage collected
9-
extern "C" fn finalize_external<T: Send + 'static>(
10+
extern "C" fn finalize_external<T: 'static>(
1011
env: Env,
1112
// Raw pointer to a `Box<T>` stored by a `napi_external`
1213
data: *mut std::ffi::c_void,
@@ -15,10 +16,10 @@ extern "C" fn finalize_external<T: Send + 'static>(
1516
hint: *mut std::ffi::c_void,
1617
) {
1718
unsafe {
18-
let data = Box::<T>::from_raw(data as *mut _);
19+
let data = Box::<DebugSendWrapper<T>>::from_raw(data as *mut _);
1920
let finalizer: fn(Env, T) = std::mem::transmute(hint as *const ());
2021

21-
finalizer(env, *data);
22+
finalizer(env, data.take());
2223
}
2324
}
2425

@@ -27,7 +28,7 @@ extern "C" fn finalize_external<T: Send + 'static>(
2728
/// module. Calling `deref` with an external created by another native module,
2829
/// even another neon module, is undefined behavior.
2930
/// <https://github.com/neon-bindings/neon/issues/591>
30-
pub unsafe fn deref<T: Send + 'static>(env: Env, local: Local) -> Option<*const T> {
31+
pub unsafe fn deref<T: 'static>(env: Env, local: Local) -> Option<*const T> {
3132
let mut result = MaybeUninit::uninit();
3233
let status = napi::typeof_value(env, local, result.as_mut_ptr());
3334

@@ -53,12 +54,15 @@ pub unsafe fn deref<T: Send + 'static>(env: Env, local: Local) -> Option<*const
5354

5455
assert_eq!(status, napi::Status::Ok);
5556

56-
Some(result.assume_init() as *const _)
57+
let v = result.assume_init();
58+
let v = &**v.cast_const().cast::<DebugSendWrapper<T>>() as *const T;
59+
60+
Some(v)
5761
}
5862

5963
/// Creates a `napi_external` from a Rust type
60-
pub unsafe fn create<T: Send + 'static>(env: Env, v: T, finalizer: fn(Env, T)) -> Local {
61-
let v = Box::new(v);
64+
pub unsafe fn create<T: 'static>(env: Env, v: T, finalizer: fn(Env, T)) -> Local {
65+
let v = Box::new(DebugSendWrapper::new(v));
6266
let mut result = MaybeUninit::uninit();
6367

6468
let status = napi::create_external(

crates/neon/src/sys/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ pub(crate) mod tsfn;
100100
#[cfg(feature = "napi-5")]
101101
pub(crate) mod date;
102102

103+
mod debug_send_wrapper;
103104
#[cfg(feature = "napi-6")]
104105
pub(crate) mod lifecycle;
105106

crates/neon/src/sys/no_panic.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use std::{
1818

1919
use super::{
2020
bindings as napi,
21+
debug_send_wrapper::DebugSendWrapper,
2122
error::fatal_error,
2223
raw::{Env, Local},
2324
};
@@ -256,7 +257,7 @@ unsafe fn external_from_panic(env: Env, panic: Panic) -> Local {
256257
let mut result = MaybeUninit::uninit();
257258
let status = napi::create_external(
258259
env,
259-
Box::into_raw(Box::new(panic)).cast(),
260+
Box::into_raw(Box::new(DebugSendWrapper::new(panic))).cast(),
260261
Some(finalize_panic),
261262
ptr::null_mut(),
262263
result.as_mut_ptr(),

crates/neon/src/types_impl/boxed.rs

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ use crate::{
1111
types::{boxed::private::JsBoxInner, private::ValueInternal, Value},
1212
};
1313

14-
type BoxAny = Box<dyn Any + Send + 'static>;
14+
type BoxAny = Box<dyn Any + 'static>;
1515

1616
mod private {
17-
pub struct JsBoxInner<T: Send + 'static> {
17+
pub struct JsBoxInner<T: 'static> {
1818
pub(super) local: crate::sys::raw::Local,
1919
// Cached raw pointer to the data contained in the `JsBox`. This value is
2020
// required to implement `Deref` for `JsBox`. Unlike most `Js` types, `JsBox`
@@ -141,15 +141,15 @@ mod private {
141141
/// Ok(cx.string(greeting))
142142
/// }
143143
#[repr(transparent)]
144-
pub struct JsBox<T: Send + 'static>(JsBoxInner<T>);
144+
pub struct JsBox<T: 'static>(JsBoxInner<T>);
145145

146-
impl<T: Send + 'static> std::fmt::Debug for JsBoxInner<T> {
146+
impl<T: 'static> std::fmt::Debug for JsBoxInner<T> {
147147
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148148
write!(f, "JsBox<{}>", std::any::type_name::<T>())
149149
}
150150
}
151151

152-
impl<T: Send + 'static> std::fmt::Debug for JsBox<T> {
152+
impl<T: 'static> std::fmt::Debug for JsBox<T> {
153153
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154154
std::fmt::Debug::fmt(&self.0, f)
155155
}
@@ -162,7 +162,7 @@ unsafe fn maybe_external_deref<'a>(env: Env, local: raw::Local) -> Option<&'a Bo
162162
}
163163

164164
// Custom `Clone` implementation since `T` might not be `Clone`
165-
impl<T: Send + 'static> Clone for JsBoxInner<T> {
165+
impl<T: 'static> Clone for JsBoxInner<T> {
166166
fn clone(&self) -> Self {
167167
Self {
168168
local: self.local,
@@ -171,21 +171,21 @@ impl<T: Send + 'static> Clone for JsBoxInner<T> {
171171
}
172172
}
173173

174-
impl<T: Send + 'static> Object for JsBox<T> {}
174+
impl<T: 'static> Object for JsBox<T> {}
175175

176-
impl<T: Send + 'static> Copy for JsBoxInner<T> {}
176+
impl<T: 'static> Copy for JsBoxInner<T> {}
177177

178-
impl<T: Send + 'static> Value for JsBox<T> {}
178+
impl<T: 'static> Value for JsBox<T> {}
179179

180-
unsafe impl<T: Send + 'static> TransparentNoCopyWrapper for JsBox<T> {
180+
unsafe impl<T: 'static> TransparentNoCopyWrapper for JsBox<T> {
181181
type Inner = JsBoxInner<T>;
182182

183183
fn into_inner(self) -> Self::Inner {
184184
self.0
185185
}
186186
}
187187

188-
impl<T: Send + 'static> ValueInternal for JsBox<T> {
188+
impl<T: 'static> ValueInternal for JsBox<T> {
189189
fn name() -> String {
190190
any::type_name::<Self>().to_string()
191191
}
@@ -219,30 +219,25 @@ impl<T: Send + 'static> ValueInternal for JsBox<T> {
219219
}
220220
}
221221

222-
/// Values contained by a `JsBox` must be `Finalize + Send + 'static`
222+
/// Values contained by a `JsBox` must be `Finalize + 'static`
223223
///
224224
/// ### `Finalize`
225225
///
226226
/// The `sys::prelude::Finalize` trait provides a `finalize` method that will be called
227227
/// immediately before the `JsBox` is garbage collected.
228228
///
229-
/// ### `Send`
230-
///
231-
/// `JsBox` may be moved across threads. It is important to guarantee that the
232-
/// contents is also safe to move across threads.
233-
///
234229
/// ### `'static'
235230
///
236231
/// The lifetime of a `JsBox` is managed by the JavaScript garbage collector. Since Rust
237232
/// is unable to verify the lifetime of the contents, references must be valid for the
238233
/// entire duration of the program. This does not mean that the `JsBox` will be valid
239234
/// until the application terminates, only that its lifetime is indefinite.
240-
impl<T: Finalize + Send + 'static> JsBox<T> {
235+
impl<T: Finalize + 'static> JsBox<T> {
241236
/// Constructs a new `JsBox` containing `value`.
242237
pub fn new<'a, C>(cx: &mut C, value: T) -> Handle<'a, JsBox<T>>
243238
where
244239
C: Context<'a>,
245-
T: Send + 'static,
240+
T: 'static,
246241
{
247242
// This function will execute immediately before the `JsBox` is garbage collected.
248243
// It unwraps the `napi_external`, downcasts the `BoxAny` and moves the type
@@ -264,7 +259,7 @@ impl<T: Finalize + Send + 'static> JsBox<T> {
264259
}
265260
}
266261

267-
impl<T: Send + 'static> Deref for JsBox<T> {
262+
impl<T: 'static> Deref for JsBox<T> {
268263
type Target = T;
269264

270265
fn deref(&self) -> &Self::Target {

0 commit comments

Comments
 (0)