summaryrefslogtreecommitdiffstats
path: root/servo/components/style/scoped_tls.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/scoped_tls.rs')
-rw-r--r--servo/components/style/scoped_tls.rs78
1 files changed, 78 insertions, 0 deletions
diff --git a/servo/components/style/scoped_tls.rs b/servo/components/style/scoped_tls.rs
new file mode 100644
index 0000000000..7491456669
--- /dev/null
+++ b/servo/components/style/scoped_tls.rs
@@ -0,0 +1,78 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Stack-scoped thread-local storage for rayon thread pools.
+
+#![allow(unsafe_code)]
+#![deny(missing_docs)]
+
+use rayon;
+use std::cell::{Ref, RefCell, RefMut};
+use std::ops::DerefMut;
+
+/// A scoped TLS set, that is alive during the `'scope` lifetime.
+///
+/// We use this on Servo to construct thread-local contexts, but clear them once
+/// we're done with restyling.
+///
+/// Note that the cleanup is done on the thread that owns the scoped TLS, thus
+/// the Send bound.
+pub struct ScopedTLS<'scope, T: Send> {
+ pool: &'scope rayon::ThreadPool,
+ slots: Box<[RefCell<Option<T>>]>,
+}
+
+/// The scoped TLS is `Sync` because no more than one worker thread can access a
+/// given slot.
+unsafe impl<'scope, T: Send> Sync for ScopedTLS<'scope, T> {}
+
+impl<'scope, T: Send> ScopedTLS<'scope, T> {
+ /// Create a new scoped TLS that will last as long as this rayon threadpool
+ /// reference.
+ pub fn new(p: &'scope rayon::ThreadPool) -> Self {
+ let count = p.current_num_threads();
+ let mut v = Vec::with_capacity(count);
+ for _ in 0..count {
+ v.push(RefCell::new(None));
+ }
+
+ ScopedTLS {
+ pool: p,
+ slots: v.into_boxed_slice(),
+ }
+ }
+
+ /// Return an immutable reference to the `Option<T>` that this thread owns.
+ pub fn borrow(&self) -> Ref<Option<T>> {
+ let idx = self.pool.current_thread_index().unwrap();
+ self.slots[idx].borrow()
+ }
+
+ /// Return a mutable reference to the `Option<T>` that this thread owns.
+ pub fn borrow_mut(&self) -> RefMut<Option<T>> {
+ let idx = self.pool.current_thread_index().unwrap();
+ self.slots[idx].borrow_mut()
+ }
+
+ /// Ensure that the current data this thread owns is initialized, or
+ /// initialize it using `f`. We want ensure() to be fast and inline, and we
+ /// want to inline the memmove that initializes the Option<T>. 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<F: FnOnce(&mut Option<T>)>(&self, f: F) -> RefMut<T> {
+ let mut opt = self.borrow_mut();
+ if opt.is_none() {
+ f(opt.deref_mut());
+ }
+
+ RefMut::map(opt, |x| x.as_mut().unwrap())
+ }
+
+ /// Returns the slots, consuming the scope.
+ pub fn into_slots(self) -> Box<[RefCell<Option<T>>]> {
+ self.slots
+ }
+}