servo: Merge #18653 - Move root-related things around so that the abstractions make more sense (from servo:ROOT-ALL-THE-THINGS); r=jdm
authorAnthony Ramine <n.oxyde@gmail.com>
Wed, 27 Sep 2017 08:09:57 -0500
changeset 383286 263b2736bf7656ae3051d75b8354afde54ee171d
parent 383285 8189e90aad8efbb9737a5ff55437ad7f19405885
child 383287 11d5f873f4094211cef995b58c41408b41fd9b95
push id95539
push userkwierso@gmail.com
push dateThu, 28 Sep 2017 00:01:12 +0000
treeherdermozilla-inbound@72de90e66155 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
milestone58.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
servo: Merge #18653 - Move root-related things around so that the abstractions make more sense (from servo:ROOT-ALL-THE-THINGS); r=jdm Source-Repo: https://github.com/servo/servo Source-Revision: 807275d8ac8e7391a36210f91b45f70814b132cc
servo/components/script/dom/bindings/root.rs
servo/components/script/dom/dedicatedworkerglobalscope.rs
servo/components/script/dom/serviceworkerglobalscope.rs
servo/components/script/dom/worklet.rs
servo/components/script/script_runtime.rs
servo/components/script/script_thread.rs
--- a/servo/components/script/dom/bindings/root.rs
+++ b/servo/components/script/dom/bindings/root.rs
@@ -30,28 +30,241 @@ use dom::bindings::reflector::{DomObject
 use dom::bindings::trace::JSTraceable;
 use dom::bindings::trace::trace_reflector;
 use dom::node::Node;
 use heapsize::HeapSizeOf;
 use js::jsapi::{JSObject, JSTracer, Heap};
 use js::rust::GCMethods;
 use mitochondria::OnceCell;
 use script_layout_interface::TrustedNodeAddress;
-use script_thread::STACK_ROOTS;
-use std::cell::UnsafeCell;
+use std::cell::{Cell, UnsafeCell};
 use std::default::Default;
 use std::hash::{Hash, Hasher};
 #[cfg(debug_assertions)]
 use std::intrinsics::type_name;
+use std::marker::PhantomData;
 use std::mem;
 use std::ops::Deref;
 use std::ptr;
 use std::rc::Rc;
 use style::thread_state;
 
+/// A rooted reference to a DOM object.
+///
+/// The JS value is pinned for the duration of this object's lifetime; roots
+/// are additive, so this object's destruction will not invalidate other roots
+/// for the same JS value. `Root`s cannot outlive the associated
+/// `RootCollection` object.
+#[allow(unrooted_must_root)]
+#[allow_unrooted_interior]
+pub struct DomRoot<T: DomObject> {
+    /// Reference to rooted value that must not outlive this container
+    ptr: Dom<T>,
+    /// List that ensures correct dynamic root ordering
+    root_list: *const RootCollection,
+}
+
+impl<T: Castable> DomRoot<T> {
+    /// Cast a DOM object root upwards to one of the interfaces it derives from.
+    pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
+        where U: Castable,
+              T: DerivedFrom<U>
+    {
+        unsafe { mem::transmute(root) }
+    }
+
+    /// Cast a DOM object root downwards to one of the interfaces it might implement.
+    pub fn downcast<U>(root: DomRoot<T>) -> Option<DomRoot<U>>
+        where U: DerivedFrom<T>
+    {
+        if root.is::<U>() {
+            Some(unsafe { mem::transmute(root) })
+        } else {
+            None
+        }
+    }
+}
+
+impl<T: DomObject> DomRoot<T> {
+    /// Create a new stack-bounded root for the provided JS-owned value.
+    /// It cannot outlive its associated `RootCollection`, and it gives
+    /// out references which cannot outlive this new `Root`.
+    #[allow(unrooted_must_root)]
+    unsafe fn new(unrooted: Dom<T>) -> DomRoot<T> {
+        debug_assert!(thread_state::get().is_script());
+        STACK_ROOTS.with(|ref collection| {
+            let collection = collection.get().unwrap();
+            (*collection).root(unrooted.reflector());
+            DomRoot {
+                ptr: unrooted,
+                root_list: collection,
+            }
+        })
+    }
+
+    /// Generate a new root from a reference
+    pub fn from_ref(unrooted: &T) -> DomRoot<T> {
+        unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
+    }
+}
+
+impl<T: DomObject> Deref for DomRoot<T> {
+    type Target = T;
+    fn deref(&self) -> &T {
+        debug_assert!(thread_state::get().is_script());
+        &self.ptr
+    }
+}
+
+impl<T: DomObject + HeapSizeOf> HeapSizeOf for DomRoot<T> {
+    fn heap_size_of_children(&self) -> usize {
+        (**self).heap_size_of_children()
+    }
+}
+
+impl<T: DomObject> PartialEq for DomRoot<T> {
+    fn eq(&self, other: &Self) -> bool {
+        self.ptr == other.ptr
+    }
+}
+
+impl<T: DomObject> Clone for DomRoot<T> {
+    fn clone(&self) -> DomRoot<T> {
+        DomRoot::from_ref(&*self)
+    }
+}
+
+unsafe impl<T: DomObject> JSTraceable for DomRoot<T> {
+    unsafe fn trace(&self, _: *mut JSTracer) {
+        // Already traced.
+    }
+}
+
+impl<T: DomObject> Drop for DomRoot<T> {
+    fn drop(&mut self) {
+        unsafe {
+            (*self.root_list).unroot(self.reflector());
+        }
+    }
+}
+
+/// A rooting mechanism for reflectors on the stack.
+/// LIFO is not required.
+///
+/// See also [*Exact Stack Rooting - Storing a GCPointer on the CStack*]
+/// (https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/GC/Exact_Stack_Rooting).
+pub struct RootCollection {
+    roots: UnsafeCell<Vec<*const Reflector>>,
+}
+
+thread_local!(static STACK_ROOTS: Cell<Option<*const RootCollection>> = Cell::new(None));
+
+pub struct ThreadLocalStackRoots<'a>(PhantomData<&'a u32>);
+
+impl<'a> ThreadLocalStackRoots<'a> {
+    pub fn new(roots: &'a RootCollection) -> Self {
+        STACK_ROOTS.with(|ref r| {
+            r.set(Some(roots))
+        });
+        ThreadLocalStackRoots(PhantomData)
+    }
+}
+
+impl<'a> Drop for ThreadLocalStackRoots<'a> {
+    fn drop(&mut self) {
+        STACK_ROOTS.with(|ref r| r.set(None));
+    }
+}
+
+impl RootCollection {
+    /// Create an empty collection of roots
+    pub fn new() -> RootCollection {
+        debug_assert!(thread_state::get().is_script());
+        RootCollection {
+            roots: UnsafeCell::new(vec![]),
+        }
+    }
+
+    /// Start tracking a stack-based root
+    unsafe fn root(&self, untracked_reflector: *const Reflector) {
+        debug_assert!(thread_state::get().is_script());
+        let roots = &mut *self.roots.get();
+        roots.push(untracked_reflector);
+        assert!(!(*untracked_reflector).get_jsobject().is_null())
+    }
+
+    /// Stop tracking a stack-based reflector, asserting if it isn't found.
+    unsafe fn unroot(&self, tracked_reflector: *const Reflector) {
+        assert!(!tracked_reflector.is_null());
+        assert!(!(*tracked_reflector).get_jsobject().is_null());
+        debug_assert!(thread_state::get().is_script());
+        let roots = &mut *self.roots.get();
+        match roots.iter().rposition(|r| *r == tracked_reflector) {
+            Some(idx) => {
+                roots.remove(idx);
+            },
+            None => panic!("Can't remove a root that was never rooted!"),
+        }
+    }
+}
+
+/// SM Callback that traces the rooted reflectors
+pub unsafe fn trace_roots(tracer: *mut JSTracer) {
+    debug!("tracing stack roots");
+    STACK_ROOTS.with(|ref collection| {
+        let collection = &*(*collection.get().unwrap()).roots.get();
+        for root in collection {
+            trace_reflector(tracer, "on stack", &**root);
+        }
+    });
+}
+
+/// Get a reference out of a rooted value.
+pub trait RootedReference<'root> {
+    /// The type of the reference.
+    type Ref: 'root;
+    /// Obtain a reference out of the rooted value.
+    fn r(&'root self) -> Self::Ref;
+}
+
+impl<'root, T: DomObject + 'root> RootedReference<'root> for DomRoot<T> {
+    type Ref = &'root T;
+    fn r(&'root self) -> &'root T {
+        self
+    }
+}
+
+impl<'root, T: DomObject + 'root> RootedReference<'root> for Dom<T> {
+    type Ref = &'root T;
+    fn r(&'root self) -> &'root T {
+        &self
+    }
+}
+
+impl<'root, T: JSTraceable + DomObject + 'root> RootedReference<'root> for [Dom<T>] {
+    type Ref = &'root [&'root T];
+    fn r(&'root self) -> &'root [&'root T] {
+        unsafe { mem::transmute(self) }
+    }
+}
+
+impl<'root, T: DomObject + 'root> RootedReference<'root> for Rc<T> {
+    type Ref = &'root T;
+    fn r(&'root self) -> &'root T {
+        self
+    }
+}
+
+impl<'root, T: RootedReference<'root> + 'root> RootedReference<'root> for Option<T> {
+    type Ref = Option<T::Ref>;
+    fn r(&'root self) -> Option<T::Ref> {
+        self.as_ref().map(RootedReference::r)
+    }
+}
+
 /// A traced reference to a DOM object
 ///
 /// This type is critical to making garbage collection work with the DOM,
 /// but it is very dangerous; if garbage collection happens with a `Dom<T>`
 /// on the stack, the `Dom<T>` can point to freed memory.
 ///
 /// This should only be used as a field in other DOM objects.
 #[must_root]
