style/
thread_state.rs

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 about what sort of thread is running and
6//! what state it's in.
7
8#![deny(missing_docs)]
9
10use std::cell::Cell;
11
12bitflags! {
13    /// A thread state flag, used for multiple assertions.
14    #[derive(Clone, Copy, Default, Debug, Eq, PartialEq)]
15    pub struct ThreadState: u32 {
16        /// Whether we're in a script thread.
17        const SCRIPT          = 0x01;
18        /// Whether we're in a layout thread.
19        const LAYOUT          = 0x02;
20
21        /// Whether we're in a script worker thread (actual web workers), or in
22        /// a layout worker thread.
23        const IN_WORKER       = 0x0100;
24
25        /// Whether the current thread is going through a GC.
26        const IN_GC           = 0x0200;
27    }
28}
29
30impl 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    /// Whether the current thread is a script thread.
37    pub fn is_script(self) -> bool {
38        self.contains(ThreadState::SCRIPT)
39    }
40
41    /// Whether the current thread is a layout thread.
42    pub fn is_layout(self) -> bool {
43        self.contains(ThreadState::LAYOUT)
44    }
45}
46
47thread_local!(static STATE: Cell<Option<ThreadState>> = const { Cell::new(None) });
48
49/// Initializes the current thread state.
50pub fn initialize(initialize_to: ThreadState) {
51    STATE.with(|state| {
52        if let Some(current_state) = state.get() {
53            if initialize_to != current_state {
54                panic!("Thread state already initialized as {:?}", current_state);
55            }
56        }
57        state.set(Some(initialize_to));
58    });
59}
60
61/// Initializes the current thread as a layout worker thread.
62pub fn initialize_layout_worker_thread() {
63    initialize(ThreadState::LAYOUT | ThreadState::IN_WORKER);
64}
65
66/// Gets the current thread state.
67pub fn get() -> ThreadState {
68    STATE.with(|state| state.get().unwrap_or_default())
69}
70
71/// Enters into a given temporary state. Panics if re-entering.
72pub fn enter(additional_flags: ThreadState) {
73    STATE.with(|state| {
74        let current_state = state.get().unwrap_or_default();
75        debug_assert!(!current_state.intersects(additional_flags));
76        state.set(Some(current_state | additional_flags));
77    })
78}
79
80/// Exits a given temporary state.
81pub fn exit(flags_to_remove: ThreadState) {
82    STATE.with(|state| {
83        let current_state = state.get().unwrap_or_default();
84        debug_assert!(current_state.contains(flags_to_remove));
85        state.set(Some(current_state & !flags_to_remove));
86    })
87}