aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/task_queue.rs
diff options
context:
space:
mode:
authorGregory Terzian <gterzian@users.noreply.github.com>2019-02-01 20:35:45 +0800
committerGregory Terzian <gterzian@users.noreply.github.com>2019-03-06 14:18:18 +0800
commitecfb9c639ad610d406e7dd278dfc7aac964ddca8 (patch)
tree2970922e9c7e7c4c586b286e3d01295b44886a7c /components/script/task_queue.rs
parent6b648429f54b6b56546e8f744657e32bd6ac21b5 (diff)
downloadservo-ecfb9c639ad610d406e7dd278dfc7aac964ddca8.tar.gz
servo-ecfb9c639ad610d406e7dd278dfc7aac964ddca8.zip
in BC event-loop, only run tasks related to fully-active documents
Diffstat (limited to 'components/script/task_queue.rs')
-rw-r--r--components/script/task_queue.rs80
1 files changed, 75 insertions, 5 deletions
diff --git a/components/script/task_queue.rs b/components/script/task_queue.rs
index e087474320f..3ed835563a3 100644
--- a/components/script/task_queue.rs
+++ b/components/script/task_queue.rs
@@ -7,12 +7,13 @@
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::worker::TrustedWorkerAddress;
use crate::script_runtime::ScriptThreadEventCategory;
+use crate::script_thread::ScriptThread;
use crate::task::TaskBox;
use crate::task_source::TaskSourceName;
use crossbeam_channel::{self, Receiver, Sender};
use msg::constellation_msg::PipelineId;
use std::cell::Cell;
-use std::collections::{HashMap, VecDeque};
+use std::collections::{HashMap, HashSet, VecDeque};
use std::default::Default;
pub type QueuedTask = (
@@ -26,8 +27,10 @@ pub type QueuedTask = (
/// Defining the operations used to convert from a msg T to a QueuedTask.
pub trait QueuedTaskConversion {
fn task_source_name(&self) -> Option<&TaskSourceName>;
+ fn pipeline_id(&self) -> Option<PipelineId>;
fn into_queued_task(self) -> Option<QueuedTask>;
fn from_queued_task(queued_task: QueuedTask) -> Self;
+ fn inactive_msg() -> Self;
fn wake_up_msg() -> Self;
fn is_wake_up(&self) -> bool;
}
@@ -43,6 +46,8 @@ pub struct TaskQueue<T> {
taken_task_counter: Cell<u64>,
/// Tasks that will be throttled for as long as we are "busy".
throttled: DomRefCell<HashMap<TaskSourceName, VecDeque<QueuedTask>>>,
+ /// Tasks for not fully-active documents.
+ inactive: DomRefCell<HashMap<PipelineId, VecDeque<QueuedTask>>>,
}
impl<T: QueuedTaskConversion> TaskQueue<T> {
@@ -53,22 +58,66 @@ impl<T: QueuedTaskConversion> TaskQueue<T> {
msg_queue: DomRefCell::new(VecDeque::new()),
taken_task_counter: Default::default(),
throttled: Default::default(),
+ inactive: Default::default(),
+ }
+ }
+
+ /// Release previously held-back tasks for documents that are now fully-active.
+ /// https://html.spec.whatwg.org/multipage/#event-loop-processing-model:fully-active
+ fn release_tasks_for_fully_active_documents(
+ &self,
+ fully_active: &HashSet<PipelineId>,
+ ) -> Vec<T> {
+ self.inactive
+ .borrow_mut()
+ .iter_mut()
+ .filter(|(pipeline_id, _)| fully_active.contains(pipeline_id))
+ .flat_map(|(_, inactive_queue)| {
+ inactive_queue
+ .drain(0..)
+ .map(|queued_task| T::from_queued_task(queued_task))
+ })
+ .collect()
+ }
+
+ /// Hold back tasks for currently not fully-active documents.
+ /// https://html.spec.whatwg.org/multipage/#event-loop-processing-model:fully-active
+ fn store_task_for_inactive_pipeline(&self, msg: T, pipeline_id: &PipelineId) {
+ let mut inactive = self.inactive.borrow_mut();
+ let inactive_queue = inactive.entry(pipeline_id.clone()).or_default();
+ inactive_queue.push_back(
+ msg.into_queued_task()
+ .expect("Incoming messages should always be convertible into queued tasks"),
+ );
+ let mut msg_queue = self.msg_queue.borrow_mut();
+ if msg_queue.is_empty() {
+ // Ensure there is at least one message.
+ // Otherwise if the just stored inactive message
+ // was the first and last of this iteration,
+ // it will result in a spurious wake-up of the event-loop.
+ msg_queue.push_back(T::inactive_msg());
}
}
/// Process incoming tasks, immediately sending priority ones downstream,
/// and categorizing potential throttles.
- fn process_incoming_tasks(&self, first_msg: T) {
- let mut incoming = Vec::with_capacity(self.port.len() + 1);
+ fn process_incoming_tasks(&self, first_msg: T, fully_active: &HashSet<PipelineId>) {
+ // 1. Make any previously stored task from now fully-active document available.
+ let mut incoming = self.release_tasks_for_fully_active_documents(fully_active);
+
+ // 2. Process the first message(artifact of the fact that select always returns a message).
if !first_msg.is_wake_up() {
incoming.push(first_msg);
}
+
+ // 3. Process any other incoming message.
while let Ok(msg) = self.port.try_recv() {
if !msg.is_wake_up() {
incoming.push(msg);
}
}
+ // 4. Filter tasks from non-priority task-sources.
let to_be_throttled: Vec<T> = incoming
.drain_filter(|msg| {
let task_source = match msg.task_source_name() {
@@ -88,6 +137,12 @@ impl<T: QueuedTaskConversion> TaskQueue<T> {
.collect();
for msg in incoming {
+ if let Some(pipeline_id) = msg.pipeline_id() {
+ if !fully_active.contains(&pipeline_id) {
+ self.store_task_for_inactive_pipeline(msg, &pipeline_id);
+ continue;
+ }
+ }
// Immediately send non-throttled tasks for processing.
let _ = self.msg_queue.borrow_mut().push_back(msg);
}
@@ -103,7 +158,7 @@ impl<T: QueuedTaskConversion> TaskQueue<T> {
let mut throttled_tasks = self.throttled.borrow_mut();
throttled_tasks
.entry(task_source.clone())
- .or_insert(VecDeque::new())
+ .or_default()
.push_back((worker, category, boxed, pipeline_id, task_source));
}
}
@@ -133,8 +188,9 @@ impl<T: QueuedTaskConversion> TaskQueue<T> {
pub fn take_tasks(&self, first_msg: T) {
// High-watermark: once reached, throttled tasks will be held-back.
const PER_ITERATION_MAX: u64 = 5;
+ let fully_active = ScriptThread::get_fully_active_document_ids();
// Always first check for new tasks, but don't reset 'taken_task_counter'.
- self.process_incoming_tasks(first_msg);
+ self.process_incoming_tasks(first_msg, &fully_active);
let mut throttled = self.throttled.borrow_mut();
let mut throttled_length: usize = throttled.values().map(|queue| queue.len()).sum();
let task_source_names = TaskSourceName::all();
@@ -165,6 +221,20 @@ impl<T: QueuedTaskConversion> TaskQueue<T> {
None => continue,
};
let msg = T::from_queued_task(queued_task);
+
+ // Hold back tasks for currently inactive documents.
+ if let Some(pipeline_id) = msg.pipeline_id() {
+ if !fully_active.contains(&pipeline_id) {
+ self.store_task_for_inactive_pipeline(msg, &pipeline_id);
+ // Reduce the length of throttles,
+ // but don't add the task to "msg_queue",
+ // and neither increment "taken_task_counter".
+ throttled_length = throttled_length - 1;
+ continue;
+ }
+ }
+
+ // Make the task available for the event-loop to handle as a message.
let _ = self.msg_queue.borrow_mut().push_back(msg);
self.taken_task_counter
.set(self.taken_task_counter.get() + 1);