scoped_tls.rs (2973B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! Stack-scoped thread-local storage for rayon thread pools. 6 7 #![allow(unsafe_code)] 8 #![deny(missing_docs)] 9 10 use crate::global_style_data::STYLO_MAX_THREADS; 11 use std::cell::{Ref, RefCell, RefMut}; 12 use std::ops::DerefMut; 13 14 /// A scoped TLS set, that is alive during the `'scope` lifetime. 15 /// 16 /// We use this on Servo to construct thread-local contexts, but clear them once 17 /// we're done with restyling. 18 /// 19 /// Note that the cleanup is done on the thread that owns the scoped TLS, thus 20 /// the Send bound. 21 pub struct ScopedTLS<'scope, T: Send> { 22 pool: Option<&'scope rayon::ThreadPool>, 23 slots: [RefCell<Option<T>>; STYLO_MAX_THREADS], 24 } 25 26 /// The scoped TLS is `Sync` because no more than one worker thread can access a 27 /// given slot. 28 unsafe impl<'scope, T: Send> Sync for ScopedTLS<'scope, T> {} 29 30 impl<'scope, T: Send> ScopedTLS<'scope, T> { 31 /// Create a new scoped TLS that will last as long as this rayon threadpool 32 /// reference. 33 pub fn new(pool: Option<&'scope rayon::ThreadPool>) -> Self { 34 debug_assert!(pool.map_or(true, |p| p.current_num_threads() <= STYLO_MAX_THREADS)); 35 ScopedTLS { 36 pool, 37 slots: Default::default(), 38 } 39 } 40 41 /// Returns the index corresponding to the calling thread in the thread pool. 42 #[inline] 43 pub fn current_thread_index(&self) -> usize { 44 self.pool.map_or(0, |p| p.current_thread_index().unwrap()) 45 } 46 47 /// Return an immutable reference to the `Option<T>` that this thread owns. 48 pub fn borrow(&self) -> Ref<'_, Option<T>> { 49 let idx = self.current_thread_index(); 50 self.slots[idx].borrow() 51 } 52 53 /// Return a mutable reference to the `Option<T>` that this thread owns. 54 pub fn borrow_mut(&self) -> RefMut<'_, Option<T>> { 55 let idx = self.current_thread_index(); 56 self.slots[idx].borrow_mut() 57 } 58 59 /// Ensure that the current data this thread owns is initialized, or 60 /// initialize it using `f`. We want ensure() to be fast and inline, and we 61 /// want to inline the memmove that initializes the Option<T>. But we don't 62 /// want to inline space for the entire large T struct in our stack frame. 63 /// That's why we hand `f` a mutable borrow to write to instead of just 64 /// having it return a T. 65 #[inline(always)] 66 pub fn ensure<F: FnOnce(&mut Option<T>)>(&self, f: F) -> RefMut<'_, T> { 67 let mut opt = self.borrow_mut(); 68 if opt.is_none() { 69 f(opt.deref_mut()); 70 } 71 72 RefMut::map(opt, |x| x.as_mut().unwrap()) 73 } 74 75 /// Returns the slots. Safe because if we have a mut reference the tls can't be referenced by 76 /// any other thread. 77 pub fn slots(&mut self) -> &mut [RefCell<Option<T>>] { 78 &mut self.slots 79 } 80 }