diff options
author | Keegan McAllister <kmcallister@mozilla.com> | 2014-10-23 14:44:17 -0700 |
---|---|---|
committer | Keegan McAllister <kmcallister@mozilla.com> | 2014-10-24 16:27:37 -0700 |
commit | 6ec0939a2248e0e092242076ed5b2cd2486c736c (patch) | |
tree | cd945ad95c170c95a99ca96259c7d20bf2b35424 /components/util | |
parent | 0162214b1fce3787bb08118790fc4d59d8528306 (diff) | |
download | servo-6ec0939a2248e0e092242076ed5b2cd2486c736c.tar.gz servo-6ec0939a2248e0e092242076ed5b2cd2486c736c.zip |
Dynamically check DOMRefCell access from layout in debug builds
Diffstat (limited to 'components/util')
-rw-r--r-- | components/util/lib.rs | 1 | ||||
-rw-r--r-- | components/util/task.rs | 15 | ||||
-rw-r--r-- | components/util/task_state.rs | 92 | ||||
-rw-r--r-- | components/util/workqueue.rs | 8 |
4 files changed, 111 insertions, 5 deletions
diff --git a/components/util/lib.rs b/components/util/lib.rs index a53f3a1c3d9..0c5c5f65e55 100644 --- a/components/util/lib.rs +++ b/components/util/lib.rs @@ -52,6 +52,7 @@ pub mod task; pub mod tid; pub mod time; pub mod taskpool; +pub mod task_state; pub mod vec; pub mod workqueue; diff --git a/components/util/task.rs b/components/util/task.rs index b3e03771610..f286efe5bc3 100644 --- a/components/util/task.rs +++ b/components/util/task.rs @@ -8,22 +8,29 @@ use std::comm::Sender; use std::task::TaskBuilder; use native::task::NativeTaskBuilder; +use task_state; + pub fn spawn_named<S: IntoMaybeOwned<'static>>(name: S, f: proc():Send) { let builder = task::TaskBuilder::new().named(name); builder.spawn(f); } -/// Arrange to send a particular message to a channel if the task built by -/// this `TaskBuilder` fails. +/// Arrange to send a particular message to a channel if the task fails. pub fn spawn_named_with_send_on_failure<T: Send>(name: &'static str, + state: task_state::TaskState, f: proc(): Send, msg: T, dest: Sender<T>, native: bool) { + let with_state = proc() { + task_state::initialize(state); + f() + }; + let future_result = if native { - TaskBuilder::new().named(name).native().try_future(f) + TaskBuilder::new().named(name).native().try_future(with_state) } else { - TaskBuilder::new().named(name).try_future(f) + TaskBuilder::new().named(name).try_future(with_state) }; let watched_name = name.to_string(); diff --git a/components/util/task_state.rs b/components/util/task_state.rs new file mode 100644 index 00000000000..7c714118b09 --- /dev/null +++ b/components/util/task_state.rs @@ -0,0 +1,92 @@ +/* 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` is not available; calls must be inside +//! `debug_assert!` or similar. All of the other functions inline away to +//! nothing. + +pub use self::imp::{initialize, enter, exit}; + +#[cfg(not(ndebug))] +pub use self::imp::get; + +bitflags! { + #[deriving(Show)] + flags TaskState: u32 { + static Script = 0x01, + static Layout = 0x02, + static Render = 0x04, + + static InWorker = 0x0100, + } +} + +// Exactly one of these should be set. +static task_types: &'static [TaskState] + = &[Script, Layout, Render]; + +macro_rules! predicates ( ( $( $f:ident = $c:ident ; )* ) => ( + impl TaskState { + $( + pub fn $f(self) -> bool { + self.contains($c) + } + )* + } +)) + +predicates! { + is_script = Script; + is_layout = Layout; + is_render = Render; +} + +#[cfg(not(ndebug))] +mod imp { + use super::{TaskState, task_types}; + + local_data_key!(STATE: TaskState) + + pub fn initialize(x: TaskState) { + match STATE.replace(Some(x)) { + None => (), + Some(s) => fail!("Task state already initialized as {}", s), + }; + get(); // check the assertion below + } + + pub fn get() -> TaskState { + let state = match STATE.get() { + None => fail!("Task state not initialized"), + Some(s) => *s, + }; + + // Exactly one of the task type flags should be set. + assert_eq!(1, task_types.iter().filter(|&&ty| state.contains(ty)).count()); + state + } + + pub fn enter(x: TaskState) { + let state = get(); + assert!(!state.intersects(x)); + STATE.replace(Some(state | x)); + } + + pub fn exit(x: TaskState) { + let state = get(); + assert!(state.contains(x)); + STATE.replace(Some(state & !x)); + } +} + +#[cfg(ndebug)] +mod imp { + use super::TaskState; + #[inline(always)] pub fn initialize(_: TaskState) { } + #[inline(always)] pub fn enter(_: TaskState) { } + #[inline(always)] pub fn exit(_: TaskState) { } +} diff --git a/components/util/workqueue.rs b/components/util/workqueue.rs index a9f9419d81c..3a0b3e32f91 100644 --- a/components/util/workqueue.rs +++ b/components/util/workqueue.rs @@ -7,6 +7,8 @@ //! Data associated with queues is simply a pair of unsigned integers. It is expected that a //! higher-level API on top of this could allow safe fork-join parallelism. +use task_state; + use native::task::NativeTaskBuilder; use rand::{Rng, XorShiftRng}; use std::mem; @@ -196,7 +198,10 @@ pub struct WorkQueue<QueueData, WorkData> { impl<QueueData: Send, WorkData: Send> WorkQueue<QueueData, WorkData> { /// Creates a new work queue and spawns all the threads associated with /// it. - pub fn new(task_name: &'static str, thread_count: uint, user_data: QueueData) -> WorkQueue<QueueData, WorkData> { + pub fn new(task_name: &'static str, + state: task_state::TaskState, + thread_count: uint, + user_data: QueueData) -> WorkQueue<QueueData, WorkData> { // Set up data structures. let (supervisor_chan, supervisor_port) = channel(); let (mut infos, mut threads) = (vec!(), vec!()); @@ -231,6 +236,7 @@ impl<QueueData: Send, WorkData: Send> WorkQueue<QueueData, WorkData> { // Spawn threads. for thread in threads.into_iter() { TaskBuilder::new().named(task_name).native().spawn(proc() { + task_state::initialize(state | task_state::InWorker); let mut thread = thread; thread.start() }) |