serviceworker: turn-off event-loop, don't assume current scope, clear runtime on shutdown

This commit is contained in:
Gregory Terzian 2020-04-30 14:43:15 +08:00
parent 89eb7c2aa2
commit bd31860c5d
3 changed files with 49 additions and 46 deletions

View file

@ -268,15 +268,12 @@ impl ServiceWorkerGlobalScope {
} = scope_things; } = scope_things;
let serialized_worker_url = script_url.to_string(); let serialized_worker_url = script_url.to_string();
let origin = GlobalScope::current() let origin = scope_url.origin();
.expect("No current global object")
.origin()
.immutable()
.clone();
thread::Builder::new() thread::Builder::new()
.name(format!("ServiceWorker for {}", serialized_worker_url)) .name(format!("ServiceWorker for {}", serialized_worker_url))
.spawn(move || { .spawn(move || {
thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER); thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER);
let runtime = new_rt_and_cx(None);
let roots = RootCollection::new(); let roots = RootCollection::new();
let _stack_roots = ThreadLocalStackRoots::new(&roots); let _stack_roots = ThreadLocalStackRoots::new(&roots);
@ -298,11 +295,31 @@ impl ServiceWorkerGlobalScope {
.referrer_policy(referrer_policy) .referrer_policy(referrer_policy)
.origin(origin); .origin(origin);
let (url, source) = match load_whole_resource( // Service workers are time limited
request, // https://w3c.github.io/ServiceWorker/#service-worker-lifetime
&init.resource_threads.sender(), let sw_lifetime_timeout = pref!(dom.serviceworker.timeout_seconds) as u64;
&GlobalScope::current().expect("No current global object"), let time_out_port = after(Duration::new(sw_lifetime_timeout, 0));
) {
let (devtools_mpsc_chan, devtools_mpsc_port) = unbounded();
ROUTER
.route_ipc_receiver_to_crossbeam_sender(devtools_receiver, devtools_mpsc_chan);
let resource_threads_sender = init.resource_threads.sender();
let global = ServiceWorkerGlobalScope::new(
init,
script_url,
devtools_mpsc_port,
runtime,
own_sender,
receiver,
time_out_port,
swmanager_sender,
scope_url,
);
let (_url, source) =
match load_whole_resource(request, &resource_threads_sender, &*global.upcast())
{
Err(_) => { Err(_) => {
println!("error loading script {}", serialized_worker_url); println!("error loading script {}", serialized_worker_url);
return; return;
@ -312,28 +329,6 @@ impl ServiceWorkerGlobalScope {
}, },
}; };
let runtime = new_rt_and_cx(None);
let (devtools_mpsc_chan, devtools_mpsc_port) = unbounded();
ROUTER
.route_ipc_receiver_to_crossbeam_sender(devtools_receiver, devtools_mpsc_chan);
// Service workers are time limited
// https://w3c.github.io/ServiceWorker/#service-worker-lifetime
let sw_lifetime_timeout = pref!(dom.serviceworker.timeout_seconds) as u64;
let time_out_port = after(Duration::new(sw_lifetime_timeout, 0));
let global = ServiceWorkerGlobalScope::new(
init,
url,
devtools_mpsc_port,
runtime,
own_sender,
receiver,
time_out_port,
swmanager_sender,
scope_url,
);
let scope = global.upcast::<WorkerGlobalScope>(); let scope = global.upcast::<WorkerGlobalScope>();
unsafe { unsafe {
@ -356,7 +351,7 @@ impl ServiceWorkerGlobalScope {
// until the event loop is destroyed, // until the event loop is destroyed,
// which happens after the closing flag is set to true, // which happens after the closing flag is set to true,
// or until the worker has run beyond its allocated time. // or until the worker has run beyond its allocated time.
while !scope.is_closing() || !global.has_timed_out() { while !scope.is_closing() && !global.has_timed_out() {
run_worker_event_loop(&*global, None); run_worker_event_loop(&*global, None);
} }
}, },
@ -364,6 +359,7 @@ impl ServiceWorkerGlobalScope {
scope.script_chan(), scope.script_chan(),
CommonScriptMsg::CollectReports, CommonScriptMsg::CollectReports,
); );
scope.clear_js_runtime();
}) })
.expect("Thread spawning failed"); .expect("Thread spawning failed");
} }
@ -390,15 +386,10 @@ impl ServiceWorkerGlobalScope {
} }
fn has_timed_out(&self) -> bool { fn has_timed_out(&self) -> bool {
// Note: this should be included in the `select` inside `run_worker_event_loop`, // TODO: https://w3c.github.io/ServiceWorker/#service-worker-lifetime
// otherwise a block on the select can prevent the timeout. // Since we don't have a shutdown mechanism yet, see #26548
if self.time_out_port.try_recv().is_ok() { // immediately stop the event-loop after executing the initial script.
let _ = self true
.swmanager_sender
.send(ServiceWorkerMsg::Timeout(self.scope_url.clone()));
return true;
}
false
} }
fn handle_script_event(&self, msg: ServiceWorkerScriptMsg) { fn handle_script_event(&self, msg: ServiceWorkerScriptMsg) {

View file

@ -100,7 +100,7 @@ pub struct WorkerGlobalScope {
#[ignore_malloc_size_of = "Arc"] #[ignore_malloc_size_of = "Arc"]
closing: Option<Arc<AtomicBool>>, closing: Option<Arc<AtomicBool>>,
#[ignore_malloc_size_of = "Defined in js"] #[ignore_malloc_size_of = "Defined in js"]
runtime: Runtime, runtime: DomRefCell<Option<Runtime>>,
location: MutNullableDom<WorkerLocation>, location: MutNullableDom<WorkerLocation>,
navigator: MutNullableDom<WorkerNavigator>, navigator: MutNullableDom<WorkerNavigator>,
@ -151,7 +151,7 @@ impl WorkerGlobalScope {
worker_type, worker_type,
worker_url: DomRefCell::new(worker_url), worker_url: DomRefCell::new(worker_url),
closing, closing,
runtime, runtime: DomRefCell::new(Some(runtime)),
location: Default::default(), location: Default::default(),
navigator: Default::default(), navigator: Default::default(),
from_devtools_sender: init.from_devtools_sender, from_devtools_sender: init.from_devtools_sender,
@ -161,8 +161,17 @@ impl WorkerGlobalScope {
} }
} }
pub fn clear_js_runtime(&self) {
let runtime = self.runtime.borrow_mut().take();
drop(runtime);
}
pub fn runtime_handle(&self) -> ParentRuntime { pub fn runtime_handle(&self) -> ParentRuntime {
self.runtime.prepare_for_new_child() self.runtime
.borrow()
.as_ref()
.unwrap()
.prepare_for_new_child()
} }
pub fn from_devtools_sender(&self) -> Option<IpcSender<DevtoolScriptControlMsg>> { pub fn from_devtools_sender(&self) -> Option<IpcSender<DevtoolScriptControlMsg>> {
@ -175,7 +184,7 @@ impl WorkerGlobalScope {
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub fn get_cx(&self) -> JSContext { pub fn get_cx(&self) -> JSContext {
unsafe { JSContext::from_ptr(self.runtime.cx()) } unsafe { JSContext::from_ptr(self.runtime.borrow().as_ref().unwrap().cx()) }
} }
pub fn is_closing(&self) -> bool { pub fn is_closing(&self) -> bool {
@ -235,7 +244,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
}; };
} }
rooted!(in(self.runtime.cx()) let mut rval = UndefinedValue()); rooted!(in(self.runtime.borrow().as_ref().unwrap().cx()) let mut rval = UndefinedValue());
for url in urls { for url in urls {
let global_scope = self.upcast::<GlobalScope>(); let global_scope = self.upcast::<GlobalScope>();
let request = NetRequestInit::new(url.clone()) let request = NetRequestInit::new(url.clone())
@ -256,7 +265,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
Ok((metadata, bytes)) => (metadata.final_url, String::from_utf8(bytes).unwrap()), Ok((metadata, bytes)) => (metadata.final_url, String::from_utf8(bytes).unwrap()),
}; };
let result = self.runtime.evaluate_script( let result = self.runtime.borrow().as_ref().unwrap().evaluate_script(
self.reflector().get_jsobject(), self.reflector().get_jsobject(),
&source, &source,
url.as_str(), url.as_str(),
@ -401,8 +410,9 @@ impl WorkerGlobalScope {
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub fn execute_script(&self, source: DOMString) { pub fn execute_script(&self, source: DOMString) {
let _aes = AutoEntryScript::new(self.upcast()); let _aes = AutoEntryScript::new(self.upcast());
rooted!(in(self.runtime.cx()) let mut rval = UndefinedValue()); let cx = self.runtime.borrow().as_ref().unwrap().cx();
match self.runtime.evaluate_script( rooted!(in(cx) let mut rval = UndefinedValue());
match self.runtime.borrow().as_ref().unwrap().evaluate_script(
self.reflector().get_jsobject(), self.reflector().get_jsobject(),
&source, &source,
self.worker_url.borrow().as_str(), self.worker_url.borrow().as_str(),
@ -419,7 +429,7 @@ impl WorkerGlobalScope {
println!("evaluate_script failed"); println!("evaluate_script failed");
unsafe { unsafe {
let ar = enter_realm(&*self); let ar = enter_realm(&*self);
report_pending_exception(self.runtime.cx(), true, InRealm::Entered(&ar)); report_pending_exception(cx, true, InRealm::Entered(&ar));
} }
} }
}, },

View file

@ -212,7 +212,9 @@ impl ServiceWorkerManager {
fn handle_message_from_constellation(&mut self, msg: ServiceWorkerMsg) -> bool { fn handle_message_from_constellation(&mut self, msg: ServiceWorkerMsg) -> bool {
match msg { match msg {
ServiceWorkerMsg::Timeout(scope) => {}, ServiceWorkerMsg::Timeout(_scope) => {
// TODO: https://w3c.github.io/ServiceWorker/#terminate-service-worker
},
ServiceWorkerMsg::ForwardDOMMessage(msg, scope_url) => { ServiceWorkerMsg::ForwardDOMMessage(msg, scope_url) => {
if let Some(registration) = self.registrations.get_mut(&scope_url) { if let Some(registration) = self.registrations.get_mut(&scope_url) {
if let Some(ref worker) = registration.active_worker { if let Some(ref worker) = registration.active_worker {