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 //! Supports dynamic assertions in about what sort of thread is running and
6 //! what state it's in.
7 
8 #![deny(missing_docs)]
9 
10 use std::cell::RefCell;
11 
12 bitflags! {
13     /// A thread state flag, used for multiple assertions.
14     pub struct ThreadState: u32 {
15         /// Whether we're in a script thread.
16         const SCRIPT          = 0x01;
17         /// Whether we're in a layout thread.
18         const LAYOUT          = 0x02;
19 
20         /// Whether we're in a script worker thread (actual web workers), or in
21         /// a layout worker thread.
22         const IN_WORKER       = 0x0100;
23 
24         /// Whether the current thread is going through a GC.
25         const IN_GC           = 0x0200;
26     }
27 }
28 
29 macro_rules! thread_types ( ( $( $fun:ident = $flag:path ; )* ) => (
30     impl ThreadState {
31         /// Whether the current thread is a worker thread.
32         pub fn is_worker(self) -> bool {
33             self.contains(ThreadState::IN_WORKER)
34         }
35 
36         $(
37             #[allow(missing_docs)]
38             pub fn $fun(self) -> bool {
39                 self.contains($flag)
40             }
41         )*
42     }
43 ));
44 
45 thread_types! {
46     is_script = ThreadState::SCRIPT;
47     is_layout = ThreadState::LAYOUT;
48 }
49 
50 thread_local!(static STATE: RefCell<Option<ThreadState>> = RefCell::new(None));
51 
52 /// Initializes the current thread state.
initialize(x: ThreadState)53 pub fn initialize(x: ThreadState) {
54     STATE.with(|ref k| {
55         if let Some(ref s) = *k.borrow() {
56             if x != *s {
57                 panic!("Thread state already initialized as {:?}", s);
58             }
59         }
60         *k.borrow_mut() = Some(x);
61     });
62 }
63 
64 /// Initializes the current thread as a layout worker thread.
initialize_layout_worker_thread()65 pub fn initialize_layout_worker_thread() {
66     initialize(ThreadState::LAYOUT | ThreadState::IN_WORKER);
67 }
68 
69 /// Gets the current thread state.
get() -> ThreadState70 pub fn get() -> ThreadState {
71     let state = STATE.with(|ref k| {
72         match *k.borrow() {
73             None => ThreadState::empty(), // Unknown thread.
74             Some(s) => s,
75         }
76     });
77 
78     state
79 }
80 
81 /// Enters into a given temporary state. Panics if re-entring.
enter(x: ThreadState)82 pub fn enter(x: ThreadState) {
83     let state = get();
84     debug_assert!(!state.intersects(x));
85     STATE.with(|ref k| {
86         *k.borrow_mut() = Some(state | x);
87     })
88 }
89 
90 /// Exits a given temporary state.
exit(x: ThreadState)91 pub fn exit(x: ThreadState) {
92     let state = get();
93     debug_assert!(state.contains(x));
94     STATE.with(|ref k| {
95         *k.borrow_mut() = Some(state & !x);
96     })
97 }
98