mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Dynamically check DOMRefCell access from layout in debug builds
This commit is contained in:
parent
0162214b1f
commit
6ec0939a22
9 changed files with 150 additions and 30 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
92
components/util/task_state.rs
Normal file
92
components/util/task_state.rs
Normal file
|
@ -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) { }
|
||||
}
|
|
@ -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()
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue