aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/script_runtime.rs
diff options
context:
space:
mode:
authorJosh Matthews <josh@joshmatthews.net>2016-09-05 10:55:31 -0400
committerJosh Matthews <josh@joshmatthews.net>2016-09-22 16:16:55 -0400
commit57b3ccd38cbdf53b8e45f68e8ff6c4fd786118dc (patch)
treef1b89e2699ce8265d9a4eca72948724d462888f6 /components/script/script_runtime.rs
parentef501603bf8997e29f8d2d538b7755838236d6c8 (diff)
downloadservo-57b3ccd38cbdf53b8e45f68e8ff6c4fd786118dc.tar.gz
servo-57b3ccd38cbdf53b8e45f68e8ff6c4fd786118dc.zip
Support promises in workers.
Diffstat (limited to 'components/script/script_runtime.rs')
-rw-r--r--components/script/script_runtime.rs105
1 files changed, 103 insertions, 2 deletions
diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs
index 0287d0e9c10..658ace4c46a 100644
--- a/components/script/script_runtime.rs
+++ b/components/script/script_runtime.rs
@@ -5,18 +5,23 @@
//! The script runtime contains common traits and structs commonly used by the
//! 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::global::{global_root_from_object, GlobalRoot, GlobalRef};
use dom::bindings::js::{RootCollection, RootCollectionPtr, trace_roots};
use dom::bindings::refcounted::{LiveDOMReferences, trace_refcounted_objects};
use dom::bindings::trace::trace_traceables;
use dom::bindings::utils::DOM_CALLBACKS;
use js::glue::CollectServoSizes;
-use js::jsapi::{DisableIncrementalGC, GCDescription, GCProgress};
+use js::jsapi::{DisableIncrementalGC, GCDescription, GCProgress, HandleObject};
use js::jsapi::{JSContext, JS_GetRuntime, JSRuntime, JSTracer, SetDOMCallbacks, SetGCSliceCallback};
use js::jsapi::{JSGCInvocationKind, JSGCStatus, JS_AddExtraGCRootsTracer, JS_SetGCCallback};
use js::jsapi::{JSGCMode, JSGCParamKey, JS_SetGCParameter, JS_SetGlobalJitCompilerOption};
use js::jsapi::{JSJitCompilerOption, JS_SetOffthreadIonCompilationEnabled, JS_SetParallelParsingEnabled};
-use js::jsapi::{JSObject, RuntimeOptionsRef, SetPreserveWrapperCallback};
+use js::jsapi::{JSObject, RuntimeOptionsRef, SetPreserveWrapperCallback, SetEnqueuePromiseJobCallback};
use js::rust::Runtime;
+use msg::constellation_msg::PipelineId;
use profile_traits::mem::{Report, ReportKind, ReportsChan};
use script_thread::{Runnable, STACK_ROOTS, trace_thread};
use std::any::Any;
@@ -24,7 +29,10 @@ use std::cell::{RefCell, Cell};
use std::io::{Write, stdout};
use std::marker::PhantomData;
use std::os;
+use std::os::raw::c_void;
+use std::panic::{self, AssertUnwindSafe};
use std::ptr;
+use std::rc::Rc;
use style::thread_state;
use time::{Tm, now};
use util::opts;
@@ -95,6 +103,97 @@ impl<'a> Drop for StackRootTLS<'a> {
}
}
+/// 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"]
+ 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: GlobalRef) {
+ 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<GlobalRoot>
+ {
+ 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.r(), 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)]
+unsafe extern "C" fn enqueue_job(_cx: *mut JSContext,
+ job: HandleObject,
+ _allocation_site: HandleObject,
+ _data: *mut c_void) -> bool {
+ let result = panic::catch_unwind(AssertUnwindSafe(|| {
+ let global = global_root_from_object(job.get());
+ let pipeline = global.r().pipeline_id();
+ global.r().enqueue_promise_job(EnqueuedPromiseCallback {
+ callback: PromiseJobCallback::new(job.get()),
+ pipeline: pipeline,
+ });
+ true
+ }));
+ match result {
+ Ok(result) => result,
+ Err(error) => {
+ store_panic_result(error);
+ return false;
+ }
+ }
+}
+
#[allow(unsafe_code)]
pub unsafe fn new_rt_and_cx() -> Runtime {
LiveDOMReferences::initialize();
@@ -118,6 +217,8 @@ pub unsafe fn new_rt_and_cx() -> Runtime {
// Pre barriers aren't working correctly at the moment
DisableIncrementalGC(runtime.rt());
+ SetEnqueuePromiseJobCallback(runtime.rt(), Some(enqueue_job), ptr::null_mut());
+
set_gc_zeal_options(runtime.rt());
// Enable or disable the JITs.