mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
Generalize promise job queue into solitary microtask queue.
This commit is contained in:
parent
bac2f68b54
commit
32d4f84a30
8 changed files with 137 additions and 144 deletions
|
@ -230,17 +230,23 @@ impl DedicatedWorkerGlobalScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let _ar = AutoWorkerReset::new(&global, worker);
|
let _ar = AutoWorkerReset::new(&global, worker.clone());
|
||||||
scope.execute_script(DOMString::from(source));
|
scope.execute_script(DOMString::from(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
let reporter_name = format!("dedicated-worker-reporter-{}", random::<u64>());
|
let reporter_name = format!("dedicated-worker-reporter-{}", random::<u64>());
|
||||||
scope.upcast::<GlobalScope>().mem_profiler_chan().run_with_memory_reporting(|| {
|
scope.upcast::<GlobalScope>().mem_profiler_chan().run_with_memory_reporting(|| {
|
||||||
|
// https://html.spec.whatwg.org/multipage/#event-loop-processing-model
|
||||||
|
// Step 1
|
||||||
while let Ok(event) = global.receive_event() {
|
while let Ok(event) = global.receive_event() {
|
||||||
if scope.is_closing() {
|
if scope.is_closing() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Step 3
|
||||||
global.handle_event(event);
|
global.handle_event(event);
|
||||||
|
// Step 6
|
||||||
|
let _ar = AutoWorkerReset::new(&global, worker.clone());
|
||||||
|
global.upcast::<WorkerGlobalScope>().perform_a_microtask_checkpoint();
|
||||||
}
|
}
|
||||||
}, reporter_name, parent_sender, CommonScriptMsg::CollectReports);
|
}, reporter_name, parent_sender, CommonScriptMsg::CollectReports);
|
||||||
}).expect("Thread spawning failed");
|
}).expect("Thread spawning failed");
|
||||||
|
|
|
@ -29,10 +29,11 @@ use js::jsapi::{JS_GetObjectRuntime, MutableHandleValue};
|
||||||
use js::panic::maybe_resume_unwind;
|
use js::panic::maybe_resume_unwind;
|
||||||
use js::rust::{CompileOptionsWrapper, Runtime, get_object_class};
|
use js::rust::{CompileOptionsWrapper, Runtime, get_object_class};
|
||||||
use libc;
|
use libc;
|
||||||
|
use microtask::Microtask;
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::{CoreResourceThread, ResourceThreads, IpcSend};
|
use net_traits::{CoreResourceThread, ResourceThreads, IpcSend};
|
||||||
use profile_traits::{mem, time};
|
use profile_traits::{mem, time};
|
||||||
use script_runtime::{CommonScriptMsg, EnqueuedPromiseCallback, ScriptChan, ScriptPort};
|
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort};
|
||||||
use script_thread::{MainThreadScriptChan, RunnableWrapper, ScriptThread};
|
use script_thread::{MainThreadScriptChan, RunnableWrapper, ScriptThread};
|
||||||
use script_traits::{MsDuration, ScriptMsg as ConstellationMsg, TimerEvent};
|
use script_traits::{MsDuration, ScriptMsg as ConstellationMsg, TimerEvent};
|
||||||
use script_traits::{TimerEventId, TimerEventRequest, TimerSource};
|
use script_traits::{TimerEventId, TimerEventRequest, TimerSource};
|
||||||
|
@ -448,25 +449,13 @@ impl GlobalScope {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start the process of executing the pending promise callbacks. They will be invoked
|
/// Enqueue a microtask for subsequent execution.
|
||||||
/// in FIFO order, synchronously, at some point in the future.
|
pub fn enqueue_microtask(&self, job: Microtask) {
|
||||||
pub fn flush_promise_jobs(&self) {
|
|
||||||
if self.is::<Window>() {
|
if self.is::<Window>() {
|
||||||
return ScriptThread::flush_promise_jobs(self);
|
return ScriptThread::enqueue_microtask(job);
|
||||||
}
|
}
|
||||||
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
|
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
|
||||||
return worker.flush_promise_jobs();
|
return worker.enqueue_microtask(job);
|
||||||
}
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enqueue a promise callback for subsequent execution.
|
|
||||||
pub fn enqueue_promise_job(&self, job: EnqueuedPromiseCallback) {
|
|
||||||
if self.is::<Window>() {
|
|
||||||
return ScriptThread::enqueue_promise_job(job, self);
|
|
||||||
}
|
|
||||||
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
|
|
||||||
return worker.enqueue_promise_job(job);
|
|
||||||
}
|
}
|
||||||
unreachable!();
|
unreachable!();
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,10 +210,15 @@ impl ServiceWorkerGlobalScope {
|
||||||
global.dispatch_activate();
|
global.dispatch_activate();
|
||||||
let reporter_name = format!("service-worker-reporter-{}", random::<u64>());
|
let reporter_name = format!("service-worker-reporter-{}", random::<u64>());
|
||||||
scope.upcast::<GlobalScope>().mem_profiler_chan().run_with_memory_reporting(|| {
|
scope.upcast::<GlobalScope>().mem_profiler_chan().run_with_memory_reporting(|| {
|
||||||
|
// https://html.spec.whatwg.org/multipage/#event-loop-processing-model
|
||||||
|
// Step 1
|
||||||
while let Ok(event) = global.receive_event() {
|
while let Ok(event) = global.receive_event() {
|
||||||
|
// Step 3
|
||||||
if !global.handle_event(event) {
|
if !global.handle_event(event) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Step 6
|
||||||
|
global.upcast::<WorkerGlobalScope>().perform_a_microtask_checkpoint();
|
||||||
}
|
}
|
||||||
}, reporter_name, scope.script_chan(), CommonScriptMsg::CollectReports);
|
}, reporter_name, scope.script_chan(), CommonScriptMsg::CollectReports);
|
||||||
}).expect("Thread spawning failed");
|
}).expect("Thread spawning failed");
|
||||||
|
|
|
@ -11,7 +11,6 @@ use dom::bindings::codegen::UnionTypes::RequestOrUSVString;
|
||||||
use dom::bindings::error::{Error, ErrorResult, Fallible, report_pending_exception};
|
use dom::bindings::error::{Error, ErrorResult, Fallible, report_pending_exception};
|
||||||
use dom::bindings::inheritance::Castable;
|
use dom::bindings::inheritance::Castable;
|
||||||
use dom::bindings::js::{MutNullableJS, Root};
|
use dom::bindings::js::{MutNullableJS, Root};
|
||||||
use dom::bindings::refcounted::Trusted;
|
|
||||||
use dom::bindings::reflector::DomObject;
|
use dom::bindings::reflector::DomObject;
|
||||||
use dom::bindings::settings_stack::AutoEntryScript;
|
use dom::bindings::settings_stack::AutoEntryScript;
|
||||||
use dom::bindings::str::DOMString;
|
use dom::bindings::str::DOMString;
|
||||||
|
@ -29,11 +28,11 @@ use js::jsapi::{HandleValue, JSAutoCompartment, JSContext, JSRuntime};
|
||||||
use js::jsval::UndefinedValue;
|
use js::jsval::UndefinedValue;
|
||||||
use js::panic::maybe_resume_unwind;
|
use js::panic::maybe_resume_unwind;
|
||||||
use js::rust::Runtime;
|
use js::rust::Runtime;
|
||||||
|
use microtask::{MicrotaskQueue, Microtask};
|
||||||
use net_traits::{IpcSend, load_whole_resource};
|
use net_traits::{IpcSend, load_whole_resource};
|
||||||
use net_traits::request::{CredentialsMode, Destination, RequestInit as NetRequestInit, Type as RequestType};
|
use net_traits::request::{CredentialsMode, Destination, RequestInit as NetRequestInit, Type as RequestType};
|
||||||
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort};
|
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort};
|
||||||
use script_runtime::{ScriptThreadEventCategory, PromiseJobQueue, EnqueuedPromiseCallback};
|
use script_thread::RunnableWrapper;
|
||||||
use script_thread::{Runnable, RunnableWrapper};
|
|
||||||
use script_traits::{TimerEvent, TimerEventId};
|
use script_traits::{TimerEvent, TimerEventId};
|
||||||
use script_traits::WorkerGlobalScopeInit;
|
use script_traits::WorkerGlobalScopeInit;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
@ -87,7 +86,7 @@ pub struct WorkerGlobalScope {
|
||||||
/// `IpcSender` doesn't exist
|
/// `IpcSender` doesn't exist
|
||||||
from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
|
from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
|
||||||
|
|
||||||
promise_job_queue: PromiseJobQueue,
|
microtask_queue: MicrotaskQueue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkerGlobalScope {
|
impl WorkerGlobalScope {
|
||||||
|
@ -117,7 +116,7 @@ impl WorkerGlobalScope {
|
||||||
navigator: Default::default(),
|
navigator: Default::default(),
|
||||||
from_devtools_sender: init.from_devtools_sender,
|
from_devtools_sender: init.from_devtools_sender,
|
||||||
from_devtools_receiver: from_devtools_receiver,
|
from_devtools_receiver: from_devtools_receiver,
|
||||||
promise_job_queue: PromiseJobQueue::new(),
|
microtask_queue: MicrotaskQueue::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,20 +158,12 @@ impl WorkerGlobalScope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enqueue_promise_job(&self, job: EnqueuedPromiseCallback) {
|
pub fn enqueue_microtask(&self, job: Microtask) {
|
||||||
self.promise_job_queue.enqueue(job, self.upcast());
|
self.microtask_queue.enqueue(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush_promise_jobs(&self) {
|
pub fn perform_a_microtask_checkpoint(&self) {
|
||||||
self.script_chan().send(CommonScriptMsg::RunnableMsg(
|
self.microtask_queue.checkpoint(|id| {
|
||||||
ScriptThreadEventCategory::WorkerEvent,
|
|
||||||
box FlushPromiseJobs {
|
|
||||||
global: Trusted::new(self),
|
|
||||||
})).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_flush_promise_jobs(&self) {
|
|
||||||
self.promise_job_queue.flush_promise_jobs(|id| {
|
|
||||||
let global = self.upcast::<GlobalScope>();
|
let global = self.upcast::<GlobalScope>();
|
||||||
assert_eq!(global.pipeline_id(), id);
|
assert_eq!(global.pipeline_id(), id);
|
||||||
Some(Root::from_ref(global))
|
Some(Root::from_ref(global))
|
||||||
|
@ -393,6 +384,8 @@ impl WorkerGlobalScope {
|
||||||
} else {
|
} else {
|
||||||
panic!("need to implement a sender for SharedWorker")
|
panic!("need to implement a sender for SharedWorker")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//XXXjdm should we do a microtask checkpoint here?
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_fire_timer(&self, timer_id: TimerEventId) {
|
pub fn handle_fire_timer(&self, timer_id: TimerEventId) {
|
||||||
|
@ -405,14 +398,3 @@ impl WorkerGlobalScope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FlushPromiseJobs {
|
|
||||||
global: Trusted<WorkerGlobalScope>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Runnable for FlushPromiseJobs {
|
|
||||||
fn handler(self: Box<FlushPromiseJobs>) {
|
|
||||||
let global = self.global.root();
|
|
||||||
global.do_flush_promise_jobs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -108,6 +108,7 @@ mod dom;
|
||||||
pub mod fetch;
|
pub mod fetch;
|
||||||
pub mod layout_wrapper;
|
pub mod layout_wrapper;
|
||||||
mod mem;
|
mod mem;
|
||||||
|
mod microtask;
|
||||||
mod network_listener;
|
mod network_listener;
|
||||||
pub mod origin;
|
pub mod origin;
|
||||||
pub mod script_runtime;
|
pub mod script_runtime;
|
||||||
|
|
83
components/script/microtask.rs
Normal file
83
components/script/microtask.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
//! Implementation of [microtasks](https://html.spec.whatwg.org/multipage/#microtask) and
|
||||||
|
//! microtask queues. It is up to implementations of event loops to store a queue and
|
||||||
|
//! perform checkpoints at appropriate times, as well as enqueue microtasks as required.
|
||||||
|
|
||||||
|
use dom::bindings::callback::ExceptionHandling;
|
||||||
|
use dom::bindings::cell::DOMRefCell;
|
||||||
|
use dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback;
|
||||||
|
use dom::bindings::js::Root;
|
||||||
|
use dom::globalscope::GlobalScope;
|
||||||
|
use msg::constellation_msg::PipelineId;
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::mem;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
/// A collection of microtasks in FIFO order.
|
||||||
|
#[derive(JSTraceable, HeapSizeOf, Default)]
|
||||||
|
pub struct MicrotaskQueue {
|
||||||
|
/// The list of enqueued microtasks that will be invoked at the next microtask checkpoint.
|
||||||
|
microtask_queue: DOMRefCell<Vec<Microtask>>,
|
||||||
|
/// https://html.spec.whatwg.org/multipage/#performing-a-microtask-checkpoint
|
||||||
|
performing_a_microtask_checkpoint: Cell<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(JSTraceable, HeapSizeOf)]
|
||||||
|
pub enum Microtask {
|
||||||
|
Promise(EnqueuedPromiseCallback),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A promise callback scheduled to run during the next microtask checkpoint (#4283).
|
||||||
|
#[derive(JSTraceable, HeapSizeOf)]
|
||||||
|
pub struct EnqueuedPromiseCallback {
|
||||||
|
#[ignore_heap_size_of = "Rc has unclear ownership"]
|
||||||
|
pub callback: Rc<PromiseJobCallback>,
|
||||||
|
pub pipeline: PipelineId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MicrotaskQueue {
|
||||||
|
/// Add a new microtask to this queue. It will be invoked as part of the next
|
||||||
|
/// microtask checkpoint.
|
||||||
|
pub fn enqueue(&self, job: Microtask) {
|
||||||
|
self.microtask_queue.borrow_mut().push(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://html.spec.whatwg.org/multipage/#perform-a-microtask-checkpoint
|
||||||
|
/// Perform a microtask checkpoint, executing all queued microtasks until the queue is empty.
|
||||||
|
pub fn checkpoint<F>(&self, target_provider: F)
|
||||||
|
where F: Fn(PipelineId) -> Option<Root<GlobalScope>>
|
||||||
|
{
|
||||||
|
if self.performing_a_microtask_checkpoint.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1
|
||||||
|
self.performing_a_microtask_checkpoint.set(true);
|
||||||
|
|
||||||
|
// Steps 2-7
|
||||||
|
while !self.microtask_queue.borrow().is_empty() {
|
||||||
|
rooted_vec!(let mut pending_queue);
|
||||||
|
mem::swap(
|
||||||
|
&mut *pending_queue,
|
||||||
|
&mut *self.microtask_queue.borrow_mut());
|
||||||
|
|
||||||
|
for job in pending_queue.iter() {
|
||||||
|
match *job {
|
||||||
|
Microtask::Promise(ref job) => {
|
||||||
|
if let Some(target) = target_provider(job.pipeline) {
|
||||||
|
let _ = job.callback.Call_(&*target, ExceptionHandling::Report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Step 8 - notify about rejected promises
|
||||||
|
|
||||||
|
// Step 9
|
||||||
|
self.performing_a_microtask_checkpoint.set(false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,10 +5,8 @@
|
||||||
//! The script runtime contains common traits and structs commonly used by the
|
//! The script runtime contains common traits and structs commonly used by the
|
||||||
//! script thread, the dom, and the worker threads.
|
//! script thread, the dom, and the worker threads.
|
||||||
|
|
||||||
use dom::bindings::callback::ExceptionHandling;
|
|
||||||
use dom::bindings::cell::DOMRefCell;
|
|
||||||
use dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback;
|
use dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback;
|
||||||
use dom::bindings::js::{Root, RootCollection, RootCollectionPtr, trace_roots};
|
use dom::bindings::js::{RootCollection, RootCollectionPtr, trace_roots};
|
||||||
use dom::bindings::refcounted::{LiveDOMReferences, trace_refcounted_objects};
|
use dom::bindings::refcounted::{LiveDOMReferences, trace_refcounted_objects};
|
||||||
use dom::bindings::settings_stack;
|
use dom::bindings::settings_stack;
|
||||||
use dom::bindings::trace::{JSTraceable, trace_traceables};
|
use dom::bindings::trace::{JSTraceable, trace_traceables};
|
||||||
|
@ -23,7 +21,7 @@ use js::jsapi::{JSJitCompilerOption, JS_SetOffthreadIonCompilationEnabled, JS_Se
|
||||||
use js::jsapi::{JSObject, RuntimeOptionsRef, SetPreserveWrapperCallback, SetEnqueuePromiseJobCallback};
|
use js::jsapi::{JSObject, RuntimeOptionsRef, SetPreserveWrapperCallback, SetEnqueuePromiseJobCallback};
|
||||||
use js::panic::wrap_panic;
|
use js::panic::wrap_panic;
|
||||||
use js::rust::Runtime;
|
use js::rust::Runtime;
|
||||||
use msg::constellation_msg::PipelineId;
|
use microtask::{EnqueuedPromiseCallback, Microtask};
|
||||||
use profile_traits::mem::{Report, ReportKind, ReportsChan};
|
use profile_traits::mem::{Report, ReportKind, ReportsChan};
|
||||||
use script_thread::{Runnable, STACK_ROOTS, trace_thread};
|
use script_thread::{Runnable, STACK_ROOTS, trace_thread};
|
||||||
use servo_config::opts;
|
use servo_config::opts;
|
||||||
|
@ -35,7 +33,6 @@ use std::os;
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::panic::AssertUnwindSafe;
|
use std::panic::AssertUnwindSafe;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::rc::Rc;
|
|
||||||
use style::thread_state;
|
use style::thread_state;
|
||||||
use time::{Tm, now};
|
use time::{Tm, now};
|
||||||
|
|
||||||
|
@ -107,86 +104,21 @@ impl<'a> Drop for StackRootTLS<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A promise callback scheduled to run during the next microtask checkpoint (#4283).
|
/// SM callback for promise job resolution. Adds a promise callback to the current
|
||||||
#[derive(JSTraceable, HeapSizeOf)]
|
/// global's microtask queue.
|
||||||
pub struct EnqueuedPromiseCallback {
|
|
||||||
#[ignore_heap_size_of = "Rc has unclear ownership"]
|
|
||||||
callback: Rc<PromiseJobCallback>,
|
|
||||||
pipeline: PipelineId,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A collection of promise callbacks in FIFO order.
|
|
||||||
#[derive(JSTraceable, HeapSizeOf)]
|
|
||||||
pub struct PromiseJobQueue {
|
|
||||||
/// A snapshot of `promise_job_queue` that was taken at the start of the microtask checkpoint.
|
|
||||||
/// Used to work around mutability errors when appending new promise jobs while performing
|
|
||||||
/// a microtask checkpoint.
|
|
||||||
flushing_job_queue: DOMRefCell<Vec<EnqueuedPromiseCallback>>,
|
|
||||||
/// The list of enqueued promise callbacks that will be invoked at the next microtask checkpoint.
|
|
||||||
promise_job_queue: DOMRefCell<Vec<EnqueuedPromiseCallback>>,
|
|
||||||
/// True if there is an outstanding runnable responsible for evaluating the promise job queue.
|
|
||||||
/// This prevents runnables flooding the event queue needlessly, since the first one will
|
|
||||||
/// execute all pending runnables.
|
|
||||||
pending_promise_job_runnable: Cell<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PromiseJobQueue {
|
|
||||||
/// Create a new PromiseJobQueue instance.
|
|
||||||
pub fn new() -> PromiseJobQueue {
|
|
||||||
PromiseJobQueue {
|
|
||||||
promise_job_queue: DOMRefCell::new(vec![]),
|
|
||||||
flushing_job_queue: DOMRefCell::new(vec![]),
|
|
||||||
pending_promise_job_runnable: Cell::new(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a new promise job callback to this queue. It will be invoked as part of the next
|
|
||||||
/// microtask checkpoint.
|
|
||||||
pub fn enqueue(&self, job: EnqueuedPromiseCallback, global: &GlobalScope) {
|
|
||||||
self.promise_job_queue.borrow_mut().push(job);
|
|
||||||
if !self.pending_promise_job_runnable.get() {
|
|
||||||
self.pending_promise_job_runnable.set(true);
|
|
||||||
global.flush_promise_jobs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform a microtask checkpoint, by invoking all of the pending promise job callbacks in
|
|
||||||
/// FIFO order (#4283).
|
|
||||||
pub fn flush_promise_jobs<F>(&self, target_provider: F)
|
|
||||||
where F: Fn(PipelineId) -> Option<Root<GlobalScope>>
|
|
||||||
{
|
|
||||||
self.pending_promise_job_runnable.set(false);
|
|
||||||
{
|
|
||||||
let mut pending_queue = self.promise_job_queue.borrow_mut();
|
|
||||||
*self.flushing_job_queue.borrow_mut() = pending_queue.drain(..).collect();
|
|
||||||
}
|
|
||||||
// N.B. borrowing this vector is safe w.r.t. mutability, since any promise job that
|
|
||||||
// is enqueued while invoking these callbacks will be placed in `pending_queue`;
|
|
||||||
// `flushing_queue` is a static snapshot during this checkpoint.
|
|
||||||
for job in &*self.flushing_job_queue.borrow() {
|
|
||||||
if let Some(target) = target_provider(job.pipeline) {
|
|
||||||
let _ = job.callback.Call_(&*target, ExceptionHandling::Report);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.flushing_job_queue.borrow_mut().clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SM callback for promise job resolution. Adds a promise callback to the current global's
|
|
||||||
/// promise job queue, and enqueues a runnable to perform a microtask checkpoint if one
|
|
||||||
/// is not already pending.
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe extern "C" fn enqueue_job(cx: *mut JSContext,
|
unsafe extern "C" fn enqueue_job(cx: *mut JSContext,
|
||||||
job: HandleObject,
|
job: HandleObject,
|
||||||
_allocation_site: HandleObject,
|
_allocation_site: HandleObject,
|
||||||
_data: *mut c_void) -> bool {
|
_data: *mut c_void) -> bool {
|
||||||
wrap_panic(AssertUnwindSafe(|| {
|
wrap_panic(AssertUnwindSafe(|| {
|
||||||
|
//XXXjdm - use a different global now?
|
||||||
let global = GlobalScope::from_object(job.get());
|
let global = GlobalScope::from_object(job.get());
|
||||||
let pipeline = global.pipeline_id();
|
let pipeline = global.pipeline_id();
|
||||||
global.enqueue_promise_job(EnqueuedPromiseCallback {
|
global.enqueue_microtask(Microtask::Promise(EnqueuedPromiseCallback {
|
||||||
callback: PromiseJobCallback::new(cx, job.get()),
|
callback: PromiseJobCallback::new(cx, job.get()),
|
||||||
pipeline: pipeline,
|
pipeline: pipeline,
|
||||||
});
|
}));
|
||||||
true
|
true
|
||||||
}), false)
|
}), false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ use js::jsval::UndefinedValue;
|
||||||
use js::rust::Runtime;
|
use js::rust::Runtime;
|
||||||
use layout_wrapper::ServoLayoutNode;
|
use layout_wrapper::ServoLayoutNode;
|
||||||
use mem::heap_size_of_self_and_children;
|
use mem::heap_size_of_self_and_children;
|
||||||
|
use microtask::{MicrotaskQueue, Microtask};
|
||||||
use msg::constellation_msg::{FrameId, FrameType, PipelineId, PipelineNamespace};
|
use msg::constellation_msg::{FrameId, FrameType, PipelineId, PipelineNamespace};
|
||||||
use net_traits::{CoreResourceMsg, FetchMetadata, FetchResponseListener};
|
use net_traits::{CoreResourceMsg, FetchMetadata, FetchResponseListener};
|
||||||
use net_traits::{IpcSend, Metadata, ReferrerPolicy, ResourceThreads};
|
use net_traits::{IpcSend, Metadata, ReferrerPolicy, ResourceThreads};
|
||||||
|
@ -81,8 +82,8 @@ use origin::Origin;
|
||||||
use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan};
|
use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan};
|
||||||
use profile_traits::time::{self, ProfilerCategory, profile};
|
use profile_traits::time::{self, ProfilerCategory, profile};
|
||||||
use script_layout_interface::message::{self, NewLayoutThreadInfo, ReflowQueryType};
|
use script_layout_interface::message::{self, NewLayoutThreadInfo, ReflowQueryType};
|
||||||
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory, EnqueuedPromiseCallback};
|
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
|
||||||
use script_runtime::{ScriptPort, StackRootTLS, get_reports, new_rt_and_cx, PromiseJobQueue};
|
use script_runtime::{ScriptPort, StackRootTLS, get_reports, new_rt_and_cx};
|
||||||
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
||||||
use script_traits::{DocumentActivity, DiscardBrowsingContext, EventResult};
|
use script_traits::{DocumentActivity, DiscardBrowsingContext, EventResult};
|
||||||
use script_traits::{InitialScriptState, LayoutMsg, LoadData, MouseButton, MouseEventType, MozBrowserEvent};
|
use script_traits::{InitialScriptState, LayoutMsg, LoadData, MouseButton, MouseEventType, MozBrowserEvent};
|
||||||
|
@ -98,6 +99,7 @@ use servo_config::opts;
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::{hash_map, HashMap, HashSet};
|
use std::collections::{hash_map, HashMap, HashSet};
|
||||||
|
use std::default::Default;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::option::Option;
|
use std::option::Option;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
@ -477,7 +479,7 @@ pub struct ScriptThread {
|
||||||
|
|
||||||
content_process_shutdown_chan: IpcSender<()>,
|
content_process_shutdown_chan: IpcSender<()>,
|
||||||
|
|
||||||
promise_job_queue: PromiseJobQueue,
|
microtask_queue: MicrotaskQueue,
|
||||||
|
|
||||||
/// A handle to the webvr thread, if available
|
/// A handle to the webvr thread, if available
|
||||||
webvr_thread: Option<IpcSender<WebVRMsg>>,
|
webvr_thread: Option<IpcSender<WebVRMsg>>,
|
||||||
|
@ -694,7 +696,7 @@ impl ScriptThread {
|
||||||
|
|
||||||
content_process_shutdown_chan: state.content_process_shutdown_chan,
|
content_process_shutdown_chan: state.content_process_shutdown_chan,
|
||||||
|
|
||||||
promise_job_queue: PromiseJobQueue::new(),
|
microtask_queue: MicrotaskQueue::default(),
|
||||||
|
|
||||||
layout_to_constellation_chan: state.layout_to_constellation_chan,
|
layout_to_constellation_chan: state.layout_to_constellation_chan,
|
||||||
|
|
||||||
|
@ -776,6 +778,7 @@ impl ScriptThread {
|
||||||
let mut mouse_move_event_index = None;
|
let mut mouse_move_event_index = None;
|
||||||
let mut animation_ticks = HashSet::new();
|
let mut animation_ticks = HashSet::new();
|
||||||
loop {
|
loop {
|
||||||
|
// https://html.spec.whatwg.org/multipage/#event-loop-processing-model step 7
|
||||||
match event {
|
match event {
|
||||||
// This has to be handled before the ResizeMsg below,
|
// This has to be handled before the ResizeMsg below,
|
||||||
// otherwise the page may not have been added to the
|
// otherwise the page may not have been added to the
|
||||||
|
@ -788,6 +791,7 @@ impl ScriptThread {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
FromConstellation(ConstellationControlMsg::Resize(id, size, size_type)) => {
|
FromConstellation(ConstellationControlMsg::Resize(id, size, size_type)) => {
|
||||||
|
// step 7.7
|
||||||
self.profile_event(ScriptThreadEventCategory::Resize, || {
|
self.profile_event(ScriptThreadEventCategory::Resize, || {
|
||||||
self.handle_resize(id, size, size_type);
|
self.handle_resize(id, size, size_type);
|
||||||
})
|
})
|
||||||
|
@ -804,6 +808,7 @@ impl ScriptThread {
|
||||||
}
|
}
|
||||||
FromConstellation(ConstellationControlMsg::TickAllAnimations(
|
FromConstellation(ConstellationControlMsg::TickAllAnimations(
|
||||||
pipeline_id)) => {
|
pipeline_id)) => {
|
||||||
|
// step 7.8
|
||||||
if !animation_ticks.contains(&pipeline_id) {
|
if !animation_ticks.contains(&pipeline_id) {
|
||||||
animation_ticks.insert(pipeline_id);
|
animation_ticks.insert(pipeline_id);
|
||||||
sequential.push(event);
|
sequential.push(event);
|
||||||
|
@ -868,11 +873,16 @@ impl ScriptThread {
|
||||||
None
|
None
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#event-loop-processing-model step 6
|
||||||
|
self.perform_a_microtask_checkpoint();
|
||||||
|
|
||||||
if let Some(retval) = result {
|
if let Some(retval) = result {
|
||||||
return retval
|
return retval
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/#event-loop-processing-model step 7.12
|
||||||
|
|
||||||
// Issue batched reflows on any pages that require it (e.g. if images loaded)
|
// Issue batched reflows on any pages that require it (e.g. if images loaded)
|
||||||
// TODO(gw): In the future we could probably batch other types of reflows
|
// TODO(gw): In the future we could probably batch other types of reflows
|
||||||
// into this loop too, but for now it's only images.
|
// into this loop too, but for now it's only images.
|
||||||
|
@ -2108,30 +2118,15 @@ impl ScriptThread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enqueue_promise_job(job: EnqueuedPromiseCallback, global: &GlobalScope) {
|
pub fn enqueue_microtask(job: Microtask) {
|
||||||
SCRIPT_THREAD_ROOT.with(|root| {
|
SCRIPT_THREAD_ROOT.with(|root| {
|
||||||
let script_thread = unsafe { &*root.get().unwrap() };
|
let script_thread = unsafe { &*root.get().unwrap() };
|
||||||
script_thread.promise_job_queue.enqueue(job, global);
|
script_thread.microtask_queue.enqueue(job);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush_promise_jobs(global: &GlobalScope) {
|
fn perform_a_microtask_checkpoint(&self) {
|
||||||
SCRIPT_THREAD_ROOT.with(|root| {
|
self.microtask_queue.checkpoint(|id| self.documents.borrow().find_global(id))
|
||||||
let script_thread = unsafe { &*root.get().unwrap() };
|
|
||||||
let _ = script_thread.dom_manipulation_task_source.queue(
|
|
||||||
box FlushPromiseJobs, global);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn do_flush_promise_jobs(&self) {
|
|
||||||
self.promise_job_queue.flush_promise_jobs(|id| self.documents.borrow().find_global(id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FlushPromiseJobs;
|
|
||||||
impl Runnable for FlushPromiseJobs {
|
|
||||||
fn main_thread_handler(self: Box<FlushPromiseJobs>, script_thread: &ScriptThread) {
|
|
||||||
script_thread.do_flush_promise_jobs();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue