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

@ -22,6 +22,8 @@ use msg::constellation_msg::{ConstellationChan, PipelineId, WorkerId};
use net_traits::ResourceTask;
use profile_traits::mem;
use script_task::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptTask};
use script_traits::TimerEventRequest;
use std::sync::mpsc::Sender;
use url::Url;
use util::mem::HeapSizeOf;
@ -96,6 +98,14 @@ impl<'a> GlobalRef<'a> {
}
}
/// Get the scheduler channel to request timer events.
pub fn scheduler_chan(&self) -> Sender<TimerEventRequest> {
match *self {
GlobalRef::Window(window) => window.scheduler_chan(),
GlobalRef::Worker(worker) => worker.scheduler_chan(),
}
}
/// Get an `IpcSender<ScriptToDevtoolsControlMsg>` to send messages to Devtools
/// task when available.
pub fn devtools_chan(&self) -> Option<IpcSender<ScriptToDevtoolsControlMsg>> {

View file

@ -37,6 +37,7 @@ use dom::bindings::js::{JS, Root};
use dom::bindings::refcounted::Trusted;
use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler};
use encoding::types::EncodingRef;
use euclid::length::Length as EuclidLength;
use euclid::matrix2d::Matrix2D;
use euclid::rect::Rect;
use euclid::size::Size2D;
@ -58,7 +59,7 @@ use net_traits::storage_task::StorageType;
use profile_traits::mem::ProfilerChan as MemProfilerChan;
use profile_traits::time::ProfilerChan as TimeProfilerChan;
use script_task::ScriptChan;
use script_traits::UntrustedNodeAddress;
use script_traits::{TimerEventChan, TimerEventId, TimerSource, UntrustedNodeAddress};
use selectors::parser::PseudoElement;
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
@ -283,6 +284,7 @@ no_jsmanaged_fields!(HashSet<T>);
// These three are interdependent, if you plan to put jsmanaged data
// in one of these make sure it is propagated properly to containing structs
no_jsmanaged_fields!(SubpageId, WindowSizeData, PipelineId);
no_jsmanaged_fields!(TimerEventId, TimerSource);
no_jsmanaged_fields!(WorkerId);
no_jsmanaged_fields!(QuirksMode);
no_jsmanaged_fields!(Runtime);
@ -293,6 +295,7 @@ no_jsmanaged_fields!(WindowProxyHandler);
no_jsmanaged_fields!(UntrustedNodeAddress);
no_jsmanaged_fields!(LengthOrPercentageOrAuto);
no_jsmanaged_fields!(RGBA);
no_jsmanaged_fields!(EuclidLength<Unit, T>);
no_jsmanaged_fields!(Matrix2D<T>);
no_jsmanaged_fields!(StorageType);
no_jsmanaged_fields!(CanvasGradientStop, LinearGradientStyle, RadialGradientStyle);
@ -311,6 +314,13 @@ impl JSTraceable for Box<ScriptChan + Send> {
}
}
impl JSTraceable for Box<TimerEventChan + Send> {
#[inline]
fn trace(&self, _trc: *mut JSTracer) {
// Do nothing
}
}
impl JSTraceable for Box<FnBox(f64, )> {
#[inline]
fn trace(&self, _trc: *mut JSTracer) {

View file

@ -29,7 +29,8 @@ use msg::constellation_msg::PipelineId;
use net_traits::load_whole_resource;
use rand::random;
use script_task::ScriptTaskEventCategory::WorkerEvent;
use script_task::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptTask, StackRootTLS, TimerSource};
use script_task::{ScriptTask, ScriptChan, ScriptPort, StackRootTLS, CommonScriptMsg};
use script_traits::{TimerEvent, TimerEventChan, TimerSource};
use std::mem::replace;
use std::rc::Rc;
use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel};
@ -101,6 +102,29 @@ impl ScriptPort for Receiver<(TrustedWorkerAddress, WorkerScriptMsg)> {
}
}
/// A TimerEventChan that can be cloned freely and will silently send a TrustedWorkerAddress
/// with timer events. While this SendableWorkerScriptChan is alive, the associated Worker
/// object will remain alive.
struct WorkerThreadTimerEventChan {
sender: Sender<(TrustedWorkerAddress, TimerEvent)>,
worker: TrustedWorkerAddress,
}
impl TimerEventChan for WorkerThreadTimerEventChan {
fn send(&self, event: TimerEvent) -> Result<(), ()> {
self.sender
.send((self.worker.clone(), event))
.map_err(|_| ())
}
fn clone(&self) -> Box<TimerEventChan + Send> {
box WorkerThreadTimerEventChan {
sender: self.sender.clone(),
worker: self.worker.clone(),
}
}
}
/// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular
/// value for the duration of this object's lifetime. This ensures that the related Worker
/// object only lives as long as necessary (ie. while events are being executed), while
@ -127,6 +151,7 @@ impl<'a> Drop for AutoWorkerReset<'a> {
enum MixedMessage {
FromWorker((TrustedWorkerAddress, WorkerScriptMsg)),
FromScheduler((TrustedWorkerAddress, TimerEvent)),
FromDevtools(DevtoolScriptControlMsg),
}
@ -139,6 +164,8 @@ pub struct DedicatedWorkerGlobalScope {
receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>,
#[ignore_heap_size_of = "Defined in std"]
own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>,
#[ignore_heap_size_of = "Defined in std"]
timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>,
#[ignore_heap_size_of = "Trusted<T> has unclear ownership like JS<T>"]
worker: DOMRefCell<Option<TrustedWorkerAddress>>,
#[ignore_heap_size_of = "Can't measure trait objects"]
@ -154,14 +181,18 @@ impl DedicatedWorkerGlobalScope {
runtime: Rc<Runtime>,
parent_sender: Box<ScriptChan + Send>,
own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>,
receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>)
receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>,
timer_event_chan: Box<TimerEventChan + Send>,
timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>)
-> DedicatedWorkerGlobalScope {
DedicatedWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited(
init, worker_url, runtime, from_devtools_receiver),
init, worker_url, runtime, from_devtools_receiver, timer_event_chan),
id: id,
receiver: receiver,
own_sender: own_sender,
timer_event_port: timer_event_port,
parent_sender: parent_sender,
worker: DOMRefCell::new(None),
}
@ -174,11 +205,13 @@ impl DedicatedWorkerGlobalScope {
runtime: Rc<Runtime>,
parent_sender: Box<ScriptChan + Send>,
own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>,
receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>)
receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>,
timer_event_chan: Box<TimerEventChan + Send>,
timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>)
-> Root<DedicatedWorkerGlobalScope> {
let scope = box DedicatedWorkerGlobalScope::new_inherited(
init, worker_url, id, from_devtools_receiver, runtime.clone(), parent_sender,
own_sender, receiver);
own_sender, receiver, timer_event_chan, timer_event_port);
DedicatedWorkerGlobalScopeBinding::Wrap(runtime.cx(), scope)
}
@ -214,9 +247,16 @@ impl DedicatedWorkerGlobalScope {
let (devtools_mpsc_chan, devtools_mpsc_port) = channel();
ROUTER.route_ipc_receiver_to_mpsc_sender(from_devtools_receiver, devtools_mpsc_chan);
let (timer_tx, timer_rx) = channel();
let timer_event_chan = box WorkerThreadTimerEventChan {
sender: timer_tx,
worker: worker.clone(),
};
let global = DedicatedWorkerGlobalScope::new(
init, url, id, devtools_mpsc_port, runtime.clone(),
parent_sender.clone(), own_sender, receiver);
parent_sender.clone(), own_sender, receiver,
timer_event_chan, timer_rx);
// FIXME(njn): workers currently don't have a unique ID suitable for using in reporter
// registration (#6631), so we instead use a random number and cross our fingers.
let scope = global.upcast::<WorkerGlobalScope>();
@ -263,13 +303,16 @@ impl DedicatedWorkerGlobalScope {
fn receive_event(&self) -> Result<MixedMessage, RecvError> {
let scope = self.upcast::<WorkerGlobalScope>();
let worker_port = &self.receiver;
let timer_event_port = &self.timer_event_port;
let devtools_port = scope.from_devtools_receiver();
let sel = Select::new();
let mut worker_handle = sel.handle(worker_port);
let mut timer_event_handle = sel.handle(timer_event_port);
let mut devtools_handle = sel.handle(devtools_port);
unsafe {
worker_handle.add();
timer_event_handle.add();
if scope.from_devtools_sender().is_some() {
devtools_handle.add();
}
@ -277,6 +320,8 @@ impl DedicatedWorkerGlobalScope {
let ret = sel.wait();
if ret == worker_handle.id() {
Ok(MixedMessage::FromWorker(try!(worker_port.recv())))
} else if ret == timer_event_handle.id() {
Ok(MixedMessage::FromScheduler(try!(timer_event_port.recv())))
} else if ret == devtools_handle.id() {
Ok(MixedMessage::FromDevtools(try!(devtools_port.recv())))
} else {
@ -301,11 +346,6 @@ impl DedicatedWorkerGlobalScope {
WorkerScriptMsg::Common(CommonScriptMsg::RefcountCleanup(addr)) => {
LiveDOMReferences::cleanup(addr);
},
WorkerScriptMsg::Common(
CommonScriptMsg::FireTimer(TimerSource::FromWorker, timer_id)) => {
let scope = self.upcast::<WorkerGlobalScope>();
scope.handle_fire_timer(timer_id);
},
WorkerScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) => {
let scope = self.upcast::<WorkerGlobalScope>();
let cx = scope.get_cx();
@ -313,9 +353,6 @@ impl DedicatedWorkerGlobalScope {
let reports = ScriptTask::get_reports(cx, path_seg);
reports_chan.send(reports);
},
WorkerScriptMsg::Common(CommonScriptMsg::FireTimer(_, _)) => {
panic!("obtained a fire timeout from window for the worker!")
},
}
}
@ -333,6 +370,18 @@ impl DedicatedWorkerGlobalScope {
_ => debug!("got an unusable devtools control message inside the worker!"),
}
},
MixedMessage::FromScheduler((linked_worker, timer_event)) => {
match timer_event {
TimerEvent(TimerSource::FromWorker, id) => {
let _ar = AutoWorkerReset::new(self, linked_worker);
let scope = self.upcast::<WorkerGlobalScope>();
scope.handle_fire_timer(id);
},
TimerEvent(_, _) => {
panic!("A worker received a TimerEvent from a window.")
}
}
}
MixedMessage::FromWorker((linked_worker, msg)) => {
let _ar = AutoWorkerReset::new(self, linked_worker);
self.handle_script_event(msg);

View file

@ -52,9 +52,9 @@ use num::traits::ToPrimitive;
use page::Page;
use profile_traits::mem;
use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64};
use script_task::{MainThreadScriptChan, SendableMainThreadScriptChan};
use script_task::{MainThreadScriptMsg, ScriptChan, ScriptPort, TimerSource};
use script_traits::ConstellationControlMsg;
use script_task::{ScriptChan, ScriptPort, MainThreadScriptMsg};
use script_task::{SendableMainThreadScriptChan, MainThreadScriptChan, MainThreadTimerEventChan};
use script_traits::{ConstellationControlMsg, TimerEventChan, TimerEventId, TimerEventRequest, TimerSource};
use selectors::parser::PseudoElement;
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
@ -70,7 +70,7 @@ use std::sync::mpsc::TryRecvError::{Disconnected, Empty};
use std::sync::mpsc::{Receiver, Sender, channel};
use string_cache::Atom;
use time;
use timers::{IsInterval, TimerCallback, TimerId, TimerManager};
use timers::{ActiveTimers, IsInterval, TimerCallback};
use url::Url;
use util::geometry::{self, MAX_RECT};
use util::str::{DOMString, HTML_SPACE_CHARACTERS};
@ -129,7 +129,9 @@ pub struct Window {
screen: MutNullableHeap<JS<Screen>>,
session_storage: MutNullableHeap<JS<Storage>>,
local_storage: MutNullableHeap<JS<Storage>>,
timers: TimerManager,
#[ignore_heap_size_of = "channels are hard"]
scheduler_chan: Sender<TimerEventRequest>,
timers: ActiveTimers,
next_worker_id: Cell<WorkerId>,
@ -425,8 +427,7 @@ impl WindowMethods for Window {
args,
timeout,
IsInterval::NonInterval,
TimerSource::FromWindow(self.id.clone()),
self.script_chan.clone())
TimerSource::FromWindow(self.id.clone()))
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-settimeout
@ -435,8 +436,7 @@ impl WindowMethods for Window {
args,
timeout,
IsInterval::NonInterval,
TimerSource::FromWindow(self.id.clone()),
self.script_chan.clone())
TimerSource::FromWindow(self.id.clone()))
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-cleartimeout
@ -450,8 +450,7 @@ impl WindowMethods for Window {
args,
timeout,
IsInterval::Interval,
TimerSource::FromWindow(self.id.clone()),
self.script_chan.clone())
TimerSource::FromWindow(self.id.clone()))
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
@ -460,8 +459,7 @@ impl WindowMethods for Window {
args,
timeout,
IsInterval::Interval,
TimerSource::FromWindow(self.id.clone()),
self.script_chan.clone())
TimerSource::FromWindow(self.id.clone()))
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval
@ -1076,7 +1074,7 @@ impl Window {
MainThreadScriptMsg::Navigate(self.id, LoadData::new(url))).unwrap();
}
pub fn handle_fire_timer(&self, timer_id: TimerId) {
pub fn handle_fire_timer(&self, timer_id: TimerEventId) {
self.timers.fire_timer(timer_id, self);
self.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::Timer);
}
@ -1122,6 +1120,10 @@ impl Window {
self.constellation_chan.clone()
}
pub fn scheduler_chan(&self) -> Sender<TimerEventRequest> {
self.scheduler_chan.clone()
}
pub fn windowproxy_handler(&self) -> WindowProxyHandler {
WindowProxyHandler(self.dom_static.windowproxy_handler.0)
}
@ -1267,6 +1269,8 @@ impl Window {
mem_profiler_chan: mem::ProfilerChan,
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
constellation_chan: ConstellationChan,
scheduler_chan: Sender<TimerEventRequest>,
timer_event_chan: MainThreadTimerEventChan,
layout_chan: LayoutChan,
id: PipelineId,
parent_info: Option<(PipelineId, SubpageId)>,
@ -1299,7 +1303,8 @@ impl Window {
screen: Default::default(),
session_storage: Default::default(),
local_storage: Default::default(),
timers: TimerManager::new(),
scheduler_chan: scheduler_chan.clone(),
timers: ActiveTimers::new(box timer_event_chan, scheduler_chan),
next_worker_id: Cell::new(WorkerId(0)),
id: id,
parent_info: parent_info,

View file

@ -72,6 +72,7 @@ impl Worker {
let resource_task = global.resource_task();
let constellation_chan = global.constellation_chan();
let scheduler_chan = global.scheduler_chan();
let (sender, receiver) = channel();
let worker = Worker::new(global, sender.clone());
@ -101,6 +102,7 @@ impl Worker {
to_devtools_sender: global.devtools_chan(),
from_devtools_sender: optional_sender,
constellation_chan: constellation_chan,
scheduler_chan: scheduler_chan,
worker_id: worker_id,
};
DedicatedWorkerGlobalScope::run_worker_scope(

View file

@ -23,12 +23,13 @@ use js::rust::Runtime;
use msg::constellation_msg::{ConstellationChan, PipelineId, WorkerId};
use net_traits::{ResourceTask, load_whole_resource};
use profile_traits::mem;
use script_task::{CommonScriptMsg, ScriptChan, ScriptPort, TimerSource};
use script_task::{CommonScriptMsg, ScriptChan, ScriptPort};
use script_traits::{TimerEventChan, TimerEventId, TimerEventRequest, TimerSource};
use std::cell::Cell;
use std::default::Default;
use std::rc::Rc;
use std::sync::mpsc::Receiver;
use timers::{IsInterval, TimerCallback, TimerId, TimerManager};
use std::sync::mpsc::{Receiver, Sender};
use timers::{ActiveTimers, IsInterval, TimerCallback};
use url::{Url, UrlParser};
use util::str::DOMString;
@ -43,6 +44,7 @@ pub struct WorkerGlobalScopeInit {
pub to_devtools_sender: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
pub from_devtools_sender: Option<IpcSender<DevtoolScriptControlMsg>>,
pub constellation_chan: ConstellationChan,
pub scheduler_chan: Sender<TimerEventRequest>,
pub worker_id: WorkerId,
}
@ -61,7 +63,7 @@ pub struct WorkerGlobalScope {
navigator: MutNullableHeap<JS<WorkerNavigator>>,
console: MutNullableHeap<JS<Console>>,
crypto: MutNullableHeap<JS<Crypto>>,
timers: TimerManager,
timers: ActiveTimers,
#[ignore_heap_size_of = "Defined in std"]
mem_profiler_chan: mem::ProfilerChan,
#[ignore_heap_size_of = "Defined in ipc-channel"]
@ -83,13 +85,17 @@ pub struct WorkerGlobalScope {
#[ignore_heap_size_of = "Defined in std"]
constellation_chan: ConstellationChan,
#[ignore_heap_size_of = "Defined in std"]
scheduler_chan: Sender<TimerEventRequest>,
}
impl WorkerGlobalScope {
pub fn new_inherited(init: WorkerGlobalScopeInit,
worker_url: Url,
runtime: Rc<Runtime>,
from_devtools_receiver: Receiver<DevtoolScriptControlMsg>)
from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
timer_event_chan: Box<TimerEventChan + Send>)
-> WorkerGlobalScope {
WorkerGlobalScope {
eventtarget: EventTarget::new_inherited(),
@ -102,13 +108,14 @@ impl WorkerGlobalScope {
navigator: Default::default(),
console: Default::default(),
crypto: Default::default(),
timers: TimerManager::new(),
timers: ActiveTimers::new(timer_event_chan, init.scheduler_chan.clone()),
mem_profiler_chan: init.mem_profiler_chan,
to_devtools_sender: init.to_devtools_sender,
from_devtools_sender: init.from_devtools_sender,
from_devtools_receiver: from_devtools_receiver,
devtools_wants_updates: Cell::new(false),
constellation_chan: init.constellation_chan,
scheduler_chan: init.scheduler_chan,
}
}
@ -132,6 +139,10 @@ impl WorkerGlobalScope {
self.constellation_chan.clone()
}
pub fn scheduler_chan(&self) -> Sender<TimerEventRequest> {
self.scheduler_chan.clone()
}
pub fn get_cx(&self) -> *mut JSContext {
self.runtime.cx()
}
@ -233,8 +244,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
args,
timeout,
IsInterval::NonInterval,
TimerSource::FromWorker,
self.script_chan())
TimerSource::FromWorker)
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
@ -243,8 +253,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
args,
timeout,
IsInterval::NonInterval,
TimerSource::FromWorker,
self.script_chan())
TimerSource::FromWorker)
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval
@ -258,8 +267,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
args,
timeout,
IsInterval::Interval,
TimerSource::FromWorker,
self.script_chan())
TimerSource::FromWorker)
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
@ -268,8 +276,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
args,
timeout,
IsInterval::Interval,
TimerSource::FromWorker,
self.script_chan())
TimerSource::FromWorker)
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval
@ -330,7 +337,7 @@ impl WorkerGlobalScope {
}
}
pub fn handle_fire_timer(&self, timer_id: TimerId) {
pub fn handle_fire_timer(&self, timer_id: TimerEventId) {
self.timers.fire_timer(timer_id, self);
}