Force all messages to worker tasks to send a TrustedWorkerAddress along with the ScriptMsg. This ensures that the main-thread Worker object is rooted for as long as there are events in flight or being processed.

This commit is contained in:
Josh Matthews 2014-12-02 16:54:43 -08:00
parent 2f059c15e7
commit 9a7cd31134
11 changed files with 234 additions and 121 deletions

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
@ -35,44 +36,93 @@ use std::rc::Rc;
use std::ptr;
use url::Url;
/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with
/// every message. While this SendableWorkerScriptChan is alive, the associated Worker object
/// will remain alive.
#[deriving(Clone)]
#[jstraceable]
pub struct SendableWorkerScriptChan {
sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
worker: TrustedWorkerAddress,
}
impl ScriptChan for SendableWorkerScriptChan {
fn send(&self, msg: ScriptMsg) {
self.sender.send((self.worker.clone(), msg));
}
fn clone(&self) -> Box<ScriptChan + Send> {
box SendableWorkerScriptChan {
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
/// providing a reference that can be cloned freely.
struct AutoWorkerReset<'a> {
workerscope: JSRef<'a, DedicatedWorkerGlobalScope>,
old_worker: Option<TrustedWorkerAddress>,
}
impl<'a> AutoWorkerReset<'a> {
fn new(workerscope: JSRef<'a, DedicatedWorkerGlobalScope>, worker: TrustedWorkerAddress) -> AutoWorkerReset<'a> {
let reset = AutoWorkerReset {
workerscope: workerscope,
old_worker: workerscope.worker.borrow().clone()
};
*workerscope.worker.borrow_mut() = Some(worker);
reset
}
}
#[unsafe_destructor]
impl<'a> Drop for AutoWorkerReset<'a> {
fn drop(&mut self) {
*self.workerscope.worker.borrow_mut() = self.old_worker.clone();
}
}
#[dom_struct]
pub struct DedicatedWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope,
receiver: Receiver<ScriptMsg>,
receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>,
own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
worker: DOMRefCell<Option<TrustedWorkerAddress>>,
/// Sender to the parent thread.
parent_sender: ScriptChan,
worker: TrustedWorkerAddress,
parent_sender: Box<ScriptChan+Send>,
}
impl DedicatedWorkerGlobalScope {
fn new_inherited(worker_url: Url,
worker: TrustedWorkerAddress,
cx: Rc<Cx>,
resource_task: ResourceTask,
parent_sender: ScriptChan,
own_sender: ScriptChan,
receiver: Receiver<ScriptMsg>)
parent_sender: Box<ScriptChan+Send>,
own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
-> DedicatedWorkerGlobalScope {
DedicatedWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited(
WorkerGlobalScopeTypeId::DedicatedGlobalScope, worker_url, cx,
resource_task, own_sender),
WorkerGlobalScopeTypeId::DedicatedGlobalScope, worker_url, cx, resource_task),
receiver: receiver,
own_sender: own_sender,
parent_sender: parent_sender,
worker: worker,
worker: DOMRefCell::new(None),
}
}
pub fn new(worker_url: Url,
worker: TrustedWorkerAddress,
cx: Rc<Cx>,
resource_task: ResourceTask,
parent_sender: ScriptChan,
own_sender: ScriptChan,
receiver: Receiver<ScriptMsg>)
parent_sender: Box<ScriptChan+Send>,
own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
-> Temporary<DedicatedWorkerGlobalScope> {
let scope = box DedicatedWorkerGlobalScope::new_inherited(
worker_url, worker, cx.clone(), resource_task, parent_sender,
worker_url, cx.clone(), resource_task, parent_sender,
own_sender, receiver);
DedicatedWorkerGlobalScopeBinding::Wrap(cx.ptr, scope)
}
@ -82,9 +132,9 @@ impl DedicatedWorkerGlobalScope {
pub fn run_worker_scope(worker_url: Url,
worker: TrustedWorkerAddress,
resource_task: ResourceTask,
parent_sender: ScriptChan,
own_sender: ScriptChan,
receiver: Receiver<ScriptMsg>) {
parent_sender: Box<ScriptChan+Send>,
own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>) {
spawn_named_native(format!("WebWorker for {}", worker_url.serialize()), proc() {
task_state::initialize(SCRIPT | IN_WORKER);
@ -104,44 +154,25 @@ impl DedicatedWorkerGlobalScope {
let (_js_runtime, js_context) = ScriptTask::new_rt_and_cx();
let global = DedicatedWorkerGlobalScope::new(
worker_url, worker, js_context.clone(), resource_task,
worker_url, js_context.clone(), resource_task,
parent_sender, own_sender, receiver).root();
match js_context.evaluate_script(
global.reflector().get_jsobject(), source, url.serialize(), 1) {
Ok(_) => (),
Err(_) => println!("evaluate_script failed")
{
let _ar = AutoWorkerReset::new(*global, worker);
match js_context.evaluate_script(
global.reflector().get_jsobject(), source, url.serialize(), 1) {
Ok(_) => (),
Err(_) => println!("evaluate_script failed")
}
}
let scope: JSRef<WorkerGlobalScope> =
WorkerGlobalScopeCast::from_ref(*global);
let target: JSRef<EventTarget> =
EventTargetCast::from_ref(*global);
loop {
match global.receiver.recv_opt() {
Ok(ScriptMsg::DOMMessage(_addr, data, nbytes)) => {
let mut message = UndefinedValue();
unsafe {
assert!(JS_ReadStructuredClone(
js_context.ptr, data as *const u64, nbytes,
JS_STRUCTURED_CLONE_VERSION, &mut message,
ptr::null(), ptr::null_mut()) != 0);
}
MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message);
},
Ok(ScriptMsg::RunnableMsg(runnable)) => {
runnable.handler()
},
Ok(ScriptMsg::WorkerPostMessage(addr, data, nbytes)) => {
Worker::handle_message(addr, data, nbytes);
},
Ok(ScriptMsg::RefcountCleanup(addr)) => {
LiveDOMReferences::cleanup(js_context.ptr, addr);
Ok((linked_worker, msg)) => {
let _ar = AutoWorkerReset::new(*global, linked_worker);
global.handle_event(msg);
}
Ok(ScriptMsg::FireTimer(TimerSource::FromWorker, timer_id)) => {
scope.handle_fire_timer(timer_id);
}
Ok(_) => panic!("Unexpected message"),
Err(_) => break,
}
}
@ -149,6 +180,58 @@ impl DedicatedWorkerGlobalScope {
}
}
pub trait DedicatedWorkerGlobalScopeHelpers {
fn script_chan(self) -> Box<ScriptChan+Send>;
}
impl<'a> DedicatedWorkerGlobalScopeHelpers for JSRef<'a, DedicatedWorkerGlobalScope> {
fn script_chan(self) -> Box<ScriptChan+Send> {
box SendableWorkerScriptChan {
sender: self.own_sender.clone(),
worker: self.worker.borrow().as_ref().unwrap().clone(),
}
}
}
trait PrivateDedicatedWorkerGlobalScopeHelpers {
fn handle_event(self, msg: ScriptMsg);
}
impl<'a> PrivateDedicatedWorkerGlobalScopeHelpers for JSRef<'a, DedicatedWorkerGlobalScope> {
fn handle_event(self, msg: ScriptMsg) {
match msg {
ScriptMsg::DOMMessage(data, nbytes) => {
let mut message = UndefinedValue();
let scope: JSRef<WorkerGlobalScope> = WorkerGlobalScopeCast::from_ref(self);
unsafe {
assert!(JS_ReadStructuredClone(
scope.get_cx(), data as *const u64, nbytes,
JS_STRUCTURED_CLONE_VERSION, &mut message,
ptr::null(), ptr::null_mut()) != 0);
}
let target: JSRef<EventTarget> = EventTargetCast::from_ref(self);
MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message);
},
ScriptMsg::RunnableMsg(runnable) => {
runnable.handler()
},
ScriptMsg::WorkerPostMessage(addr, data, nbytes) => {
Worker::handle_message(addr, data, nbytes);
},
ScriptMsg::RefcountCleanup(addr) => {
let scope: JSRef<WorkerGlobalScope> = WorkerGlobalScopeCast::from_ref(self);
LiveDOMReferences::cleanup(scope.get_cx(), addr);
}
ScriptMsg::FireTimer(TimerSource::FromWorker, timer_id) => {
let scope: JSRef<WorkerGlobalScope> = WorkerGlobalScopeCast::from_ref(self);
scope.handle_fire_timer(timer_id);
}
_ => panic!("Unexpected message"),
}
}
}
impl<'a> DedicatedWorkerGlobalScopeMethods for JSRef<'a, DedicatedWorkerGlobalScope> {
fn PostMessage(self, cx: *mut JSContext, message: JSVal) -> ErrorResult {
let mut data = ptr::null_mut();
@ -162,8 +245,8 @@ impl<'a> DedicatedWorkerGlobalScopeMethods for JSRef<'a, DedicatedWorkerGlobalSc
return Err(DataClone);
}
let ScriptChan(ref sender) = self.parent_sender;
sender.send(ScriptMsg::WorkerPostMessage(self.worker.clone(), data, nbytes));
let worker = self.worker.borrow().as_ref().unwrap().clone();
self.parent_sender.send(ScriptMsg::WorkerPostMessage(worker, data, nbytes));
Ok(())
}