Bug 1444081 - Shutdown the Parent JSRuntime instead of leaking it. r=fitzgen
authorNicolas B. Pierron <nicolas.b.pierron@gmail.com>
Thu, 08 Mar 2018 14:14:41 +0000
changeset 407220 018578db0224
parent 407219 4c664530b7e3
child 407221 5b2d608c1924
push id33596
push userncsoregi@mozilla.com
push date2018-03-09 00:18 +0000
treeherdermozilla-central@31a33fc61956 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfitzgen
bugs1444081
milestone60.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1444081 - Shutdown the Parent JSRuntime instead of leaking it. r=fitzgen
js/rust/src/rust.rs
--- a/js/rust/src/rust.rs
+++ b/js/rust/src/rust.rs
@@ -11,18 +11,19 @@ use std::char;
 use std::ffi;
 use std::ptr;
 use std::slice;
 use std::mem;
 use std::u32;
 use std::default::Default;
 use std::marker;
 use std::ops::{Deref, DerefMut};
-use std::sync::{Once, ONCE_INIT};
+use std::sync::{Once, ONCE_INIT, Arc, Mutex};
 use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
+use std::sync::mpsc::{SyncSender, sync_channel};
 use std::thread;
 use jsapi::root::*;
 use jsval::{self, UndefinedValue};
 use glue::{CreateAutoObjectVector, CreateCallArgsFromVp, AppendToAutoObjectVector, DeleteAutoObjectVector, IsDebugBuild};
 use glue::{CreateAutoIdVector, SliceAutoIdVector, DestroyAutoIdVector};
 use glue::{NewCompileOptions, DeleteCompileOptions};
 use panic;
 
@@ -60,16 +61,17 @@ impl ToResult for bool {
 // ___________________________________________________________________________
 // friendly Rustic API to runtimes
 
 thread_local!(static CONTEXT: Cell<*mut JSContext> = Cell::new(ptr::null_mut()));
 
 lazy_static! {
     static ref OUTSTANDING_RUNTIMES: AtomicUsize = AtomicUsize::new(0);
     static ref SHUT_DOWN: AtomicBool = AtomicBool::new(false);
+    static ref SHUT_DOWN_SIGNAL: Arc<Mutex<Option<SyncSender<()>>>> = Arc::new(Mutex::new(None));
 }
 
 /// A wrapper for the `JSContext` structure in SpiderMonkey.
 pub struct Runtime {
     cx: *mut JSContext,
 }
 
 impl Runtime {
@@ -96,17 +98,16 @@ impl Runtime {
 
         unsafe {
             #[derive(Debug)]
             struct Parent(UnsafeCell<*mut JSContext>);
             unsafe impl ::std::marker::Sync for Parent {}
 
             impl Parent {
                 fn set(&self, val: *mut JSContext) {
-                    assert!(self.get().is_null());
                     self.as_atomic().store(val, Ordering::SeqCst);
                     assert_eq!(self.get(), val);
                 }
 
                 fn get(&self) -> *mut JSContext {
                     self.as_atomic().load(Ordering::SeqCst)
                 }
 
@@ -120,33 +121,40 @@ impl Runtime {
             lazy_static! {
                 static ref PARENT: Parent = Parent(UnsafeCell::new(0 as *mut _));
             }
             static ONCE: Once = ONCE_INIT;
 
             ONCE.call_once(|| {
                 // There is a 1:1 relationship between threads and JSContexts,
                 // so we must spawn a new thread for the parent context.
+                let (tx, rx) = sync_channel(0);
+                *SHUT_DOWN_SIGNAL.lock().unwrap() = Some(tx);
                 let _ = thread::spawn(move || {
                     let is_debug_mozjs = cfg!(feature = "debugmozjs");
                     let diagnostic = JS::detail::InitWithFailureDiagnostic(is_debug_mozjs);
                     if !diagnostic.is_null() {
                         panic!("JS::detail::InitWithFailureDiagnostic failed: {}",
                                ffi::CStr::from_ptr(diagnostic).to_string_lossy());
                     }
 
                     let context = JS_NewContext(
                         DEFAULT_HEAPSIZE, ChunkSize as u32, ptr::null_mut());
                     assert!(!context.is_null());
                     JS::InitSelfHostedCode(context);
                     PARENT.set(context);
 
-                    loop {
-                        thread::sleep(::std::time::Duration::new(::std::u64::MAX, 0));
-                    }
+                    // The last JSRuntime child died, resume the execution by destroying the parent.
+                    rx.recv().unwrap();
+                    let cx = PARENT.get();
+                    JS_DestroyContext(cx);
+                    JS_ShutDown();
+                    PARENT.set(0 as *mut _);
+                    // Unblock the last child thread, waiting for the JS_ShutDown.
+                    rx.recv().unwrap();
                 });
 
                 while PARENT.get().is_null() {
                     thread::yield_now();
                 }
             });
 
             assert_eq!(IsDebugBuild(), cfg!(feature = "debugmozjs"));
@@ -248,17 +256,23 @@ impl Drop for Runtime {
 
             CONTEXT.with(|context| {
                 assert_eq!(context.get(), self.cx);
                 context.set(ptr::null_mut());
             });
 
             if OUTSTANDING_RUNTIMES.fetch_sub(1, Ordering::SeqCst) == 1 {
                 SHUT_DOWN.store(true, Ordering::SeqCst);
-                JS_ShutDown();
+                let signal = &SHUT_DOWN_SIGNAL.lock().unwrap();
+                let signal = signal.as_ref().unwrap();
+                // Send a signal to shutdown the Parent runtime.
+                signal.send(()).unwrap();
+                // Wait for it to be destroyed before resuming the execution
+                // of static variable destructors.
+                signal.send(()).unwrap();
             }
         }
     }
 }
 
 // ___________________________________________________________________________
 // Rooting API for standard JS things