Allow refcounting arbitrary DOM objects in concert with the GC to enable safe, asynchronous/cross-task references to pinned objects.

This commit is contained in:
Josh Matthews 2014-11-20 17:17:02 -05:00
parent c4b93d30e4
commit 2f059c15e7
8 changed files with 210 additions and 154 deletions

View file

@ -15,6 +15,7 @@ use dom::bindings::error::Error::{InvalidState, InvalidAccess};
use dom::bindings::error::Error::{Network, Syntax, Security, Abort, Timeout};
use dom::bindings::global::{GlobalField, GlobalRef, GlobalRoot};
use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, OptionalRootedRootable};
use dom::bindings::refcounted::Trusted;
use dom::bindings::str::ByteString;
use dom::bindings::utils::{Reflectable, reflect_dom_object};
use dom::document::Document;
@ -37,13 +38,10 @@ use hyper::http::RawStatus;
use hyper::mime::{mod, Mime};
use hyper::method::{Method, Get, Head, Connect, Trace, Extension};
use js::jsapi::{JS_AddObjectRoot, JS_ParseJSON, JS_RemoveObjectRoot, JSContext};
use js::jsapi::{JS_ParseJSON, JSContext};
use js::jsapi::JS_ClearPendingException;
use js::jsval::{JSVal, NullValue, UndefinedValue};
use libc;
use libc::c_void;
use net::resource_task::{ResourceTask, ResourceCORSData, Load, LoadData, LoadResponse, Payload, Done};
use cors::{allow_cross_origin_request, CORSRequest, RequestMode};
use servo_util::str::DOMString;
@ -73,15 +71,6 @@ enum XMLHttpRequestState {
XHRDone = 4, // So as not to conflict with the ProgressMsg `Done`
}
struct XHRReleaseHandler(TrustedXHRAddress);
impl Runnable for XHRReleaseHandler {
fn handler(&self) {
let XHRReleaseHandler(addr) = *self;
XMLHttpRequest::handle_release(addr);
}
}
struct XHRProgressHandler {
addr: TrustedXHRAddress,
progress: XHRProgress,
@ -95,7 +84,7 @@ impl XHRProgressHandler {
impl Runnable for XHRProgressHandler {
fn handler(&self) {
XMLHttpRequest::handle_progress(self.addr, self.progress.clone());
XMLHttpRequest::handle_progress(self.addr.clone(), self.progress.clone());
}
}
@ -162,7 +151,6 @@ pub struct XMLHttpRequest {
send_flag: Cell<bool>,
global: GlobalField,
pinned_count: Cell<uint>,
timer: DOMRefCell<Timer>,
fetch_time: Cell<i64>,
terminate_sender: DOMRefCell<Option<Sender<TerminateReason>>>,
@ -196,7 +184,6 @@ impl XMLHttpRequest {
upload_events: Cell::new(false),
global: GlobalField::from_rooted(global),
pinned_count: Cell::new(0),
timer: DOMRefCell::new(Timer::new().unwrap()),
fetch_time: Cell::new(0),
terminate_sender: DOMRefCell::new(None),
@ -213,14 +200,8 @@ impl XMLHttpRequest {
}
pub fn handle_progress(addr: TrustedXHRAddress, progress: XHRProgress) {
unsafe {
let xhr = JS::from_trusted_xhr_address(addr).root();
xhr.process_partial_response(progress);
}
}
pub fn handle_release(addr: TrustedXHRAddress) {
addr.release_once();
let xhr = addr.to_temporary().root();
xhr.process_partial_response(progress);
}
fn fetch(fetch_type: &SyncOrAsync, resource_task: ResourceTask,
@ -233,9 +214,9 @@ impl XMLHttpRequest {
SyncOrAsync::Sync(xhr) => {
xhr.process_partial_response(msg);
},
SyncOrAsync::Async(addr, script_chan) => {
SyncOrAsync::Async(ref addr, script_chan) => {
let ScriptChan(ref chan) = *script_chan;
chan.send(ScriptMsg::RunnableMsg(box XHRProgressHandler::new(addr, msg)));
chan.send(ScriptMsg::RunnableMsg(box XHRProgressHandler::new(addr.clone(), msg)));
}
}
}
@ -639,9 +620,8 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {
// unpin it. This is to ensure that the object will stay alive
// as long as there are (possibly cancelled) inflight events queued up
// in the script task's port
let addr = unsafe {
self.to_trusted()
};
let addr = Trusted::new(self.global.root().root_ref().get_cx(), self,
script_chan.clone());
spawn_named("XHRTask", proc() {
let _ = XMLHttpRequest::fetch(&mut SyncOrAsync::Async(addr, &script_chan),
resource_task,
@ -650,8 +630,6 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {
cors_request,
gen_id,
start_port);
let ScriptChan(ref chan) = script_chan;
chan.send(ScriptMsg::RunnableMsg(box XHRReleaseHandler(addr)));
});
let timeout = self.timeout.get();
if timeout > 0 {
@ -768,20 +746,9 @@ impl XMLHttpRequestDerived for EventTarget {
}
}
pub struct TrustedXHRAddress(pub *const c_void);
impl TrustedXHRAddress {
pub fn release_once(self) {
unsafe {
JS::from_trusted_xhr_address(self).root().release_once();
}
}
}
pub type TrustedXHRAddress = Trusted<XMLHttpRequest>;
trait PrivateXMLHttpRequestHelpers {
unsafe fn to_trusted(self) -> TrustedXHRAddress;
fn release_once(self);
fn change_ready_state(self, XMLHttpRequestState);
fn process_partial_response(self, progress: XHRProgress);
fn terminate_ongoing_fetch(self);
@ -796,33 +763,6 @@ trait PrivateXMLHttpRequestHelpers {
}
impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> {
// Creates a trusted address to the object, and roots it. Always pair this with a release()
unsafe fn to_trusted(self) -> TrustedXHRAddress {
if self.pinned_count.get() == 0 {
JS_AddObjectRoot(self.global.root().root_ref().get_cx(), self.reflector().rootable());
}
let pinned_count = self.pinned_count.get();
self.pinned_count.set(pinned_count + 1);
TrustedXHRAddress(self.deref() as *const XMLHttpRequest as *const libc::c_void)
}
fn release_once(self) {
if self.sync.get() {
// Lets us call this at various termination cases without having to
// check self.sync every time, since the pinning mechanism only is
// meaningful during an async fetch
return;
}
assert!(self.pinned_count.get() > 0)
let pinned_count = self.pinned_count.get();
self.pinned_count.set(pinned_count - 1);
if self.pinned_count.get() == 0 {
unsafe {
JS_RemoveObjectRoot(self.global.root().root_ref().get_cx(), self.reflector().rootable());
}
}
}
fn change_ready_state(self, rs: XMLHttpRequestState) {
assert!(self.ready_state.get() != rs)
self.ready_state.set(rs);