aboutsummaryrefslogtreecommitdiffstats
path: root/components/util/task_state.rs
blob: ef1dbb2ed3e3122928a25f95040adf6069ea68fb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/* 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 http://mozilla.org/MPL/2.0/. */

//! Supports dynamic assertions in debug builds about what sort of task is
//! running and what state it's in.
//!
//! In release builds, `get` returns 0.  All of the other functions inline
//! away to nothing.

pub use self::imp::{initialize, get, enter, exit};

bitflags! {
    #[derive(Show)]
    flags TaskState: u32 {
        const SCRIPT          = 0x01,
        const LAYOUT          = 0x02,
        const PAINT           = 0x04,

        const IN_WORKER       = 0x0100,
        const IN_GC           = 0x0200,
        const IN_HTML_PARSER  = 0x0400,
    }
}

macro_rules! task_types ( ( $( $fun:ident = $flag:ident ; )* ) => (
    impl TaskState {
        $(
            pub fn $fun(self) -> bool {
                self.contains($flag)
            }
        )*
    }

    #[cfg(not(ndebug))]
    static TYPES: &'static [TaskState]
        = &[ $( $flag ),* ];
));

task_types! {
    is_script = SCRIPT;
    is_layout = LAYOUT;
    is_paint = PAINT;
}

#[cfg(not(ndebug))]
mod imp {
    use super::{TaskState, TYPES};
    use std::cell::RefCell;

    thread_local!(static STATE: RefCell<Option<TaskState>> = RefCell::new(None));

    pub fn initialize(x: TaskState) {
        STATE.with(|ref k| {
            match *k.borrow() {
                Some(s) => panic!("Task state already initialized as {:?}", s),
                None => ()
            };
            *k.borrow_mut() = Some(x);
        });
        get(); // check the assertion below
    }

    pub fn get() -> TaskState {
        let state = STATE.with(|ref k| {
            match *k.borrow() {
                None => panic!("Task state not initialized"),
                Some(s) => s,
            }
        });

        // Exactly one of the task type flags should be set.
        assert_eq!(1, TYPES.iter().filter(|&&ty| state.contains(ty)).count());
        state
    }

    pub fn enter(x: TaskState) {
        let state = get();
        assert!(!state.intersects(x));
        STATE.with(|ref k| {
            *k.borrow_mut() = Some(state | x);
        })
    }

    pub fn exit(x: TaskState) {
        let state = get();
        assert!(state.contains(x));
        STATE.with(|ref k| {
            *k.borrow_mut() = Some(state & !x);
        })
    }
}

#[cfg(ndebug)]
mod imp {
    use super::TaskState;
    #[inline(always)] pub fn initialize(_: TaskState) { }
    #[inline(always)] pub fn get() -> TaskState { TaskState::empty() }
    #[inline(always)] pub fn enter(_: TaskState) { }
    #[inline(always)] pub fn exit(_: TaskState) { }
}