Timers are scheduled by a dedicated per-constellation thread.

This commit is contained in:
benshu 2015-08-30 01:45:07 +02:00
parent 674589c370
commit 553a0dbefd
21 changed files with 786 additions and 334 deletions

View file

@ -82,6 +82,7 @@ use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent};
use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{InitialScriptState, MouseButton, NewLayoutInfo};
use script_traits::{OpaqueScriptLayoutChannel, ScriptState, ScriptTaskFactory};
use script_traits::{TimerEvent, TimerEventChan, TimerEventRequest, TimerSource};
use std::any::Any;
use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
@ -96,7 +97,6 @@ use std::sync::mpsc::{Receiver, Select, Sender, channel};
use std::sync::{Arc, Mutex};
use string_cache::Atom;
use time::{Tm, now};
use timers::TimerId;
use url::{Url, UrlParser};
use util::opts;
use util::str::DOMString;
@ -156,12 +156,6 @@ impl InProgressLoad {
}
}
#[derive(Copy, Clone)]
pub enum TimerSource {
FromWindow(PipelineId),
FromWorker
}
pub trait Runnable {
fn handler(self: Box<Self>);
}
@ -175,6 +169,7 @@ enum MixedMessage {
FromScript(MainThreadScriptMsg),
FromDevtools(DevtoolScriptControlMsg),
FromImageCache(ImageCacheResult),
FromScheduler(TimerEvent),
}
/// Common messages used to control the event loops in both the script and the worker
@ -182,10 +177,6 @@ pub enum CommonScriptMsg {
/// Requests that the script task measure its memory usage. The results are sent back via the
/// supplied channel.
CollectReports(ReportsChan),
/// Fires a JavaScript timeout
/// TimerSource must be FromWindow when dispatched to ScriptTask and
/// must be FromWorker when dispatched to a DedicatedGlobalWorkerScope
FireTimer(TimerSource, TimerId),
/// A DOM object's last pinned reference was removed (dispatched to all tasks).
RefcountCleanup(TrustedReference),
/// Generic message that encapsulates event handling.
@ -205,6 +196,7 @@ pub enum ScriptTaskEventCategory {
NetworkEvent,
Resize,
ScriptEvent,
TimerEvent,
UpdateReplacedElement,
SetViewport,
WebSocketEvent,
@ -327,6 +319,20 @@ impl MainThreadScriptChan {
}
}
pub struct MainThreadTimerEventChan(Sender<TimerEvent>);
impl TimerEventChan for MainThreadTimerEventChan {
fn send(&self, event: TimerEvent) -> Result<(), ()> {
let MainThreadTimerEventChan(ref chan) = *self;
chan.send(event).map_err(|_| ())
}
fn clone(&self) -> Box<TimerEventChan + Send> {
let MainThreadTimerEventChan(ref chan) = *self;
box MainThreadTimerEventChan((*chan).clone())
}
}
pub struct StackRootTLS;
impl StackRootTLS {
@ -408,6 +414,10 @@ pub struct ScriptTask {
/// List of pipelines that have been owned and closed by this script task.
closed_pipelines: RefCell<HashSet<PipelineId>>,
scheduler_chan: Sender<TimerEventRequest>,
timer_event_chan: Sender<TimerEvent>,
timer_event_port: Receiver<TimerEvent>,
}
/// In the event of task failure, all data on the stack runs its destructor. However, there
@ -604,6 +614,8 @@ impl ScriptTask {
let image_cache_port =
ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_image_cache_port);
let (timer_event_chan, timer_event_port) = channel();
ScriptTask {
page: DOMRefCell::new(None),
incomplete_loads: DOMRefCell::new(vec!()),
@ -631,6 +643,10 @@ impl ScriptTask {
js_runtime: Rc::new(runtime),
mouse_over_targets: DOMRefCell::new(vec!()),
closed_pipelines: RefCell::new(HashSet::new()),
scheduler_chan: state.scheduler_chan,
timer_event_chan: timer_event_chan,
timer_event_port: timer_event_port,
}
}
@ -717,26 +733,30 @@ impl ScriptTask {
// Receive at least one message so we don't spinloop.
let mut event = {
let sel = Select::new();
let mut port1 = sel.handle(&self.port);
let mut port2 = sel.handle(&self.control_port);
let mut port3 = sel.handle(&self.devtools_port);
let mut port4 = sel.handle(&self.image_cache_port);
let mut script_port = sel.handle(&self.port);
let mut control_port = sel.handle(&self.control_port);
let mut timer_event_port = sel.handle(&self.timer_event_port);
let mut devtools_port = sel.handle(&self.devtools_port);
let mut image_cache_port = sel.handle(&self.image_cache_port);
unsafe {
port1.add();
port2.add();
script_port.add();
control_port.add();
timer_event_port.add();
if self.devtools_chan.is_some() {
port3.add();
devtools_port.add();
}
port4.add();
image_cache_port.add();
}
let ret = sel.wait();
if ret == port1.id() {
if ret == script_port.id() {
MixedMessage::FromScript(self.port.recv().unwrap())
} else if ret == port2.id() {
} else if ret == control_port.id() {
MixedMessage::FromConstellation(self.control_port.recv().unwrap())
} else if ret == port3.id() {
} else if ret == timer_event_port.id() {
MixedMessage::FromScheduler(self.timer_event_port.recv().unwrap())
} else if ret == devtools_port.id() {
MixedMessage::FromDevtools(self.devtools_port.recv().unwrap())
} else if ret == port4.id() {
} else if ret == image_cache_port.id() {
MixedMessage::FromImageCache(self.image_cache_port.recv().unwrap())
} else {
panic!("unexpected select result")
@ -797,12 +817,15 @@ impl ScriptTask {
// on and execute the sequential non-resize events we've seen.
match self.control_port.try_recv() {
Err(_) => match self.port.try_recv() {
Err(_) => match self.devtools_port.try_recv() {
Err(_) => match self.image_cache_port.try_recv() {
Err(_) => break,
Ok(ev) => event = MixedMessage::FromImageCache(ev),
Err(_) => match self.timer_event_port.try_recv() {
Err(_) => match self.devtools_port.try_recv() {
Err(_) => match self.image_cache_port.try_recv() {
Err(_) => break,
Ok(ev) => event = MixedMessage::FromImageCache(ev),
},
Ok(ev) => event = MixedMessage::FromDevtools(ev),
},
Ok(ev) => event = MixedMessage::FromDevtools(ev),
Ok(ev) => event = MixedMessage::FromScheduler(ev),
},
Ok(ev) => event = MixedMessage::FromScript(ev),
},
@ -823,6 +846,7 @@ impl ScriptTask {
},
MixedMessage::FromConstellation(inner_msg) => self.handle_msg_from_constellation(inner_msg),
MixedMessage::FromScript(inner_msg) => self.handle_msg_from_script(inner_msg),
MixedMessage::FromScheduler(inner_msg) => self.handle_timer_event(inner_msg),
MixedMessage::FromDevtools(inner_msg) => self.handle_msg_from_devtools(inner_msg),
MixedMessage::FromImageCache(inner_msg) => self.handle_msg_from_image_cache(inner_msg),
}
@ -871,7 +895,8 @@ impl ScriptTask {
*category,
_ => ScriptTaskEventCategory::ScriptEvent
}
}
},
MixedMessage::FromScheduler(_) => ScriptTaskEventCategory::TimerEvent,
}
}
@ -893,6 +918,7 @@ impl ScriptTask {
ScriptTaskEventCategory::ScriptEvent => ProfilerCategory::ScriptEvent,
ScriptTaskEventCategory::UpdateReplacedElement => ProfilerCategory::ScriptUpdateReplacedElement,
ScriptTaskEventCategory::SetViewport => ProfilerCategory::ScriptSetViewport,
ScriptTaskEventCategory::TimerEvent => ProfilerCategory::ScriptTimerEvent,
ScriptTaskEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent,
ScriptTaskEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent,
ScriptTaskEventCategory::XhrEvent => ProfilerCategory::ScriptXhrEvent,
@ -966,12 +992,6 @@ impl ScriptTask {
runnable.handler(self),
MainThreadScriptMsg::DocumentLoadsComplete(id) =>
self.handle_loads_complete(id),
MainThreadScriptMsg::Common(
CommonScriptMsg::FireTimer(TimerSource::FromWindow(id), timer_id)) =>
self.handle_fire_timer_msg(id, timer_id),
MainThreadScriptMsg::Common(
CommonScriptMsg::FireTimer(TimerSource::FromWorker, _)) =>
panic!("Worker timeouts must not be sent to script task"),
MainThreadScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable)) =>
// The category of the runnable is ignored by the pattern, however
// it is still respected by profiling (see categorize_msg).
@ -983,6 +1003,22 @@ impl ScriptTask {
}
}
fn handle_timer_event(&self, timer_event: TimerEvent) {
let TimerEvent(source, id) = timer_event;
let pipeline_id = match source {
TimerSource::FromWindow(pipeline_id) => pipeline_id,
TimerSource::FromWorker => panic!("Worker timeouts must not be sent to script task"),
};
let page = self.root_page();
let page = page.find(pipeline_id).expect("ScriptTask: received fire timer msg for a
pipeline ID not associated with this script task. This is a bug.");
let window = page.window();
window.r().handle_fire_timer(id);
}
fn handle_msg_from_devtools(&self, msg: DevtoolScriptControlMsg) {
let page = self.root_page();
match msg {
@ -1272,15 +1308,6 @@ impl ScriptTask {
reports_chan.send(reports);
}
/// Handles a timer that fired.
fn handle_fire_timer_msg(&self, id: PipelineId, timer_id: TimerId) {
let page = self.root_page();
let page = page.find(id).expect("ScriptTask: received fire timer msg for a
pipeline ID not associated with this script task. This is a bug.");
let window = page.window();
window.r().handle_fire_timer(timer_id);
}
/// Handles freeze message
fn handle_freeze_msg(&self, id: PipelineId) {
// Workaround for a race condition when navigating before the initial page has
@ -1587,6 +1614,8 @@ impl ScriptTask {
self.mem_profiler_chan.clone(),
self.devtools_chan.clone(),
self.constellation_chan.clone(),
self.scheduler_chan.clone(),
MainThreadTimerEventChan(self.timer_event_chan.clone()),
incomplete.layout_chan,
incomplete.pipeline_id,
incomplete.parent_info,