Auto merge of #8603 - benschulz:timers-clean-up, r=jdm

Timers clean up

This PR splits the `ActiveTimers` abstraction into

 - `OneshotTimers` for scheduling "arbitrary" oneshot timers, such as XHR timeouts, and
 - `JsTimers`, based on `OneshotTimers`, for scheduling JS timers (`setTimeout`/`setInterval`).

The result is mich cleaner and the timer initialization steps now closely resemble the specification.

**Notes**
 - The second and third commit are strictly renames and code rearrangements.
 - I'm not particularily happy with the `OneshotTimerCallback` enum and its circular dependency with `XHRTimeoutCallback`, but I couldn't come up with anything better.

<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/8603)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-02-21 21:43:28 +05:30
commit 8f278109ce
5 changed files with 371 additions and 277 deletions

View file

@ -23,7 +23,7 @@ use net_traits::ResourceThread;
use profile_traits::mem;
use script_thread::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptThread};
use script_traits::{MsDuration, ScriptMsg as ConstellationMsg, TimerEventRequest};
use timers::{ScheduledCallback, TimerHandle};
use timers::{OneshotTimerCallback, OneshotTimerHandle};
use url::Url;
/// A freely-copyable reference to a rooted global object.
@ -224,9 +224,9 @@ impl<'a> GlobalRef<'a> {
/// Schedule the given `callback` to be invoked after at least `duration` milliseconds have
/// passed.
pub fn schedule_callback(&self,
callback: Box<ScheduledCallback>,
callback: OneshotTimerCallback,
duration: MsDuration)
-> TimerHandle {
-> OneshotTimerHandle {
match *self {
GlobalRef::Window(window) => window.schedule_callback(callback, duration),
GlobalRef::Worker(worker) => worker.schedule_callback(callback, duration),
@ -234,7 +234,7 @@ impl<'a> GlobalRef<'a> {
}
/// Unschedule a previously-scheduled callback.
pub fn unschedule_callback(&self, handle: TimerHandle) {
pub fn unschedule_callback(&self, handle: OneshotTimerHandle) {
match *self {
GlobalRef::Window(window) => window.unschedule_callback(handle),
GlobalRef::Worker(worker) => worker.unschedule_callback(handle),

View file

@ -76,7 +76,7 @@ use style::context::ReflowGoal;
use style::error_reporting::ParseErrorReporter;
use style::selector_impl::PseudoElement;
use time;
use timers::{ActiveTimers, IsInterval, ScheduledCallback, TimerCallback, TimerHandle};
use timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle, OneshotTimers, TimerCallback};
use url::Url;
use util::geometry::{self, MAX_RECT};
use util::str::{DOMString, HTML_SPACE_CHARACTERS};
@ -149,7 +149,7 @@ pub struct Window {
local_storage: MutNullableHeap<JS<Storage>>,
#[ignore_heap_size_of = "channels are hard"]
scheduler_chan: IpcSender<TimerEventRequest>,
timers: ActiveTimers,
timers: OneshotTimers,
next_worker_id: Cell<WorkerId>,
@ -466,7 +466,8 @@ impl WindowMethods for Window {
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-settimeout
fn SetTimeout(&self, _cx: *mut JSContext, callback: Rc<Function>, timeout: i32, args: Vec<HandleValue>) -> i32 {
self.timers.set_timeout_or_interval(TimerCallback::FunctionTimerCallback(callback),
self.timers.set_timeout_or_interval(GlobalRef::Window(self),
TimerCallback::FunctionTimerCallback(callback),
args,
timeout,
IsInterval::NonInterval,
@ -475,7 +476,8 @@ impl WindowMethods for Window {
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-settimeout
fn SetTimeout_(&self, _cx: *mut JSContext, callback: DOMString, timeout: i32, args: Vec<HandleValue>) -> i32 {
self.timers.set_timeout_or_interval(TimerCallback::StringTimerCallback(callback),
self.timers.set_timeout_or_interval(GlobalRef::Window(self),
TimerCallback::StringTimerCallback(callback),
args,
timeout,
IsInterval::NonInterval,
@ -484,12 +486,13 @@ impl WindowMethods for Window {
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-cleartimeout
fn ClearTimeout(&self, handle: i32) {
self.timers.clear_timeout_or_interval(handle);
self.timers.clear_timeout_or_interval(GlobalRef::Window(self), handle);
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
fn SetInterval(&self, _cx: *mut JSContext, callback: Rc<Function>, timeout: i32, args: Vec<HandleValue>) -> i32 {
self.timers.set_timeout_or_interval(TimerCallback::FunctionTimerCallback(callback),
self.timers.set_timeout_or_interval(GlobalRef::Window(self),
TimerCallback::FunctionTimerCallback(callback),
args,
timeout,
IsInterval::Interval,
@ -498,7 +501,8 @@ impl WindowMethods for Window {
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
fn SetInterval_(&self, _cx: *mut JSContext, callback: DOMString, timeout: i32, args: Vec<HandleValue>) -> i32 {
self.timers.set_timeout_or_interval(TimerCallback::StringTimerCallback(callback),
self.timers.set_timeout_or_interval(GlobalRef::Window(self),
TimerCallback::StringTimerCallback(callback),
args,
timeout,
IsInterval::Interval,
@ -1155,13 +1159,13 @@ impl Window {
self.scheduler_chan.clone()
}
pub fn schedule_callback(&self, callback: Box<ScheduledCallback>, duration: MsDuration) -> TimerHandle {
pub fn schedule_callback(&self, callback: OneshotTimerCallback, duration: MsDuration) -> OneshotTimerHandle {
self.timers.schedule_callback(callback,
duration,
TimerSource::FromWindow(self.id.clone()))
}
pub fn unschedule_callback(&self, handle: TimerHandle) {
pub fn unschedule_callback(&self, handle: OneshotTimerHandle) {
self.timers.unschedule_callback(handle);
}
@ -1349,7 +1353,7 @@ impl Window {
session_storage: Default::default(),
local_storage: Default::default(),
scheduler_chan: scheduler_chan.clone(),
timers: ActiveTimers::new(timer_event_chan, scheduler_chan),
timers: OneshotTimers::new(timer_event_chan, scheduler_chan),
next_worker_id: Cell::new(WorkerId(0)),
id: id,
parent_info: parent_info,

View file

@ -30,7 +30,7 @@ use std::cell::Cell;
use std::default::Default;
use std::rc::Rc;
use std::sync::mpsc::Receiver;
use timers::{ActiveTimers, IsInterval, ScheduledCallback, TimerCallback, TimerHandle};
use timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle, OneshotTimers, TimerCallback};
use url::Url;
use util::str::DOMString;
@ -64,7 +64,7 @@ pub struct WorkerGlobalScope {
navigator: MutNullableHeap<JS<WorkerNavigator>>,
console: MutNullableHeap<JS<Console>>,
crypto: MutNullableHeap<JS<Crypto>>,
timers: ActiveTimers,
timers: OneshotTimers,
#[ignore_heap_size_of = "Defined in std"]
mem_profiler_chan: mem::ProfilerChan,
#[ignore_heap_size_of = "Defined in ipc-channel"]
@ -98,6 +98,7 @@ impl WorkerGlobalScope {
from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
timer_event_chan: IpcSender<TimerEvent>)
-> WorkerGlobalScope {
WorkerGlobalScope {
eventtarget: EventTarget::new_inherited(),
next_worker_id: Cell::new(WorkerId(0)),
@ -109,7 +110,7 @@ impl WorkerGlobalScope {
navigator: Default::default(),
console: Default::default(),
crypto: Default::default(),
timers: ActiveTimers::new(timer_event_chan, init.scheduler_chan.clone()),
timers: OneshotTimers::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,
@ -144,13 +145,13 @@ impl WorkerGlobalScope {
self.scheduler_chan.clone()
}
pub fn schedule_callback(&self, callback: Box<ScheduledCallback>, duration: MsDuration) -> TimerHandle {
pub fn schedule_callback(&self, callback: OneshotTimerCallback, duration: MsDuration) -> OneshotTimerHandle {
self.timers.schedule_callback(callback,
duration,
TimerSource::FromWorker)
}
pub fn unschedule_callback(&self, handle: TimerHandle) {
pub fn unschedule_callback(&self, handle: OneshotTimerHandle) {
self.timers.unschedule_callback(handle);
}
@ -250,7 +251,8 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
fn SetTimeout(&self, _cx: *mut JSContext, callback: Rc<Function>, timeout: i32, args: Vec<HandleValue>) -> i32 {
self.timers.set_timeout_or_interval(TimerCallback::FunctionTimerCallback(callback),
self.timers.set_timeout_or_interval(GlobalRef::Worker(self),
TimerCallback::FunctionTimerCallback(callback),
args,
timeout,
IsInterval::NonInterval,
@ -259,7 +261,8 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
fn SetTimeout_(&self, _cx: *mut JSContext, callback: DOMString, timeout: i32, args: Vec<HandleValue>) -> i32 {
self.timers.set_timeout_or_interval(TimerCallback::StringTimerCallback(callback),
self.timers.set_timeout_or_interval(GlobalRef::Worker(self),
TimerCallback::StringTimerCallback(callback),
args,
timeout,
IsInterval::NonInterval,
@ -268,12 +271,13 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval
fn ClearTimeout(&self, handle: i32) {
self.timers.clear_timeout_or_interval(handle);
self.timers.clear_timeout_or_interval(GlobalRef::Worker(self), handle);
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
fn SetInterval(&self, _cx: *mut JSContext, callback: Rc<Function>, timeout: i32, args: Vec<HandleValue>) -> i32 {
self.timers.set_timeout_or_interval(TimerCallback::FunctionTimerCallback(callback),
self.timers.set_timeout_or_interval(GlobalRef::Worker(self),
TimerCallback::FunctionTimerCallback(callback),
args,
timeout,
IsInterval::Interval,
@ -282,7 +286,8 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
fn SetInterval_(&self, _cx: *mut JSContext, callback: DOMString, timeout: i32, args: Vec<HandleValue>) -> i32 {
self.timers.set_timeout_or_interval(TimerCallback::StringTimerCallback(callback),
self.timers.set_timeout_or_interval(GlobalRef::Worker(self),
TimerCallback::StringTimerCallback(callback),
args,
timeout,
IsInterval::Interval,

View file

@ -59,7 +59,7 @@ use std::sync::mpsc::channel;
use std::sync::{Arc, Mutex};
use string_cache::Atom;
use time;
use timers::{ScheduledCallback, TimerHandle};
use timers::{OneshotTimerCallback, OneshotTimerHandle};
use url::Url;
use url::percent_encoding::{utf8_percent_encode, USERNAME_ENCODE_SET, PASSWORD_ENCODE_SET};
use util::str::DOMString;
@ -146,7 +146,7 @@ pub struct XMLHttpRequest {
upload_events: Cell<bool>,
send_flag: Cell<bool>,
timeout_cancel: DOMRefCell<Option<TimerHandle>>,
timeout_cancel: DOMRefCell<Option<OneshotTimerHandle>>,
fetch_time: Cell<i64>,
generation_id: Cell<GenerationId>,
response_status: Cell<Result<(), ()>>,
@ -1055,39 +1055,15 @@ impl XMLHttpRequest {
self.dispatch_progress_event(false, type_, len, total);
}
fn set_timeout(&self, duration_ms: u32) {
#[derive(JSTraceable, HeapSizeOf)]
struct ScheduledXHRTimeout {
#[ignore_heap_size_of = "Because it is non-owning"]
xhr: Trusted<XMLHttpRequest>,
generation_id: GenerationId,
}
impl ScheduledCallback for ScheduledXHRTimeout {
fn invoke(self: Box<Self>) {
let this = *self;
let xhr = this.xhr.root();
if xhr.ready_state.get() != XMLHttpRequestState::Done {
xhr.process_partial_response(XHRProgress::Errored(this.generation_id, Error::Timeout));
}
}
fn box_clone(&self) -> Box<ScheduledCallback> {
box ScheduledXHRTimeout {
xhr: self.xhr.clone(),
generation_id: self.generation_id,
}
}
}
// Sets up the object to timeout in a given number of milliseconds
// This will cancel all previous timeouts
let global = self.global();
let callback = ScheduledXHRTimeout {
let callback = OneshotTimerCallback::XhrTimeout(XHRTimeoutCallback {
xhr: Trusted::new(self, global.r().networking_task_source()),
generation_id: self.generation_id.get(),
};
});
let duration = Length::new(duration_ms as u64);
*self.timeout_cancel.borrow_mut() = Some(global.r().schedule_callback(box callback, duration));
*self.timeout_cancel.borrow_mut() = Some(global.r().schedule_callback(callback, duration));
}
fn cancel_timeout(&self) {
@ -1368,6 +1344,22 @@ impl XMLHttpRequest {
}
}
#[derive(JSTraceable, HeapSizeOf)]
pub struct XHRTimeoutCallback {
#[ignore_heap_size_of = "Because it is non-owning"]
xhr: Trusted<XMLHttpRequest>,
generation_id: GenerationId,
}
impl XHRTimeoutCallback {
pub fn invoke(self) {
let xhr = self.xhr.root();
if xhr.ready_state.get() != XMLHttpRequestState::Done {
xhr.process_partial_response(XHRProgress::Errored(self.generation_id, Error::Timeout));
}
}
}
trait Extractable {
fn extract(&self) -> (Vec<u8>, Option<DOMString>);
}