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

Commit 9b0d063

Browse files
author
bors-servo
authored
Auto merge of #487 - jdm:shutdown, r=asajeffrey
Prevent initializing and shutting down SpiderMonkey from different threads Based on [this comment](https://github.com/servo/mozjs/blob/e21c05b415dfc246175ff8d5fc48b0e8c5b4e9e9/mozjs/js/public/Initialization.h#L19-L23), it appears that JS_Init and JS_ShutDown need to be called on the same thread. These changes make JSEngine a non-sendable type, and add JSEngineHandle as a sendable type that allows verifying that all outstanding consumers of the engine are dropped before shutting down.
2 parents 57ac9ee + c8c1453 commit 9b0d063

15 files changed

+60
-21
lines changed

src/rust.rs

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use std::os::raw::c_void;
2323
use std::cell::Cell;
2424
use std::marker::PhantomData;
2525
use std::sync::{Arc, Mutex};
26+
use std::sync::atomic::{AtomicU32, Ordering};
2627

2728
use consts::{JSCLASS_RESERVED_SLOTS_MASK, JSCLASS_GLOBAL_SLOT_COUNT};
2829
use consts::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
@@ -172,11 +173,31 @@ pub enum JSEngineError {
172173
/// A handle that must be kept alive in order to create new Runtimes.
173174
/// When this handle is dropped, the engine is shut down and cannot
174175
/// be reinitialized.
175-
pub struct JSEngine(());
176+
pub struct JSEngine {
177+
/// The count of alive handles derived from this initialized instance.
178+
outstanding_handles: Arc<AtomicU32>,
179+
// Ensure this type cannot be sent between threads.
180+
marker: PhantomData<*mut ()>,
181+
}
182+
183+
pub struct JSEngineHandle(Arc<AtomicU32>);
184+
185+
impl Clone for JSEngineHandle {
186+
fn clone(&self) -> JSEngineHandle {
187+
self.0.fetch_add(1, Ordering::SeqCst);
188+
JSEngineHandle(self.0.clone())
189+
}
190+
}
191+
192+
impl Drop for JSEngineHandle {
193+
fn drop(&mut self) {
194+
self.0.fetch_sub(1, Ordering::SeqCst);
195+
}
196+
}
176197

177198
impl JSEngine {
178199
/// Initialize the JS engine to prepare for creating new JS runtimes.
179-
pub fn init() -> Result<Arc<JSEngine>, JSEngineError> {
200+
pub fn init() -> Result<JSEngine, JSEngineError> {
180201
let mut state = ENGINE_STATE.lock().unwrap();
181202
match *state {
182203
EngineState::Initialized => return Err(JSEngineError::AlreadyInitialized),
@@ -189,9 +210,22 @@ impl JSEngine {
189210
Err(JSEngineError::InitFailed)
190211
} else {
191212
*state = EngineState::Initialized;
192-
Ok(Arc::new(JSEngine(())))
213+
Ok(JSEngine {
214+
outstanding_handles: Arc::new(AtomicU32::new(0)),
215+
marker: PhantomData,
216+
})
193217
}
194218
}
219+
220+
pub fn can_shutdown(&self) -> bool {
221+
self.outstanding_handles.load(Ordering::SeqCst) == 0
222+
}
223+
224+
/// Create a handle to this engine.
225+
pub fn handle(&self) -> JSEngineHandle {
226+
self.outstanding_handles.fetch_add(1, Ordering::SeqCst);
227+
JSEngineHandle(self.outstanding_handles.clone())
228+
}
195229
}
196230

197231
/// Shut down the JS engine, invalidating any existing runtimes and preventing
@@ -200,6 +234,11 @@ impl Drop for JSEngine {
200234
fn drop(&mut self) {
201235
let mut state = ENGINE_STATE.lock().unwrap();
202236
if *state == EngineState::Initialized {
237+
assert_eq!(
238+
self.outstanding_handles.load(Ordering::SeqCst),
239+
0,
240+
"There are outstanding JS engine handles"
241+
);
203242
*state = EngineState::ShutDown;
204243
unsafe {
205244
JS_ShutDown();
@@ -215,7 +254,7 @@ pub struct ParentRuntime {
215254
/// Raw pointer to the underlying SpiderMonkey runtime.
216255
parent: *mut JSRuntime,
217256
/// Handle to ensure the JS engine remains running while this handle exists.
218-
engine: Arc<JSEngine>,
257+
engine: JSEngineHandle,
219258
/// The number of children of the runtime that created this ParentRuntime value.
220259
children_of_parent: Arc<()>,
221260
}
@@ -226,7 +265,7 @@ pub struct Runtime {
226265
/// Raw pointer to the underlying SpiderMonkey context.
227266
cx: *mut JSContext,
228267
/// The engine that this runtime is associated with.
229-
engine: Arc<JSEngine>,
268+
engine: JSEngineHandle,
230269
/// If this Runtime was created with a parent, this member exists to ensure
231270
/// that that parent's count of outstanding children (see [outstanding_children])
232271
/// remains accurate and will be automatically decreased when this Runtime value
@@ -251,7 +290,7 @@ impl Runtime {
251290
}
252291

253292
/// Creates a new `JSContext`.
254-
pub fn new(engine: Arc<JSEngine>) -> Runtime {
293+
pub fn new(engine: JSEngineHandle) -> Runtime {
255294
unsafe { Self::create(engine, None) }
256295
}
257296

@@ -279,7 +318,7 @@ impl Runtime {
279318
Self::create(parent.engine.clone(), Some(parent))
280319
}
281320

282-
unsafe fn create(engine: Arc<JSEngine>, parent: Option<ParentRuntime>) -> Runtime {
321+
unsafe fn create(engine: JSEngineHandle, parent: Option<ParentRuntime>) -> Runtime {
283322
let parent_runtime = parent.as_ref().map_or(
284323
ptr::null_mut(),
285324
|r| r.parent,

tests/callback.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use std::str;
2525
#[test]
2626
fn callback() {
2727
let engine = JSEngine::init().unwrap();
28-
let runtime = Runtime::new(engine);
28+
let runtime = Runtime::new(engine.handle());
2929
let context = runtime.cx();
3030
let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;
3131
let c_option = RealmOptions::default();

tests/capture_stack.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use std::ptr;
2222
#[test]
2323
fn capture_stack() {
2424
let engine = JSEngine::init().unwrap();
25-
let runtime = Runtime::new(engine);
25+
let runtime = Runtime::new(engine.handle());
2626
let context = runtime.cx();
2727
let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;
2828
let c_option = RealmOptions::default();

tests/custom_auto_rooter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ unsafe impl CustomTrace for TraceCheck {
3434
#[test]
3535
fn virtual_trace_called() {
3636
let engine = JSEngine::init().unwrap();
37-
let rt = Runtime::new(engine);
37+
let rt = Runtime::new(engine.handle());
3838
let cx = rt.cx();
3939

4040
let mut rooter = CustomAutoRooter::new(TraceCheck::new());

tests/custom_auto_rooter_macro.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ unsafe impl CustomTrace for TraceCheck {
3131
#[test]
3232
fn custom_auto_rooter_macro() {
3333
let engine = JSEngine::init().unwrap();
34-
let rt = Runtime::new(engine);
34+
let rt = Runtime::new(engine.handle());
3535
let cx = rt.cx();
3636

3737
auto_root!(in(cx) let vec = vec![TraceCheck::new(), TraceCheck::new()]);

tests/enumerate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use std::ptr;
2323
#[test]
2424
fn enumerate() {
2525
let engine = JSEngine::init().unwrap();
26-
let rt = Runtime::new(engine);
26+
let rt = Runtime::new(engine.handle());
2727
let cx = rt.cx();
2828
let options = RealmOptions::default();
2929

tests/evaluate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::ptr;
1515
#[test]
1616
fn evaluate() {
1717
let engine = JSEngine::init().unwrap();
18-
let rt = Runtime::new(engine);
18+
let rt = Runtime::new(engine.handle());
1919
let cx = rt.cx();
2020

2121
unsafe {

tests/panic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::ptr;
2020
#[should_panic]
2121
fn test_panic() {
2222
let engine = JSEngine::init().unwrap();
23-
let runtime = Runtime::new(engine);
23+
let runtime = Runtime::new(engine.handle());
2424
let context = runtime.cx();
2525
let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;
2626
let c_option = RealmOptions::default();

tests/rooting.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use std::ptr;
2929
fn rooting() {
3030
unsafe {
3131
let engine = JSEngine::init().unwrap();
32-
let runtime = Runtime::new(engine);
32+
let runtime = Runtime::new(engine.handle());
3333
let cx = runtime.cx();
3434
JS_SetGCZeal(cx, 2, 1);
3535

tests/runtime.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use std::sync::mpsc::channel;
2424
fn runtime() {
2525
let engine = JSEngine::init().unwrap();
2626
assert!(JSEngine::init().is_err());
27-
let runtime = Runtime::new(engine);
27+
let runtime = Runtime::new(engine.handle());
2828
unsafe {
2929
let cx = runtime.cx();
3030
let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;

tests/runtime_no_outlive.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use mozjs::rust::{JSEngine, Runtime};
1010
#[should_panic]
1111
fn runtime() {
1212
let engine = JSEngine::init().unwrap();
13-
let runtime = Runtime::new(engine);
13+
let runtime = Runtime::new(engine.handle());
1414
let _parent = runtime.prepare_for_new_child();
1515
drop(runtime);
1616
}

tests/stack_limit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::ptr;
1515
#[test]
1616
fn stack_limit() {
1717
let engine = JSEngine::init().unwrap();
18-
let rt = Runtime::new(engine);
18+
let rt = Runtime::new(engine.handle());
1919
let cx = rt.cx();
2020

2121
unsafe {

tests/typedarray.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use std::ptr;
2121
#[test]
2222
fn typedarray() {
2323
let engine = JSEngine::init().unwrap();
24-
let rt = Runtime::new(engine);
24+
let rt = Runtime::new(engine.handle());
2525
let cx = rt.cx();
2626

2727
unsafe {

tests/typedarray_panic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::ptr;
2020
#[should_panic]
2121
fn typedarray_update_panic() {
2222
let engine = JSEngine::init().unwrap();
23-
let rt = Runtime::new(engine);
23+
let rt = Runtime::new(engine.handle());
2424
let cx = rt.cx();
2525
let options = RealmOptions::default();
2626

tests/vec_conversion.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use std::ptr;
2121
#[test]
2222
fn vec_conversion() {
2323
let engine = JSEngine::init().unwrap();
24-
let rt = Runtime::new(engine);
24+
let rt = Runtime::new(engine.handle());
2525
let cx = rt.cx();
2626

2727
let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;

0 commit comments

Comments
 (0)