auto merge of #4057 : jdm/servo/refcountdom, r=Ms2ger

This replaces the specialized TrustedXHRAddress and TrustedWorkerAddress code that was used for the same purpose. A non-zero refcount pins the given DOM object's reflector and prevents it from being GCed even when there are no other outstanding references visible to SpiderMonkey. This will enable us to implement asynchronous operations that refer to particular DOM objects (such as "queue a task to fire a simple event named load at the iframe element" from the spec) safely and conveniently, and paves the way for things like asynchronous network responses.

Some concerns about the resulting size of XHR progress messages have been expressed, but I believe optimizations to reduce that can be implemented in subsequent PRs.

r? @Ms2ger - note in particular the changes to the worker lifetime code. I couldn't figure out how to achieve an identical lifetime to the previous addref/release pairing, and I also was having trouble figuring out why the existing setup was safe. The new implementation now holds the main script task Worker object alive via the TrustedWorkerAddress field in the dedicated worker global scope, which is a significant difference.
This commit is contained in:
bors-servo 2014-12-29 11:57:45 -07:00
commit 2c259f477c
13 changed files with 423 additions and 254 deletions

View file

@ -15,6 +15,7 @@ use dom::bindings::conversions::FromJSValConvertible;
use dom::bindings::conversions::StringificationBehavior;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, JSRef, RootCollection, Temporary, OptionalRootable};
use dom::bindings::refcounted::LiveDOMReferences;
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap};
use dom::document::{Document, IsHTMLDocument, DocumentHelpers, DocumentSource};
@ -63,13 +64,14 @@ use geom::point::Point2D;
use hyper::header::{Header, HeaderFormat};
use hyper::header::common::util as header_util;
use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC};
use js::jsapi::{JSContext, JSRuntime, JSTracer};
use js::jsapi::{JSContext, JSRuntime};
use js::jsapi::{JS_SetGCParameter, JSGC_MAX_BYTES};
use js::jsapi::{JS_SetGCCallback, JSGCStatus, JSGC_BEGIN, JSGC_END};
use js::rust::{Cx, RtUtils};
use js;
use url::Url;
use libc;
use libc::size_t;
use std::any::{Any, AnyRefExt};
use std::comm::{channel, Sender, Receiver, Select};
@ -114,23 +116,41 @@ pub enum ScriptMsg {
DOMMessage(*mut u64, size_t),
/// Posts a message to the Worker object (dispatched to all tasks).
WorkerPostMessage(TrustedWorkerAddress, *mut u64, size_t),
/// Releases one reference to the Worker object (dispatched to all tasks).
WorkerRelease(TrustedWorkerAddress),
/// Generic message that encapsulates event handling.
RunnableMsg(Box<Runnable+Send>),
/// A DOM object's last pinned reference was removed (dispatched to all tasks).
RefcountCleanup(*const libc::c_void),
}
/// A cloneable interface for communicating with an event loop.
pub trait ScriptChan {
/// Send a message to the associated event loop.
fn send(&self, msg: ScriptMsg);
/// Clone this handle.
fn clone(&self) -> Box<ScriptChan+Send>;
}
/// Encapsulates internal communication within the script task.
#[deriving(Clone)]
pub struct ScriptChan(pub Sender<ScriptMsg>);
#[jstraceable]
pub struct NonWorkerScriptChan(pub Sender<ScriptMsg>);
no_jsmanaged_fields!(ScriptChan)
impl ScriptChan for NonWorkerScriptChan {
fn send(&self, msg: ScriptMsg) {
let NonWorkerScriptChan(ref chan) = *self;
chan.send(msg);
}
impl ScriptChan {
fn clone(&self) -> Box<ScriptChan+Send> {
let NonWorkerScriptChan(ref chan) = *self;
box NonWorkerScriptChan(chan.clone())
}
}
impl NonWorkerScriptChan {
/// Creates a new script chan.
pub fn new() -> (Receiver<ScriptMsg>, ScriptChan) {
pub fn new() -> (Receiver<ScriptMsg>, Box<NonWorkerScriptChan>) {
let (chan, port) = channel();
(port, ScriptChan(chan))
(port, box NonWorkerScriptChan(chan))
}
}
@ -165,7 +185,7 @@ pub struct ScriptTask {
port: Receiver<ScriptMsg>,
/// A channel to hand out to script task-based entities that need to be able to enqueue
/// events in the event queue.
chan: ScriptChan,
chan: NonWorkerScriptChan,
/// A channel to hand out to tasks that need to respond to a message from the script task.
control_chan: ScriptControlChan,
@ -280,7 +300,7 @@ impl ScriptTaskFactory for ScriptTask {
box compositor as Box<ScriptListener>,
layout_chan,
script_port,
ScriptChan(script_chan),
NonWorkerScriptChan(script_chan),
control_chan,
control_port,
constellation_chan,
@ -312,7 +332,7 @@ impl ScriptTask {
compositor: Box<ScriptListener+'static>,
layout_chan: LayoutChan,
port: Receiver<ScriptMsg>,
chan: ScriptChan,
chan: NonWorkerScriptChan,
control_chan: ScriptControlChan,
control_port: Receiver<ConstellationControlMsg>,
constellation_chan: ConstellationChan,
@ -368,6 +388,7 @@ impl ScriptTask {
}
pub fn new_rt_and_cx() -> (js::rust::rt, Rc<Cx>) {
LiveDOMReferences::initialize();
let js_runtime = js::rust::rt();
assert!({
let ptr: *mut JSRuntime = (*js_runtime).ptr;
@ -577,10 +598,10 @@ impl ScriptTask {
panic!("unexpected message"),
ScriptMsg::WorkerPostMessage(addr, data, nbytes) =>
Worker::handle_message(addr, data, nbytes),
ScriptMsg::WorkerRelease(addr) =>
Worker::handle_release(addr),
ScriptMsg::RunnableMsg(runnable) =>
runnable.handler(),
ScriptMsg::RefcountCleanup(addr) =>
LiveDOMReferences::cleanup(self.get_cx(), addr),
}
}