diff --git a/components/style/parallel.rs b/components/style/parallel.rs index 859548d3c8b..fecdff844a6 100644 --- a/components/style/parallel.rs +++ b/components/style/parallel.rs @@ -125,6 +125,19 @@ pub fn traverse_dom(traversal: &D, } } +/// A callback to create our thread local context. This needs to be +/// out of line so we don't allocate stack space for the entire struct +/// in the caller. +#[inline(never)] +fn create_thread_local_context<'scope, E, D>( + traversal: &'scope D, + slot: &mut Option) + where E: TElement + 'scope, + D: DomTraversal +{ + *slot = Some(traversal.create_thread_local_context()) +} + /// A parallel top-down DOM traversal. /// /// This algorithm traverses the DOM in a breadth-first, top-down manner. The @@ -153,7 +166,8 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode], { // Scope the borrow of the TLS so that the borrow is dropped before // a potential recursive call when we pass TailCall. - let mut tlc = tls.ensure(|| traversal.create_thread_local_context()); + let mut tlc = tls.ensure( + |slot: &mut Option| create_thread_local_context(traversal, slot)); for n in nodes { // If the last node we processed produced children, spawn them off diff --git a/components/style/scoped_tls.rs b/components/style/scoped_tls.rs index 78537bce5f9..b1ccb4c7179 100644 --- a/components/style/scoped_tls.rs +++ b/components/style/scoped_tls.rs @@ -9,6 +9,7 @@ use rayon; use std::cell::{Ref, RefCell, RefMut}; +use std::ops::DerefMut; /// A scoped TLS set, that is alive during the `'scope` lifetime. /// @@ -52,11 +53,16 @@ impl<'scope, T: Send> ScopedTLS<'scope, T> { } /// Ensure that the current data this thread owns is initialized, or - /// initialize it using `f`. - pub fn ensure T>(&self, f: F) -> RefMut { + /// initialize it using `f`. We want ensure() to be fast and inline, and we + /// want to inline the memmove that initializes the Option. But we don't + /// want to inline space for the entire large T struct in our stack frame. + /// That's why we hand `f` a mutable borrow to write to instead of just + /// having it return a T. + #[inline(always)] + pub fn ensure)>(&self, f: F) -> RefMut { let mut opt = self.borrow_mut(); if opt.is_none() { - *opt = Some(f()); + f(opt.deref_mut()); } RefMut::map(opt, |x| x.as_mut().unwrap())