@@ -83,23 +296,16 @@ impl<T: DomObject> Dom<T> {
     pub fn from_ref(obj: &T) -> Dom<T> {
         debug_assert!(thread_state::get().is_script());
         Dom {
             ptr: unsafe { NonZero::new_unchecked(&*obj) },
         }
     }
 }
 
-impl<'root, T: DomObject + 'root> RootedReference<'root> for Dom<T> {
-    type Ref = &'root T;
-    fn r(&'root self) -> &'root T {
-        &self
-    }
-}
-
 impl<T: DomObject> Deref for Dom<T> {
     type Target = T;
 
     fn deref(&self) -> &T {
         debug_assert!(thread_state::get().is_script());
         // We can only have &Dom<T> from a rooted thing, so it's safe to deref
         // it to &T.
         unsafe { &*self.ptr.get() }
@@ -455,214 +661,16 @@ impl<T: DomObject> LayoutDom<T> {
     /// safe to call because it originates from the layout thread, and it cannot
     /// mutate DOM nodes.
     pub fn get_for_script(&self) -> &T {
         debug_assert!(thread_state::get().is_script());
         unsafe { &*self.ptr.get() }
     }
 }
 
-/// Get a reference out of a rooted value.
-pub trait RootedReference<'root> {
-    /// The type of the reference.
-    type Ref: 'root;
-    /// Obtain a reference out of the rooted value.
-    fn r(&'root self) -> Self::Ref;
-}
-
-impl<'root, T: JSTraceable + DomObject + 'root> RootedReference<'root> for [Dom<T>] {
-    type Ref = &'root [&'root T];
-    fn r(&'root self) -> &'root [&'root T] {
-        unsafe { mem::transmute(self) }
-    }
-}
-
-impl<'root, T: DomObject + 'root> RootedReference<'root> for Rc<T> {
-    type Ref = &'root T;
-    fn r(&'root self) -> &'root T {
-        self
-    }
-}
-
-impl<'root, T: RootedReference<'root> + 'root> RootedReference<'root> for Option<T> {
-    type Ref = Option<T::Ref>;
-    fn r(&'root self) -> Option<T::Ref> {
-        self.as_ref().map(RootedReference::r)
-    }
-}
-
-/// A rooting mechanism for reflectors on the stack.
-/// LIFO is not required.
-///
-/// See also [*Exact Stack Rooting - Storing a GCPointer on the CStack*]
-/// (https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/GC/Exact_Stack_Rooting).
-pub struct RootCollection {
-    roots: UnsafeCell<Vec<*const Reflector>>,
-}
-
-/// A pointer to a RootCollection, for use in global variables.
-pub struct RootCollectionPtr(pub *const RootCollection);
-
-impl Copy for RootCollectionPtr {}
-impl Clone for RootCollectionPtr {
-    fn clone(&self) -> RootCollectionPtr {
-        *self
-    }
-}
-
-impl RootCollection {
-    /// Create an empty collection of roots
-    pub fn new() -> RootCollection {
-        debug_assert!(thread_state::get().is_script());
-        RootCollection {
-            roots: UnsafeCell::new(vec![]),
-        }
-    }
-
-    /// Start tracking a stack-based root
-    unsafe fn root(&self, untracked_reflector: *const Reflector) {
-        debug_assert!(thread_state::get().is_script());
-        let roots = &mut *self.roots.get();
-        roots.push(untracked_reflector);
-        assert!(!(*untracked_reflector).get_jsobject().is_null())
-    }
-
-    /// Stop tracking a stack-based reflector, asserting if it isn't found.
-    unsafe fn unroot(&self, tracked_reflector: *const Reflector) {
-        assert!(!tracked_reflector.is_null());
-        assert!(!(*tracked_reflector).get_jsobject().is_null());
-        debug_assert!(thread_state::get().is_script());
-        let roots = &mut *self.roots.get();
-        match roots.iter().rposition(|r| *r == tracked_reflector) {
-            Some(idx) => {
-                roots.remove(idx);
-            },
-            None => panic!("Can't remove a root that was never rooted!"),
-        }
-    }
-}
-
-/// SM Callback that traces the rooted reflectors
-pub unsafe fn trace_roots(tracer: *mut JSTracer) {
-    debug!("tracing stack roots");
-    STACK_ROOTS.with(|ref collection| {
-        let RootCollectionPtr(collection) = collection.get().unwrap();
-        let collection = &*(*collection).roots.get();
-        for root in collection {
-            trace_reflector(tracer, "on stack", &**root);
-        }
-    });
-}
-
-/// A rooted reference to a DOM object.
-///
-/// The JS value is pinned for the duration of this object's lifetime; roots
-/// are additive, so this object's destruction will not invalidate other roots
-/// for the same JS value. `Root`s cannot outlive the associated
-/// `RootCollection` object.
-#[allow(unrooted_must_root)]
-#[allow_unrooted_interior]
-pub struct DomRoot<T: DomObject> {
-    /// Reference to rooted value that must not outlive this container
-    ptr: Dom<T>,
-    /// List that ensures correct dynamic root ordering
-    root_list: *const RootCollection,
-}
-
-impl<T: Castable> DomRoot<T> {
-    /// Cast a DOM object root upwards to one of the interfaces it derives from.
-    pub fn upcast<U>(root: DomRoot<T>) -> DomRoot<U>
-        where U: Castable,
-              T: DerivedFrom<U>
-    {
-        unsafe { mem::transmute(root) }
-    }
-
-    /// Cast a DOM object root downwards to one of the interfaces it might implement.
-    pub fn downcast<U>(root: DomRoot<T>) -> Option<DomRoot<U>>
-        where U: DerivedFrom<T>
-    {
-        if root.is::<U>() {
-            Some(unsafe { mem::transmute(root) })
-        } else {
-            None
-        }
-    }
-}
-
-impl<T: DomObject> DomRoot<T> {
-    /// Create a new stack-bounded root for the provided JS-owned value.
-    /// It cannot outlive its associated `RootCollection`, and it gives
-    /// out references which cannot outlive this new `Root`.
-    #[allow(unrooted_must_root)]
-    unsafe fn new(unrooted: Dom<T>) -> DomRoot<T> {
-        debug_assert!(thread_state::get().is_script());
-        STACK_ROOTS.with(|ref collection| {
-            let RootCollectionPtr(collection) = collection.get().unwrap();
-            (*collection).root(unrooted.reflector());
-            DomRoot {
-                ptr: unrooted,
-                root_list: collection,
-            }
-        })
-    }
-
-    /// Generate a new root from a reference
-    pub fn from_ref(unrooted: &T) -> DomRoot<T> {
-        unsafe { DomRoot::new(Dom::from_ref(unrooted)) }
-    }
-}
-
-impl<'root, T: DomObject + 'root> RootedReference<'root> for DomRoot<T> {
-    type Ref = &'root T;
-    fn r(&'root self) -> &'root T {
-        self
-    }
-}
-
-impl<T: DomObject> Deref for DomRoot<T> {
-    type Target = T;
-    fn deref(&self) -> &T {
-        debug_assert!(thread_state::get().is_script());
-        &self.ptr
-    }
-}
-
-impl<T: DomObject + HeapSizeOf> HeapSizeOf for DomRoot<T> {
-    fn heap_size_of_children(&self) -> usize {
-        (**self).heap_size_of_children()
-    }
-}
-
-impl<T: DomObject> PartialEq for DomRoot<T> {
-    fn eq(&self, other: &Self) -> bool {
-        self.ptr == other.ptr
-    }
-}
-
-impl<T: DomObject> Clone for DomRoot<T> {
-    fn clone(&self) -> DomRoot<T> {
-        DomRoot::from_ref(&*self)
-    }
-}
-
-impl<T: DomObject> Drop for DomRoot<T> {
-    fn drop(&mut self) {
-        unsafe {
-            (*self.root_list).unroot(self.reflector());
-        }
-    }
-}
-
-unsafe impl<T: DomObject> JSTraceable for DomRoot<T> {
-    unsafe fn trace(&self, _: *mut JSTracer) {
-        // Already traced.
-    }
-}
-
 /// Helper trait for safer manipulations of Option<Heap<T>> values.
 pub trait OptionalHeapSetter {
     type Value;
     /// Update this optional heap value with a new value.
     fn set(&mut self, v: Option<Self::Value>);
 }
 
 impl<T: GCMethods + Copy> OptionalHeapSetter for Option<Heap<T>> where Heap<T>: Default {
--- a/servo/components/script/dom/dedicatedworkerglobalscope.rs
+++ b/servo/components/script/dom/dedicatedworkerglobalscope.rs
@@ -7,17 +7,17 @@ use devtools_traits::DevtoolScriptContro
 use dom::abstractworker::{SharedRt, SimpleWorkerErrorHandler, WorkerScriptMsg};
 use dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThreadWorkerChan};
 use dom::bindings::cell::DomRefCell;
 use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
 use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
 use dom::bindings::error::{ErrorInfo, ErrorResult};
 use dom::bindings::inheritance::Castable;
 use dom::bindings::reflector::DomObject;
-use dom::bindings::root::{DomRoot, RootCollection};
+use dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots};
 use dom::bindings::str::DOMString;
 use dom::bindings::structuredclone::StructuredCloneData;
 use dom::errorevent::ErrorEvent;
 use dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
 use dom::eventtarget::EventTarget;
 use dom::globalscope::GlobalScope;
 use dom::messageevent::MessageEvent;
 use dom::worker::{TrustedWorkerAddress, Worker};
