Source code

Revision control

Other Tools

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 rayon;
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: &'scope rayon::ThreadPool,
23
slots: Box<[RefCell<Option<T>>]>,
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(p: &'scope rayon::ThreadPool) -> Self {
34
let count = p.current_num_threads();
35
let mut v = Vec::with_capacity(count);
36
for _ in 0..count {
37
v.push(RefCell::new(None));
38
}
39
40
ScopedTLS {
41
pool: p,
42
slots: v.into_boxed_slice(),
43
}
44
}
45
46
/// Return an immutable reference to the `Option<T>` that this thread owns.
47
pub fn borrow(&self) -> Ref<Option<T>> {
48
let idx = self.pool.current_thread_index().unwrap();
49
self.slots[idx].borrow()
50
}
51
52
/// Return a mutable reference to the `Option<T>` that this thread owns.
53
pub fn borrow_mut(&self) -> RefMut<Option<T>> {
54
let idx = self.pool.current_thread_index().unwrap();
55
self.slots[idx].borrow_mut()
56
}
57
58
/// Ensure that the current data this thread owns is initialized, or
59
/// initialize it using `f`. We want ensure() to be fast and inline, and we
60
/// want to inline the memmove that initializes the Option<T>. But we don't
61
/// want to inline space for the entire large T struct in our stack frame.
62
/// That's why we hand `f` a mutable borrow to write to instead of just
63
/// having it return a T.
64
#[inline(always)]
65
pub fn ensure<F: FnOnce(&mut Option<T>)>(&self, f: F) -> RefMut<T> {
66
let mut opt = self.borrow_mut();
67
if opt.is_none() {
68
f(opt.deref_mut());
69
}
70
71
RefMut::map(opt, |x| x.as_mut().unwrap())
72
}
73
74
/// Unsafe access to the slots. This can be used to access the TLS when
75
/// the caller knows that the pool does not have access to the TLS.
76
pub unsafe fn unsafe_get(&self) -> &[RefCell<Option<T>>] {
77
&self.slots
78
}
79
}