@@ -27,17 +27,17 @@ use ipc_channel::ipc::{self, IpcReceiver
 use ipc_channel::router::ROUTER;
 use js::jsapi::{HandleValue, JS_SetInterruptCallback};
 use js::jsapi::{JSAutoCompartment, JSContext, NullHandleValue};
 use js::jsval::UndefinedValue;
 use js::rust::Runtime;
 use msg::constellation_msg::TopLevelBrowsingContextId;
 use net_traits::{IpcSend, load_whole_resource};
 use net_traits::request::{CredentialsMode, Destination, RequestInit, Type as RequestType};
-use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, StackRootTLS, new_rt_and_cx};
+use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, new_rt_and_cx};
 use script_runtime::ScriptThreadEventCategory::WorkerEvent;
 use script_traits::{TimerEvent, TimerSource, WorkerGlobalScopeInit, WorkerScriptLoadOrigin};
 use servo_rand::random;
 use servo_url::ServoUrl;
 use std::mem::replace;
 use std::sync::{Arc, Mutex};
 use std::sync::atomic::AtomicBool;
 use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel};
@@ -167,17 +167,17 @@ impl DedicatedWorkerGlobalScope {
         thread::Builder::new().name(name).spawn(move || {
             thread_state::initialize(thread_state::SCRIPT | thread_state::IN_WORKER);
 
             if let Some(top_level_browsing_context_id) = top_level_browsing_context_id {
                 TopLevelBrowsingContextId::install(top_level_browsing_context_id);
             }
 
             let roots = RootCollection::new();
-            let _stack_roots_tls = StackRootTLS::new(&roots);
+            let _stack_roots = ThreadLocalStackRoots::new(&roots);
 
             let WorkerScriptLoadOrigin { referrer_url, referrer_policy, pipeline_id } = worker_load_origin;
 
             let request = RequestInit {
                 url: worker_url.clone(),
                 type_: RequestType::Script,
                 destination: Destination::Worker,
                 credentials_mode: CredentialsMode::Include,
--- a/servo/components/script/dom/serviceworkerglobalscope.rs
+++ b/servo/components/script/dom/serviceworkerglobalscope.rs
@@ -4,33 +4,33 @@
 
 use devtools;
 use devtools_traits::DevtoolScriptControlMsg;
 use dom::abstractworker::WorkerScriptMsg;
 use dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding;
 use dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding::ServiceWorkerGlobalScopeMethods;
 use dom::bindings::inheritance::Castable;
 use dom::bindings::reflector::DomObject;
-use dom::bindings::root::{DomRoot, RootCollection};
+use dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots};
 use dom::bindings::str::DOMString;
 use dom::event::Event;
 use dom::eventtarget::EventTarget;
 use dom::extendableevent::ExtendableEvent;
 use dom::extendablemessageevent::ExtendableMessageEvent;
 use dom::globalscope::GlobalScope;
 use dom::workerglobalscope::WorkerGlobalScope;
 use dom_struct::dom_struct;
 use ipc_channel::ipc::{self, IpcSender, IpcReceiver};
 use ipc_channel::router::ROUTER;
 use js::jsapi::{JS_SetInterruptCallback, JSAutoCompartment, JSContext};
 use js::jsval::UndefinedValue;
 use js::rust::Runtime;
 use net_traits::{load_whole_resource, IpcSend, CustomResponseMediator};
 use net_traits::request::{CredentialsMode, Destination, RequestInit, Type as RequestType};
-use script_runtime::{CommonScriptMsg, ScriptChan, StackRootTLS, new_rt_and_cx};
+use script_runtime::{CommonScriptMsg, ScriptChan, new_rt_and_cx};
 use script_traits::{TimerEvent, WorkerGlobalScopeInit, ScopeThings, ServiceWorkerMsg, WorkerScriptLoadOrigin};
 use servo_config::prefs::PREFS;
 use servo_rand::random;
 use servo_url::ServoUrl;
 use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel};
 use std::thread;
 use std::time::Duration;
 use style::thread_state::{self, IN_WORKER, SCRIPT};
@@ -149,17 +149,17 @@ impl ServiceWorkerGlobalScope {
                           worker_load_origin,
                           .. } = scope_things;
 
         let serialized_worker_url = script_url.to_string();
         let origin = GlobalScope::current().expect("No current global object").origin().immutable().clone();
         thread::Builder::new().name(format!("ServiceWorker for {}", serialized_worker_url)).spawn(move || {
             thread_state::initialize(SCRIPT | IN_WORKER);
             let roots = RootCollection::new();
-            let _stack_roots_tls = StackRootTLS::new(&roots);
+            let _stack_roots = ThreadLocalStackRoots::new(&roots);
 
             let WorkerScriptLoadOrigin { referrer_url, referrer_policy, pipeline_id } = worker_load_origin;
 
             let request = RequestInit {
                 url: script_url.clone(),
                 type_: RequestType::Script,
                 destination: Destination::ServiceWorker,
                 credentials_mode: CredentialsMode::Include,
--- a/servo/components/script/dom/worklet.rs
+++ b/servo/components/script/dom/worklet.rs
@@ -15,17 +15,17 @@ use dom::bindings::codegen::Bindings::Wi
 use dom::bindings::codegen::Bindings::WorkletBinding::WorkletMethods;
 use dom::bindings::codegen::Bindings::WorkletBinding::WorkletOptions;
 use dom::bindings::codegen::Bindings::WorkletBinding::Wrap;
 use dom::bindings::error::Error;
 use dom::bindings::inheritance::Castable;
 use dom::bindings::refcounted::TrustedPromise;
 use dom::bindings::reflector::Reflector;
 use dom::bindings::reflector::reflect_dom_object;
-use dom::bindings::root::{Dom, DomRoot, RootCollection};
+use dom::bindings::root::{Dom, DomRoot, RootCollection, ThreadLocalStackRoots};
 use dom::bindings::str::USVString;
 use dom::bindings::trace::JSTraceable;
 use dom::bindings::trace::RootedTraceableBox;
 use dom::globalscope::GlobalScope;
 use dom::promise::Promise;
 use dom::testworkletglobalscope::TestWorkletTask;
 use dom::window::Window;
 use dom::workletglobalscope::WorkletGlobalScope;
@@ -42,17 +42,16 @@ use msg::constellation_msg::PipelineId;
 use net_traits::IpcSend;
 use net_traits::load_whole_resource;
 use net_traits::request::Destination;
 use net_traits::request::RequestInit;
 use net_traits::request::RequestMode;
 use net_traits::request::Type as RequestType;
 use script_runtime::CommonScriptMsg;
 use script_runtime::ScriptThreadEventCategory;
-use script_runtime::StackRootTLS;
 use script_runtime::new_rt_and_cx;
 use script_thread::{MainThreadScriptMsg, ScriptThread};
 use servo_rand;
 use servo_url::ImmutableOrigin;
 use servo_url::ServoUrl;
 use std::cmp::max;
 use std::collections::HashMap;
 use std::collections::hash_map;
@@ -424,17 +423,17 @@ impl WorkletThread {
         // TODO: name this thread
         thread::spawn(move || {
             // TODO: add a new IN_WORKLET thread state?
             // TODO: set interrupt handler?
             // TODO: configure the JS runtime (e.g. discourage GC, encourage agressive JIT)
             debug!("Initializing worklet thread.");
             thread_state::initialize(thread_state::SCRIPT | thread_state::IN_WORKER);
             let roots = RootCollection::new();
-            let _stack_roots_tls = StackRootTLS::new(&roots);
+            let _stack_roots = ThreadLocalStackRoots::new(&roots);
             let mut thread = RootedTraceableBox::new(WorkletThread {
                 role: role,
                 control_receiver: control_receiver,
                 primary_sender: init.primary_sender,
                 hot_backup_sender: init.hot_backup_sender,
                 cold_backup_sender: init.cold_backup_sender,
                 global_init: init.global_init,
                 global_scopes: HashMap::new(),
--- a/servo/components/script/script_runtime.rs
+++ b/servo/components/script/script_runtime.rs
@@ -2,39 +2,38 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! The script runtime contains common traits and structs commonly used by the
 //! script thread, the dom, and the worker threads.
 
 use dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback;
 use dom::bindings::refcounted::{LiveDOMReferences, trace_refcounted_objects};
-use dom::bindings::root::{RootCollection, RootCollectionPtr, trace_roots};
+use dom::bindings::root::trace_roots;
 use dom::bindings::settings_stack;
 use dom::bindings::trace::{JSTraceable, trace_traceables};
 use dom::bindings::utils::DOM_CALLBACKS;
 use dom::globalscope::GlobalScope;
 use js::glue::CollectServoSizes;
 use js::jsapi::{DisableIncrementalGC, GCDescription, GCProgress, HandleObject};
 use js::jsapi::{JSContext, JS_GetRuntime, JSRuntime, JSTracer, SetDOMCallbacks, SetGCSliceCallback};
 use js::jsapi::{JSGCInvocationKind, JSGCStatus, JS_AddExtraGCRootsTracer, JS_SetGCCallback};
 use js::jsapi::{JSGCMode, JSGCParamKey, JS_SetGCParameter, JS_SetGlobalJitCompilerOption};
 use js::jsapi::{JSJitCompilerOption, JS_SetOffthreadIonCompilationEnabled, JS_SetParallelParsingEnabled};
 use js::jsapi::{JSObject, RuntimeOptionsRef, SetPreserveWrapperCallback, SetEnqueuePromiseJobCallback};
 use js::panic::wrap_panic;
 use js::rust::Runtime;
 use microtask::{EnqueuedPromiseCallback, Microtask};
 use profile_traits::mem::{Report, ReportKind, ReportsChan};
-use script_thread::{STACK_ROOTS, trace_thread};
+use script_thread::trace_thread;
 use servo_config::opts;
 use servo_config::prefs::PREFS;
 use std::cell::Cell;
 use std::fmt;
 use std::io::{Write, stdout};
-use std::marker::PhantomData;
 use std::os;
 use std::os::raw::c_void;
 use std::panic::AssertUnwindSafe;
 use std::ptr;
 use style::thread_state;
 use task::TaskBox;
 use time::{Tm, now};
 
@@ -97,33 +96,16 @@ pub enum ScriptThreadEventCategory {
 
 /// An interface for receiving ScriptMsg values in an event loop. Used for synchronous DOM
 /// APIs that need to abstract over multiple kinds of event loops (worker/main thread) with
 /// different Receiver interfaces.
 pub trait ScriptPort {
     fn recv(&self) -> Result<CommonScriptMsg, ()>;
 }
 
-pub struct StackRootTLS<'a>(PhantomData<&'a u32>);
-
-impl<'a> StackRootTLS<'a> {
-    pub fn new(roots: &'a RootCollection) -> StackRootTLS<'a> {
-        STACK_ROOTS.with(|ref r| {
-            r.set(Some(RootCollectionPtr(roots as *const _)))
-        });
-        StackRootTLS(PhantomData)
-    }
-}
-
-impl<'a> Drop for StackRootTLS<'a> {
-    fn drop(&mut self) {
-        STACK_ROOTS.with(|ref r| r.set(None));
-    }
-}
-
 /// SM callback for promise job resolution. Adds a promise callback to the current
 /// global's microtask queue.
 #[allow(unsafe_code)]
 unsafe extern "C" fn enqueue_job(cx: *mut JSContext,
                                  job: HandleObject,
                                  _allocation_site: HandleObject,
                                  _data: *mut c_void) -> bool {
     wrap_panic(AssertUnwindSafe(|| {
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -30,17 +30,17 @@ use dom::bindings::codegen::Bindings::Do
 use dom::bindings::codegen::Bindings::EventBinding::EventInit;
 use dom::bindings::codegen::Bindings::TransitionEventBinding::TransitionEventInit;
 use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
 use dom::bindings::conversions::{ConversionResult, FromJSValConvertible, StringificationBehavior};
 use dom::bindings::inheritance::Castable;
 use dom::bindings::num::Finite;
 use dom::bindings::reflector::DomObject;
 use dom::bindings::root::{Dom, DomRoot, MutNullableDom, RootCollection};
-use dom::bindings::root::{RootCollectionPtr, RootedReference};
+use dom::bindings::root::{RootedReference, ThreadLocalStackRoots};
 use dom::bindings::str::DOMString;
 use dom::bindings::structuredclone::StructuredCloneData;
 use dom::bindings::trace::JSTraceable;
 use dom::bindings::utils::WRAP_CALLBACKS;
 use dom::customelementregistry::{CallbackReaction, CustomElementDefinition, CustomElementReactionStack};
 use dom::document::{Document, DocumentSource, FocusType, HasBrowsingContext, IsHTMLDocument, TouchEventResult};
 use dom::element::Element;
 use dom::event::{Event, EventBubbles, EventCancelable};
@@ -81,17 +81,17 @@ use net_traits::{FetchMetadata, FetchRes
 use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads};
 use net_traits::image_cache::{ImageCache, PendingImageResponse};
 use net_traits::request::{CredentialsMode, Destination, RedirectMode, RequestInit};
 use net_traits::storage_thread::StorageType;
 use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan};
 use profile_traits::time::{self, ProfilerCategory, profile};
 use script_layout_interface::message::{self, Msg, NewLayoutThreadInfo, ReflowQueryType};
 use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
-use script_runtime::{ScriptPort, StackRootTLS, get_reports, new_rt_and_cx};
+use script_runtime::{ScriptPort, get_reports, new_rt_and_cx};
 use script_traits::{CompositorEvent, ConstellationControlMsg};
 use script_traits::{DiscardBrowsingContext, DocumentActivity, EventResult};
 use script_traits::{InitialScriptState, JsEvalResult, LayoutMsg, LoadData};
 use script_traits::{MouseButton, MouseEventType, MozBrowserEvent, NewLayoutInfo};
 use script_traits::{PaintMetricType, Painter, ScriptMsg, ScriptThreadFactory};
 use script_traits::{ScriptToConstellationChan, TimerEvent, TimerSchedulerMsg};
 use script_traits::{TimerSource, TouchEventType, TouchId, UntrustedNodeAddress};
 use script_traits::{UpdatePipelineIdReason, WindowSizeData, WindowSizeType};
@@ -124,17 +124,16 @@ use task_source::user_interaction::UserI
 use time::{get_time, precise_time_ns, Tm};
 use url::Position;
 use url::percent_encoding::percent_decode;
 use webdriver_handlers;
 use webvr_traits::{WebVREvent, WebVRMsg};
 
 pub type ImageCacheMsg = (PipelineId, PendingImageResponse);
 
-thread_local!(pub static STACK_ROOTS: Cell<Option<RootCollectionPtr>> = Cell::new(None));
 thread_local!(static SCRIPT_THREAD_ROOT: Cell<Option<*const ScriptThread>> = Cell::new(None));
 
 pub unsafe fn trace_thread(tr: *mut JSTracer) {
     SCRIPT_THREAD_ROOT.with(|root| {
         if let Some(script_thread) = root.get() {
             debug!("tracing fields of ScriptThread");
             (*script_thread).trace(tr);
         }
@@ -540,17 +539,17 @@ impl ScriptThreadFactory for ScriptThrea
 
         let (sender, receiver) = channel();
         let layout_chan = sender.clone();
         thread::Builder::new().name(format!("ScriptThread {:?}", state.id)).spawn(move || {
             thread_state::initialize(thread_state::SCRIPT);
             PipelineNamespace::install(state.pipeline_namespace_id);
             TopLevelBrowsingContextId::install(state.top_level_browsing_context_id);
             let roots = RootCollection::new();
-            let _stack_roots_tls = StackRootTLS::new(&roots);
+            let _stack_roots = ThreadLocalStackRoots::new(&roots);
             let id = state.id;
             let browsing_context_id = state.browsing_context_id;
             let top_level_browsing_context_id = state.top_level_browsing_context_id;
             let parent_info = state.parent_info;
             let mem_profiler_chan = state.mem_profiler_chan.clone();
             let window_size = state.window_size;
             let script_thread = ScriptThread::new(state,
                                                   script_port,