aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorGregory Terzian <gterzian@users.noreply.github.com>2020-06-24 15:07:48 +0800
committerGregory Terzian <gterzian@users.noreply.github.com>2020-06-30 13:22:38 +0800
commit44ebca72da45575df4e5970e25920c46c14aa0cb (patch)
tree88a9ed7d5529b9b83425d7b7cdd9b86f18ec12c2 /components
parent0b61cfc3ae803ac0f9deef937f890f83b24c9a35 (diff)
downloadservo-44ebca72da45575df4e5970e25920c46c14aa0cb.tar.gz
servo-44ebca72da45575df4e5970e25920c46c14aa0cb.zip
ensure clean shutdown of all threads running JS
Diffstat (limited to 'components')
-rw-r--r--components/background_hang_monitor/background_hang_monitor.rs107
-rw-r--r--components/background_hang_monitor/tests/hang_monitor_tests.rs70
-rw-r--r--components/constellation/constellation.rs99
-rw-r--r--components/constellation/pipeline.rs75
-rw-r--r--components/layout_thread/lib.rs25
-rw-r--r--components/layout_thread_2020/lib.rs25
-rw-r--r--components/layout_traits/lib.rs2
-rw-r--r--components/msg/constellation_msg.rs18
-rw-r--r--components/script/dom/bindings/trace.rs3
-rw-r--r--components/script/dom/dedicatedworkerglobalscope.rs8
-rw-r--r--components/script/dom/globalscope.rs42
-rw-r--r--components/script/dom/serviceworkerglobalscope.rs14
-rw-r--r--components/script/dom/worker.rs8
-rw-r--r--components/script/dom/workerglobalscope.rs23
-rw-r--r--components/script/dom/xmlhttprequest.rs5
-rw-r--r--components/script/microtask.rs4
-rw-r--r--components/script/script_runtime.rs33
-rw-r--r--components/script/script_thread.rs167
-rw-r--r--components/script/serviceworker_manager.rs42
-rw-r--r--components/script/task.rs8
-rw-r--r--components/script/task_manager.rs2
-rw-r--r--components/script/timers.rs7
-rw-r--r--components/script_layout_interface/message.rs2
-rw-r--r--components/script_traits/lib.rs2
-rw-r--r--components/servo/lib.rs6
25 files changed, 565 insertions, 232 deletions
diff --git a/components/background_hang_monitor/background_hang_monitor.rs b/components/background_hang_monitor/background_hang_monitor.rs
index 69e9a5d60bd..bc736402521 100644
--- a/components/background_hang_monitor/background_hang_monitor.rs
+++ b/components/background_hang_monitor/background_hang_monitor.rs
@@ -3,14 +3,17 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::sampler::{NativeStack, Sampler};
-use crossbeam_channel::{after, unbounded, Receiver, Sender};
+use crossbeam_channel::{after, never, unbounded, Receiver, Sender};
use ipc_channel::ipc::{IpcReceiver, IpcSender};
use ipc_channel::router::ROUTER;
use msg::constellation_msg::MonitoredComponentId;
use msg::constellation_msg::{
- BackgroundHangMonitor, BackgroundHangMonitorClone, BackgroundHangMonitorRegister,
+ BackgroundHangMonitor, BackgroundHangMonitorClone, BackgroundHangMonitorExitSignal,
+ BackgroundHangMonitorRegister,
+};
+use msg::constellation_msg::{
+ BackgroundHangMonitorControlMsg, HangAlert, HangAnnotation, HangMonitorAlert,
};
-use msg::constellation_msg::{HangAlert, HangAnnotation, HangMonitorAlert, SamplerControlMsg};
use std::cell::Cell;
use std::collections::{HashMap, VecDeque};
use std::thread;
@@ -19,23 +22,32 @@ use std::time::{Duration, Instant};
#[derive(Clone)]
pub struct HangMonitorRegister {
sender: Sender<(MonitoredComponentId, MonitoredComponentMsg)>,
+ monitoring_enabled: bool,
}
impl HangMonitorRegister {
/// Start a new hang monitor worker, and return a handle to register components for monitoring.
pub fn init(
constellation_chan: IpcSender<HangMonitorAlert>,
- control_port: IpcReceiver<SamplerControlMsg>,
+ control_port: IpcReceiver<BackgroundHangMonitorControlMsg>,
+ monitoring_enabled: bool,
) -> Box<dyn BackgroundHangMonitorRegister> {
let (sender, port) = unbounded();
let _ = thread::Builder::new().spawn(move || {
- let mut monitor =
- BackgroundHangMonitorWorker::new(constellation_chan, control_port, port);
+ let mut monitor = BackgroundHangMonitorWorker::new(
+ constellation_chan,
+ control_port,
+ port,
+ monitoring_enabled,
+ );
while monitor.run() {
// Monitoring until all senders have been dropped...
}
});
- Box::new(HangMonitorRegister { sender })
+ Box::new(HangMonitorRegister {
+ sender,
+ monitoring_enabled,
+ })
}
}
@@ -48,8 +60,13 @@ impl BackgroundHangMonitorRegister for HangMonitorRegister {
component_id: MonitoredComponentId,
transient_hang_timeout: Duration,
permanent_hang_timeout: Duration,
+ exit_signal: Option<Box<dyn BackgroundHangMonitorExitSignal>>,
) -> Box<dyn BackgroundHangMonitor> {
- let bhm_chan = BackgroundHangMonitorChan::new(self.sender.clone(), component_id);
+ let bhm_chan = BackgroundHangMonitorChan::new(
+ self.sender.clone(),
+ component_id,
+ self.monitoring_enabled,
+ );
#[cfg(all(
target_os = "windows",
@@ -71,6 +88,7 @@ impl BackgroundHangMonitorRegister for HangMonitorRegister {
thread::current().name().map(str::to_owned),
transient_hang_timeout,
permanent_hang_timeout,
+ exit_signal,
));
Box::new(bhm_chan)
}
@@ -85,7 +103,13 @@ impl BackgroundHangMonitorClone for HangMonitorRegister {
/// Messages sent from monitored components to the monitor.
pub enum MonitoredComponentMsg {
/// Register component for monitoring,
- Register(Box<dyn Sampler>, Option<String>, Duration, Duration),
+ Register(
+ Box<dyn Sampler>,
+ Option<String>,
+ Duration,
+ Duration,
+ Option<Box<dyn BackgroundHangMonitorExitSignal>>,
+ ),
/// Unregister component for monitoring.
Unregister,
/// Notify start of new activity for a given component,
@@ -101,17 +125,20 @@ pub struct BackgroundHangMonitorChan {
sender: Sender<(MonitoredComponentId, MonitoredComponentMsg)>,
component_id: MonitoredComponentId,
disconnected: Cell<bool>,
+ monitoring_enabled: bool,
}
impl BackgroundHangMonitorChan {
pub fn new(
sender: Sender<(MonitoredComponentId, MonitoredComponentMsg)>,
component_id: MonitoredComponentId,
+ monitoring_enabled: bool,
) -> Self {
BackgroundHangMonitorChan {
sender,
component_id: component_id,
disconnected: Default::default(),
+ monitoring_enabled,
}
}
@@ -128,12 +155,16 @@ impl BackgroundHangMonitorChan {
impl BackgroundHangMonitor for BackgroundHangMonitorChan {
fn notify_activity(&self, annotation: HangAnnotation) {
- let msg = MonitoredComponentMsg::NotifyActivity(annotation);
- self.send(msg);
+ if self.monitoring_enabled {
+ let msg = MonitoredComponentMsg::NotifyActivity(annotation);
+ self.send(msg);
+ }
}
fn notify_wait(&self) {
- let msg = MonitoredComponentMsg::NotifyWait;
- self.send(msg);
+ if self.monitoring_enabled {
+ let msg = MonitoredComponentMsg::NotifyWait;
+ self.send(msg);
+ }
}
fn unregister(&self) {
let msg = MonitoredComponentMsg::Unregister;
@@ -150,6 +181,7 @@ struct MonitoredComponent {
sent_transient_alert: bool,
sent_permanent_alert: bool,
is_waiting: bool,
+ exit_signal: Option<Box<dyn BackgroundHangMonitorExitSignal>>,
}
struct Sample(MonitoredComponentId, Instant, NativeStack);
@@ -159,20 +191,22 @@ pub struct BackgroundHangMonitorWorker {
monitored_components: HashMap<MonitoredComponentId, MonitoredComponent>,
constellation_chan: IpcSender<HangMonitorAlert>,
port: Receiver<(MonitoredComponentId, MonitoredComponentMsg)>,
- control_port: Receiver<SamplerControlMsg>,
+ control_port: Receiver<BackgroundHangMonitorControlMsg>,
sampling_duration: Option<Duration>,
sampling_max_duration: Option<Duration>,
last_sample: Instant,
creation: Instant,
sampling_baseline: Instant,
samples: VecDeque<Sample>,
+ monitoring_enabled: bool,
}
impl BackgroundHangMonitorWorker {
pub fn new(
constellation_chan: IpcSender<HangMonitorAlert>,
- control_port: IpcReceiver<SamplerControlMsg>,
+ control_port: IpcReceiver<BackgroundHangMonitorControlMsg>,
port: Receiver<(MonitoredComponentId, MonitoredComponentMsg)>,
+ monitoring_enabled: bool,
) -> Self {
let control_port = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(control_port);
Self {
@@ -187,6 +221,7 @@ impl BackgroundHangMonitorWorker {
sampling_baseline: Instant::now(),
creation: Instant::now(),
samples: Default::default(),
+ monitoring_enabled,
}
}
@@ -232,13 +267,19 @@ impl BackgroundHangMonitorWorker {
}
pub fn run(&mut self) -> bool {
- let timeout = if let Some(duration) = self.sampling_duration {
- duration
+ let tick = if let Some(duration) = self.sampling_duration {
+ let duration = duration
.checked_sub(Instant::now() - self.last_sample)
- .unwrap_or_else(|| Duration::from_millis(0))
+ .unwrap_or_else(|| Duration::from_millis(0));
+ after(duration)
} else {
- Duration::from_millis(100)
+ if self.monitoring_enabled {
+ after(Duration::from_millis(100))
+ } else {
+ never()
+ }
};
+
let received = select! {
recv(self.port) -> event => {
match event {
@@ -249,24 +290,38 @@ impl BackgroundHangMonitorWorker {
},
recv(self.control_port) -> event => {
match event {
- Ok(SamplerControlMsg::Enable(rate, max_duration)) => {
+ Ok(BackgroundHangMonitorControlMsg::EnableSampler(rate, max_duration)) => {
println!("Enabling profiler.");
self.sampling_duration = Some(rate);
self.sampling_max_duration = Some(max_duration);
self.sampling_baseline = Instant::now();
None
- }
- Ok(SamplerControlMsg::Disable) => {
+ },
+ Ok(BackgroundHangMonitorControlMsg::DisableSampler) => {
println!("Disabling profiler.");
self.finish_sampled_profile();
self.sampling_duration = None;
- None
- }
+ return true;
+ },
+ Ok(BackgroundHangMonitorControlMsg::Exit(sender)) => {
+ for component in self.monitored_components.values() {
+ if let Some(signal) = component.exit_signal.as_ref() {
+ signal.signal_to_exit();
+ }
+ }
+
+ // Confirm exit with to the constellation.
+ let _ = sender.send(());
+
+ // Also exit the BHM.
+ return false;
+ },
Err(_) => return false,
}
}
- recv(after(timeout)) -> _ => None,
+ recv(tick) -> _ => None,
};
+
if let Some(msg) = received {
self.handle_msg(msg);
while let Ok(another_msg) = self.port.try_recv() {
@@ -297,6 +352,7 @@ impl BackgroundHangMonitorWorker {
name,
transient_hang_timeout,
permanent_hang_timeout,
+ exit_signal,
),
) => {
let component = MonitoredComponent {
@@ -308,6 +364,7 @@ impl BackgroundHangMonitorWorker {
sent_transient_alert: false,
sent_permanent_alert: false,
is_waiting: true,
+ exit_signal,
};
if let Some(name) = name {
self.component_names.insert(component_id.clone(), name);
diff --git a/components/background_hang_monitor/tests/hang_monitor_tests.rs b/components/background_hang_monitor/tests/hang_monitor_tests.rs
index 978d2831552..f379585b5e2 100644
--- a/components/background_hang_monitor/tests/hang_monitor_tests.rs
+++ b/components/background_hang_monitor/tests/hang_monitor_tests.rs
@@ -9,8 +9,13 @@ use background_hang_monitor::HangMonitorRegister;
use ipc_channel::ipc;
use msg::constellation_msg::ScriptHangAnnotation;
use msg::constellation_msg::TEST_PIPELINE_ID;
-use msg::constellation_msg::{HangAlert, HangAnnotation, HangMonitorAlert};
+use msg::constellation_msg::{
+ BackgroundHangMonitorControlMsg, BackgroundHangMonitorExitSignal, HangAlert, HangAnnotation,
+ HangMonitorAlert,
+};
use msg::constellation_msg::{MonitoredComponentId, MonitoredComponentType};
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
@@ -27,12 +32,16 @@ fn test_hang_monitoring() {
ipc::channel().expect("ipc channel failure");
let (_sampler_sender, sampler_receiver) = ipc::channel().expect("ipc channel failure");
- let background_hang_monitor_register =
- HangMonitorRegister::init(background_hang_monitor_ipc_sender.clone(), sampler_receiver);
+ let background_hang_monitor_register = HangMonitorRegister::init(
+ background_hang_monitor_ipc_sender.clone(),
+ sampler_receiver,
+ true,
+ );
let background_hang_monitor = background_hang_monitor_register.register_component(
MonitoredComponentId(TEST_PIPELINE_ID, MonitoredComponentType::Script),
Duration::from_millis(10),
Duration::from_millis(1000),
+ None,
);
// Start an activity.
@@ -125,12 +134,16 @@ fn test_hang_monitoring_unregister() {
ipc::channel().expect("ipc channel failure");
let (_sampler_sender, sampler_receiver) = ipc::channel().expect("ipc channel failure");
- let background_hang_monitor_register =
- HangMonitorRegister::init(background_hang_monitor_ipc_sender.clone(), sampler_receiver);
+ let background_hang_monitor_register = HangMonitorRegister::init(
+ background_hang_monitor_ipc_sender.clone(),
+ sampler_receiver,
+ true,
+ );
let background_hang_monitor = background_hang_monitor_register.register_component(
MonitoredComponentId(TEST_PIPELINE_ID, MonitoredComponentType::Script),
Duration::from_millis(10),
Duration::from_millis(1000),
+ None,
);
// Start an activity.
@@ -146,3 +159,50 @@ fn test_hang_monitoring_unregister() {
// No new alert yet
assert!(background_hang_monitor_receiver.try_recv().is_err());
}
+
+#[test]
+fn test_hang_monitoring_exit_signal() {
+ let _lock = SERIAL.lock().unwrap();
+
+ let (background_hang_monitor_ipc_sender, _background_hang_monitor_receiver) =
+ ipc::channel().expect("ipc channel failure");
+ let (control_sender, control_receiver) = ipc::channel().expect("ipc channel failure");
+
+ struct BHMExitSignal {
+ closing: Arc<AtomicBool>,
+ }
+
+ impl BackgroundHangMonitorExitSignal for BHMExitSignal {
+ fn signal_to_exit(&self) {
+ self.closing.store(true, Ordering::SeqCst);
+ }
+ }
+
+ let closing = Arc::new(AtomicBool::new(false));
+ let signal = BHMExitSignal {
+ closing: closing.clone(),
+ };
+
+ // Init a worker, without active monitoring.
+ let background_hang_monitor_register = HangMonitorRegister::init(
+ background_hang_monitor_ipc_sender.clone(),
+ control_receiver,
+ false,
+ );
+ let _background_hang_monitor = background_hang_monitor_register.register_component(
+ MonitoredComponentId(TEST_PIPELINE_ID, MonitoredComponentType::Script),
+ Duration::from_millis(10),
+ Duration::from_millis(1000),
+ Some(Box::new(signal)),
+ );
+
+ let (exit_sender, exit_receiver) = ipc::channel().expect("Failed to create IPC channel!");
+ // Send the exit message.
+ let _ = control_sender.send(BackgroundHangMonitorControlMsg::Exit(exit_sender));
+
+ // Assert we receive a confirmation back.
+ assert!(exit_receiver.recv().is_ok());
+
+ // Assert we get the exit signal.
+ while !closing.load(Ordering::SeqCst) {}
+}
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index cfca54e8da5..4c3db87e758 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -126,7 +126,9 @@ use keyboard_types::KeyboardEvent;
use layout_traits::LayoutThreadFactory;
use log::{Level, LevelFilter, Log, Metadata, Record};
use media::{GLPlayerThreads, WindowGLContext};
-use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert, SamplerControlMsg};
+use msg::constellation_msg::{
+ BackgroundHangMonitorControlMsg, BackgroundHangMonitorRegister, HangMonitorAlert,
+};
use msg::constellation_msg::{
BroadcastChannelRouterId, MessagePortId, MessagePortRouterId, PipelineNamespace,
PipelineNamespaceId, PipelineNamespaceRequest, TraversalDirection,
@@ -294,16 +296,18 @@ pub struct Constellation<Message, LTF, STF, SWF> {
/// None when in multiprocess mode.
background_monitor_register: Option<Box<dyn BackgroundHangMonitorRegister>>,
- /// Channels to control all sampling profilers.
- sampling_profiler_control: Vec<IpcSender<SamplerControlMsg>>,
+ /// Channels to control all background-hang monitors.
+ /// TODO: store them on the relevant BrowsingContextGroup,
+ /// so that they could be controlled on a "per-tab/event-loop" basis.
+ background_monitor_control_senders: Vec<IpcSender<BackgroundHangMonitorControlMsg>>,
/// A channel for the background hang monitor to send messages
/// to the constellation.
- background_hang_monitor_sender: Option<IpcSender<HangMonitorAlert>>,
+ background_hang_monitor_sender: IpcSender<HangMonitorAlert>,
/// A channel for the constellation to receiver messages
/// from the background hang monitor.
- background_hang_monitor_receiver: Option<Receiver<Result<HangMonitorAlert, IpcError>>>,
+ background_hang_monitor_receiver: Receiver<Result<HangMonitorAlert, IpcError>>,
/// An IPC channel for layout threads to send messages to the constellation.
/// This is the layout threads' view of `layout_receiver`.
@@ -783,42 +787,28 @@ where
ipc_scheduler_receiver,
);
- let (background_hang_monitor_sender, background_hang_monitor_receiver) =
- if opts::get().background_hang_monitor {
- let (bhm_sender, ipc_bhm_receiver) =
- ipc::channel().expect("ipc channel failure");
- (
- Some(bhm_sender),
- Some(route_ipc_receiver_to_new_mpsc_receiver_preserving_errors(
- ipc_bhm_receiver,
- )),
- )
- } else {
- (None, None)
- };
+ let (background_hang_monitor_sender, ipc_bhm_receiver) =
+ ipc::channel().expect("ipc channel failure");
+ let background_hang_monitor_receiver =
+ route_ipc_receiver_to_new_mpsc_receiver_preserving_errors(ipc_bhm_receiver);
// If we are in multiprocess mode,
// a dedicated per-process hang monitor will be initialized later inside the content process.
// See run_content_process in servo/lib.rs
- let (background_monitor_register, sampler_chan) =
- if opts::multiprocess() || !opts::get().background_hang_monitor {
- (None, vec![])
- } else {
- let (sampling_profiler_control, sampling_profiler_port) =
- ipc::channel().expect("ipc channel failure");
- if let Some(bhm_sender) = background_hang_monitor_sender.clone() {
- (
- Some(HangMonitorRegister::init(
- bhm_sender,
- sampling_profiler_port,
- )),
- vec![sampling_profiler_control],
- )
- } else {
- warn!("No BHM sender found in BHM mode.");
- (None, vec![])
- }
- };
+ let (background_monitor_register, bhm_control_chans) = if opts::multiprocess() {
+ (None, vec![])
+ } else {
+ let (bhm_control_chan, bhm_control_port) =
+ ipc::channel().expect("ipc channel failure");
+ (
+ Some(HangMonitorRegister::init(
+ background_hang_monitor_sender.clone(),
+ bhm_control_port,
+ opts::get().background_hang_monitor,
+ )),
+ vec![bhm_control_chan],
+ )
+ };
let (ipc_layout_sender, ipc_layout_receiver) =
ipc::channel().expect("ipc channel failure");
@@ -871,7 +861,7 @@ where
background_hang_monitor_sender,
background_hang_monitor_receiver,
background_monitor_register,
- sampling_profiler_control: sampler_chan,
+ background_monitor_control_senders: bhm_control_chans,
layout_sender: ipc_layout_sender,
script_receiver: script_receiver,
compositor_receiver: compositor_receiver,
@@ -1197,8 +1187,8 @@ where
Err(e) => return self.handle_send_error(pipeline_id, e),
};
- if let Some(sampler_chan) = pipeline.sampler_control_chan {
- self.sampling_profiler_control.push(sampler_chan);
+ if let Some(chan) = pipeline.bhm_control_chan {
+ self.background_monitor_control_senders.push(chan);
}
if let Some(host) = host {
@@ -1363,7 +1353,7 @@ where
recv(self.script_receiver) -> msg => {
msg.expect("Unexpected script channel panic in constellation").map(Request::Script)
}
- recv(self.background_hang_monitor_receiver.as_ref().unwrap_or(&never())) -> msg => {
+ recv(self.background_hang_monitor_receiver) -> msg => {
msg.expect("Unexpected BHM channel panic in constellation").map(Request::BackgroundHangMonitor)
}
recv(self.compositor_receiver) -> msg => {
@@ -1629,15 +1619,18 @@ where
},
FromCompositorMsg::SetCursor(cursor) => self.handle_set_cursor_msg(cursor),
FromCompositorMsg::EnableProfiler(rate, max_duration) => {
- for chan in &self.sampling_profiler_control {
- if let Err(e) = chan.send(SamplerControlMsg::Enable(rate, max_duration)) {
+ for chan in &self.background_monitor_control_senders {
+ if let Err(e) = chan.send(BackgroundHangMonitorControlMsg::EnableSampler(
+ rate,
+ max_duration,
+ )) {
warn!("error communicating with sampling profiler: {}", e);
}
}
},
FromCompositorMsg::DisableProfiler => {
- for chan in &self.sampling_profiler_control {
- if let Err(e) = chan.send(SamplerControlMsg::Disable) {
+ for chan in &self.background_monitor_control_senders {
+ if let Err(e) = chan.send(BackgroundHangMonitorControlMsg::DisableSampler) {
warn!("error communicating with sampling profiler: {}", e);
}
}
@@ -2658,6 +2651,22 @@ where
self.mem_profiler_chan.send(mem::ProfilerMsg::Exit);
+ // Tell all BHMs to exit,
+ // and to ensure their monitored components exit
+ // even when currently hanging(on JS or sync XHR).
+ // This must be done before starting the process of closing all pipelines.
+ for chan in &self.background_monitor_control_senders {
+ let (exit_sender, exit_receiver) =
+ ipc::channel().expect("Failed to create IPC channel!");
+ if let Err(e) = chan.send(BackgroundHangMonitorControlMsg::Exit(exit_sender)) {
+ warn!("error communicating with bhm: {}", e);
+ continue;
+ }
+ if exit_receiver.recv().is_err() {
+ warn!("Failed to receive exit confirmation from BHM.");
+ }
+ }
+
// Close the top-level browsing contexts
let browsing_context_ids: Vec<BrowsingContextId> = self
.browsing_contexts
diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs
index 4fa5d6e2fd9..40d38c13159 100644
--- a/components/constellation/pipeline.rs
+++ b/components/constellation/pipeline.rs
@@ -21,7 +21,9 @@ use layout_traits::LayoutThreadFactory;
use media::WindowGLContext;
use metrics::PaintTimeMetrics;
use msg::constellation_msg::TopLevelBrowsingContextId;
-use msg::constellation_msg::{BackgroundHangMonitorRegister, HangMonitorAlert, SamplerControlMsg};
+use msg::constellation_msg::{
+ BackgroundHangMonitorControlMsg, BackgroundHangMonitorRegister, HangMonitorAlert,
+};
use msg::constellation_msg::{BrowsingContextId, HistoryStateId};
use msg::constellation_msg::{
PipelineId, PipelineNamespace, PipelineNamespaceId, PipelineNamespaceRequest,
@@ -128,7 +130,7 @@ pub struct InitialPipelineState {
pub background_monitor_register: Option<Box<dyn BackgroundHangMonitorRegister>>,
/// A channel for the background hang monitor to send messages to the constellation.
- pub background_hang_monitor_to_constellation_chan: Option<IpcSender<HangMonitorAlert>>,
+ pub background_hang_monitor_to_constellation_chan: IpcSender<HangMonitorAlert>,
/// A channel for the layout thread to send messages to the constellation.
pub layout_to_constellation_chan: IpcSender<LayoutMsg>,
@@ -205,7 +207,7 @@ pub struct InitialPipelineState {
pub struct NewPipeline {
pub pipeline: Pipeline,
- pub sampler_control_chan: Option<IpcSender<SamplerControlMsg>>,
+ pub bhm_control_chan: Option<IpcSender<BackgroundHangMonitorControlMsg>>,
}
impl Pipeline {
@@ -220,7 +222,7 @@ impl Pipeline {
// probably requires a general low-memory strategy.
let (pipeline_chan, pipeline_port) = ipc::channel().expect("Pipeline main chan");
- let (script_chan, sampler_chan) = match state.event_loop {
+ let (script_chan, bhm_control_chan) = match state.event_loop {
Some(script_chan) => {
let new_layout_info = NewLayoutInfo {
parent_info: state.parent_pipeline_id,
@@ -279,7 +281,7 @@ impl Pipeline {
background_hang_monitor_to_constellation_chan: state
.background_hang_monitor_to_constellation_chan
.clone(),
- sampling_profiler_port: None,
+ bhm_control_port: None,
scheduler_chan: state.scheduler_chan,
devtools_chan: script_to_devtools_chan,
bluetooth_thread: state.bluetooth_thread,
@@ -309,34 +311,26 @@ impl Pipeline {
// Spawn the child process.
//
// Yes, that's all there is to it!
- let sampler_chan = if opts::multiprocess() {
- let (sampler_chan, sampler_port) = ipc::channel().expect("Sampler chan");
- unprivileged_pipeline_content.sampling_profiler_port = Some(sampler_port);
+ let bhm_control_chan = if opts::multiprocess() {
+ let (bhm_control_chan, bhm_control_port) =
+ ipc::channel().expect("Sampler chan");
+ unprivileged_pipeline_content.bhm_control_port = Some(bhm_control_port);
let _ = unprivileged_pipeline_content.spawn_multiprocess()?;
- Some(sampler_chan)
+ Some(bhm_control_chan)
} else {
// Should not be None in single-process mode.
- if opts::get().background_hang_monitor {
- let register = state.background_monitor_register.expect(
- "Couldn't start content, no background monitor has been initiated",
- );
- unprivileged_pipeline_content.start_all::<Message, LTF, STF>(
- false,
- Some(register),
- state.event_loop_waker,
- );
- None
- } else {
- unprivileged_pipeline_content.start_all::<Message, LTF, STF>(
- false,
- None,
- state.event_loop_waker,
- );
- None
- }
+ let register = state
+ .background_monitor_register
+ .expect("Couldn't start content, no background monitor has been initiated");
+ unprivileged_pipeline_content.start_all::<Message, LTF, STF>(
+ false,
+ register,
+ state.event_loop_waker,
+ );
+ None
};
- (EventLoop::new(script_chan), sampler_chan)
+ (EventLoop::new(script_chan), bhm_control_chan)
},
};
@@ -353,7 +347,7 @@ impl Pipeline {
);
Ok(NewPipeline {
pipeline,
- sampler_control_chan: sampler_chan,
+ bhm_control_chan,
})
}
@@ -494,8 +488,8 @@ pub struct UnprivilegedPipelineContent {
opener: Option<BrowsingContextId>,
namespace_request_sender: IpcSender<PipelineNamespaceRequest>,
script_to_constellation_chan: ScriptToConstellationChan,
- background_hang_monitor_to_constellation_chan: Option<IpcSender<HangMonitorAlert>>,
- sampling_profiler_port: Option<IpcReceiver<SamplerControlMsg>>,
+ background_hang_monitor_to_constellation_chan: IpcSender<HangMonitorAlert>,
+ bhm_control_port: Option<IpcReceiver<BackgroundHangMonitorControlMsg>>,
layout_to_constellation_chan: IpcSender<LayoutMsg>,
scheduler_chan: IpcSender<TimerSchedulerMsg>,
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
@@ -526,7 +520,7 @@ impl UnprivilegedPipelineContent {
pub fn start_all<Message, LTF, STF>(
self,
wait_for_completion: bool,
- background_hang_monitor_register: Option<Box<dyn BackgroundHangMonitorRegister>>,
+ background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
event_loop_waker: Option<Box<dyn EventLoopWaker>>,
) where
LTF: LayoutThreadFactory<Message = Message>,
@@ -634,17 +628,12 @@ impl UnprivilegedPipelineContent {
pub fn register_with_background_hang_monitor(
&mut self,
- ) -> Option<Box<dyn BackgroundHangMonitorRegister>> {
- self.background_hang_monitor_to_constellation_chan
- .clone()
- .map(|bhm| {
- HangMonitorRegister::init(
- bhm.clone(),
- self.sampling_profiler_port
- .take()
- .expect("no sampling profiler?"),
- )
- })
+ ) -> Box<dyn BackgroundHangMonitorRegister> {
+ HangMonitorRegister::init(
+ self.background_hang_monitor_to_constellation_chan.clone(),
+ self.bhm_control_port.take().expect("no sampling profiler?"),
+ opts::get().background_hang_monitor,
+ )
}
pub fn script_to_constellation_chan(&self) -> &ScriptToConstellationChan {
diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs
index 07cb7c5a4c8..7446e13a3b5 100644
--- a/components/layout_thread/lib.rs
+++ b/components/layout_thread/lib.rs
@@ -153,7 +153,7 @@ pub struct LayoutThread {
font_cache_sender: IpcSender<()>,
/// A means of communication with the background hang monitor.
- background_hang_monitor: Option<Box<dyn BackgroundHangMonitor>>,
+ background_hang_monitor: Box<dyn BackgroundHangMonitor>,
/// The channel on which messages can be sent to the constellation.
constellation_chan: IpcSender<ConstellationMsg>,
@@ -266,7 +266,7 @@ impl LayoutThreadFactory for LayoutThread {
is_iframe: bool,
chan: (Sender<Msg>, Receiver<Msg>),
pipeline_port: IpcReceiver<LayoutControlMsg>,
- background_hang_monitor_register: Option<Box<dyn BackgroundHangMonitorRegister>>,
+ background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
constellation_chan: IpcSender<ConstellationMsg>,
script_chan: IpcSender<ConstellationControlMsg>,
image_cache: Arc<dyn ImageCache>,
@@ -299,13 +299,13 @@ impl LayoutThreadFactory for LayoutThread {
// Ensures layout thread is destroyed before we send shutdown message
let sender = chan.0;
- let background_hang_monitor = background_hang_monitor_register.map(|bhm| {
- bhm.register_component(
+ let background_hang_monitor = background_hang_monitor_register
+ .register_component(
MonitoredComponentId(id, MonitoredComponentType::Layout),
Duration::from_millis(1000),
Duration::from_millis(5000),
- )
- });
+ None,
+ );
let layout = LayoutThread::new(
id,
@@ -483,7 +483,7 @@ impl LayoutThread {
is_iframe: bool,
port: Receiver<Msg>,
pipeline_port: IpcReceiver<LayoutControlMsg>,
- background_hang_monitor: Option<Box<dyn BackgroundHangMonitor>>,
+ background_hang_monitor: Box<dyn BackgroundHangMonitor>,
constellation_chan: IpcSender<ConstellationMsg>,
script_chan: IpcSender<ConstellationControlMsg>,
image_cache: Arc<dyn ImageCache>,
@@ -656,8 +656,7 @@ impl LayoutThread {
Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart,
};
self.background_hang_monitor
- .as_ref()
- .map(|bhm| bhm.notify_activity(HangAnnotation::Layout(hang_annotation)));
+ .notify_activity(HangAnnotation::Layout(hang_annotation));
}
/// Receives and dispatches messages from the script and constellation threads
@@ -669,9 +668,7 @@ impl LayoutThread {
}
// Notify the background-hang-monitor we are waiting for an event.
- self.background_hang_monitor
- .as_ref()
- .map(|bhm| bhm.notify_wait());
+ self.background_hang_monitor.notify_wait();
let request = select! {
recv(self.pipeline_port) -> msg => Request::FromPipeline(msg.unwrap()),
@@ -926,9 +923,7 @@ impl LayoutThread {
);
self.root_flow.borrow_mut().take();
- self.background_hang_monitor
- .as_ref()
- .map(|bhm| bhm.unregister());
+ self.background_hang_monitor.unregister();
}
fn handle_add_stylesheet(&self, stylesheet: &Stylesheet, guard: &SharedRwLockReadGuard) {
diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs
index 4e04f6f0c4b..ce23c087056 100644
--- a/components/layout_thread_2020/lib.rs
+++ b/components/layout_thread_2020/lib.rs
@@ -134,7 +134,7 @@ pub struct LayoutThread {
font_cache_sender: IpcSender<()>,
/// A means of communication with the background hang monitor.
- background_hang_monitor: Option<Box<dyn BackgroundHangMonitor>>,
+ background_hang_monitor: Box<dyn BackgroundHangMonitor>,
/// The channel on which messages can be sent to the script thread.
script_chan: IpcSender<ConstellationControlMsg>,
@@ -234,7 +234,7 @@ impl LayoutThreadFactory for LayoutThread {
is_iframe: bool,
chan: (Sender<Msg>, Receiver<Msg>),
pipeline_port: IpcReceiver<LayoutControlMsg>,
- background_hang_monitor_register: Option<Box<dyn BackgroundHangMonitorRegister>>,
+ background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
constellation_chan: IpcSender<ConstellationMsg>,
script_chan: IpcSender<ConstellationControlMsg>,
image_cache: Arc<dyn ImageCache>,
@@ -267,13 +267,13 @@ impl LayoutThreadFactory for LayoutThread {
// Ensures layout thread is destroyed before we send shutdown message
let sender = chan.0;
- let background_hang_monitor = background_hang_monitor_register.map(|bhm| {
- bhm.register_component(
+ let background_hang_monitor = background_hang_monitor_register
+ .register_component(
MonitoredComponentId(id, MonitoredComponentType::Layout),
Duration::from_millis(1000),
Duration::from_millis(5000),
- )
- });
+ None,
+ );
let layout = LayoutThread::new(
id,
@@ -450,7 +450,7 @@ impl LayoutThread {
is_iframe: bool,
port: Receiver<Msg>,
pipeline_port: IpcReceiver<LayoutControlMsg>,
- background_hang_monitor: Option<Box<dyn BackgroundHangMonitor>>,
+ background_hang_monitor: Box<dyn BackgroundHangMonitor>,
constellation_chan: IpcSender<ConstellationMsg>,
script_chan: IpcSender<ConstellationControlMsg>,
image_cache: Arc<dyn ImageCache>,
@@ -619,8 +619,7 @@ impl LayoutThread {
Msg::SetNavigationStart(..) => LayoutHangAnnotation::SetNavigationStart,
};
self.background_hang_monitor
- .as_ref()
- .map(|bhm| bhm.notify_activity(HangAnnotation::Layout(hang_annotation)));
+ .notify_activity(HangAnnotation::Layout(hang_annotation));
}
/// Receives and dispatches messages from the script and constellation threads
@@ -632,9 +631,7 @@ impl LayoutThread {
}
// Notify the background-hang-monitor we are waiting for an event.
- self.background_hang_monitor
- .as_ref()
- .map(|bhm| bhm.notify_wait());
+ self.background_hang_monitor.notify_wait();
let request = select! {
recv(self.pipeline_port) -> msg => Request::FromPipeline(msg.unwrap()),
@@ -851,9 +848,7 @@ impl LayoutThread {
}
fn exit_now(&mut self) {
- self.background_hang_monitor
- .as_ref()
- .map(|bhm| bhm.unregister());
+ self.background_hang_monitor.unregister();
}
fn handle_add_stylesheet(&self, stylesheet: &Stylesheet, guard: &SharedRwLockReadGuard) {
diff --git a/components/layout_traits/lib.rs b/components/layout_traits/lib.rs
index c33aa3e47aa..416d0e5b662 100644
--- a/components/layout_traits/lib.rs
+++ b/components/layout_traits/lib.rs
@@ -36,7 +36,7 @@ pub trait LayoutThreadFactory {
is_iframe: bool,
chan: (Sender<Self::Message>, Receiver<Self::Message>),
pipeline_port: IpcReceiver<LayoutControlMsg>,
- background_hang_monitor: Option<Box<dyn BackgroundHangMonitorRegister>>,
+ background_hang_monitor: Box<dyn BackgroundHangMonitorRegister>,
constellation_chan: IpcSender<ConstellationMsg>,
script_chan: IpcSender<ConstellationControlMsg>,
image_cache: Arc<dyn ImageCache>,
diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs
index 69fb9a2a0cd..f1c26142078 100644
--- a/components/msg/constellation_msg.rs
+++ b/components/msg/constellation_msg.rs
@@ -679,6 +679,7 @@ pub trait BackgroundHangMonitorRegister: BackgroundHangMonitorClone + Send {
component: MonitoredComponentId,
transient_hang_timeout: Duration,
permanent_hang_timeout: Duration,
+ exit_signal: Option<Box<dyn BackgroundHangMonitorExitSignal>>,
) -> Box<dyn BackgroundHangMonitor>;
}
@@ -702,10 +703,21 @@ pub trait BackgroundHangMonitor {
fn unregister(&self);
}
+/// A means for the BHM to signal a monitored component to exit.
+/// Useful when the component is hanging, and cannot be notified via the usual way.
+/// The component should implement this in a way allowing for the signal to be received when hanging,
+/// if at all.
+pub trait BackgroundHangMonitorExitSignal: Send {
+ /// Called by the BHM, to notify the monitored component to exit.
+ fn signal_to_exit(&self);
+}
+
/// Messages to control the sampling profiler.
#[derive(Deserialize, Serialize)]
-pub enum SamplerControlMsg {
+pub enum BackgroundHangMonitorControlMsg {
/// Enable the sampler, with a given sampling rate and max total sampling duration.
- Enable(Duration, Duration),
- Disable,
+ EnableSampler(Duration, Duration),
+ DisableSampler,
+ /// Exit, and propagate the signal to monitored components.
+ Exit(IpcSender<()>),
}
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 9d7b385adc3..411327bca39 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -42,7 +42,7 @@ use crate::dom::gpucommandencoder::GPUCommandEncoderState;
use crate::dom::htmlimageelement::SourceSet;
use crate::dom::htmlmediaelement::{HTMLMediaElementFetchContext, MediaFrameRenderer};
use crate::dom::identityhub::Identities;
-use crate::script_runtime::StreamConsumer;
+use crate::script_runtime::{ContextForRequestInterrupt, StreamConsumer};
use crate::script_thread::IncompleteParserContexts;
use crate::task::TaskBox;
use app_units::Au;
@@ -502,6 +502,7 @@ unsafe_no_jsmanaged_fields!(TimelineMarkerType);
unsafe_no_jsmanaged_fields!(WorkerId);
unsafe_no_jsmanaged_fields!(BufferQueue, QuirksMode, StrTendril);
unsafe_no_jsmanaged_fields!(Runtime);
+unsafe_no_jsmanaged_fields!(ContextForRequestInterrupt);
unsafe_no_jsmanaged_fields!(HeaderMap, Method);
unsafe_no_jsmanaged_fields!(WindowProxyHandler);
unsafe_no_jsmanaged_fields!(UntrustedNodeAddress, OpaqueNode);
diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs
index 0095429b571..1bc71fad4d5 100644
--- a/components/script/dom/dedicatedworkerglobalscope.rs
+++ b/components/script/dom/dedicatedworkerglobalscope.rs
@@ -30,7 +30,8 @@ use crate::fetch::load_whole_resource;
use crate::realms::{enter_realm, AlreadyInRealm, InRealm};
use crate::script_runtime::ScriptThreadEventCategory::WorkerEvent;
use crate::script_runtime::{
- new_child_runtime, CommonScriptMsg, JSContext as SafeJSContext, Runtime, ScriptChan, ScriptPort,
+ new_child_runtime, CommonScriptMsg, ContextForRequestInterrupt, JSContext as SafeJSContext,
+ Runtime, ScriptChan, ScriptPort,
};
use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
use crate::task_source::networking::NetworkingTaskSource;
@@ -256,7 +257,7 @@ impl DedicatedWorkerGlobalScope {
worker_url,
runtime,
from_devtools_receiver,
- Some(closing),
+ closing,
gpu_id_hub,
),
task_queue: TaskQueue::new(receiver, own_sender.clone()),
@@ -324,6 +325,7 @@ impl DedicatedWorkerGlobalScope {
browsing_context: Option<BrowsingContextId>,
gpu_id_hub: Arc<Mutex<Identities>>,
control_receiver: Receiver<DedicatedWorkerControlMsg>,
+ context_sender: Sender<ContextForRequestInterrupt>,
) -> JoinHandle<()> {
let serialized_worker_url = worker_url.to_string();
let name = format!("WebWorker for {}", serialized_worker_url);
@@ -377,6 +379,8 @@ impl DedicatedWorkerGlobalScope {
new_child_runtime(parent, Some(task_source))
};
+ let _ = context_sender.send(ContextForRequestInterrupt::new(runtime.cx()));
+
let (devtools_mpsc_chan, devtools_mpsc_port) = unbounded();
ROUTER.route_ipc_receiver_to_crossbeam_sender(
from_devtools_receiver,
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index 433a24ae786..eec99116bcf 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -52,7 +52,9 @@ use crate::dom::workletglobalscope::WorkletGlobalScope;
use crate::microtask::{Microtask, MicrotaskQueue, UserMicrotask};
use crate::realms::{enter_realm, AlreadyInRealm, InRealm};
use crate::script_module::ModuleTree;
-use crate::script_runtime::{CommonScriptMsg, JSContext as SafeJSContext, ScriptChan, ScriptPort};
+use crate::script_runtime::{
+ CommonScriptMsg, ContextForRequestInterrupt, JSContext as SafeJSContext, ScriptChan, ScriptPort,
+};
use crate::script_thread::{MainThreadScriptChan, ScriptThread};
use crate::task::TaskCanceller;
use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
@@ -130,6 +132,8 @@ pub struct AutoCloseWorker {
/// A sender of control messages,
/// currently only used to signal shutdown.
control_sender: Sender<DedicatedWorkerControlMsg>,
+ /// The context to request an interrupt on the worker thread.
+ context: ContextForRequestInterrupt,
}
impl Drop for AutoCloseWorker {
@@ -146,6 +150,8 @@ impl Drop for AutoCloseWorker {
warn!("Couldn't send an exit message to a dedicated worker.");
}
+ self.context.request_interrupt();
+
// TODO: step 2 and 3.
// Step 4 is unnecessary since we don't use actual ports for dedicated workers.
if self
@@ -2049,6 +2055,7 @@ impl GlobalScope {
closing: Arc<AtomicBool>,
join_handle: JoinHandle<()>,
control_sender: Sender<DedicatedWorkerControlMsg>,
+ context: ContextForRequestInterrupt,
) {
self.list_auto_close_worker
.borrow_mut()
@@ -2056,6 +2063,7 @@ impl GlobalScope {
closing,
join_handle: Some(join_handle),
control_sender: control_sender,
+ context,
});
}
@@ -2713,6 +2721,20 @@ impl GlobalScope {
unreachable!();
}
+ /// Returns a boolean indicating whether the event-loop
+ /// where this global is running on can continue running JS.
+ pub fn can_continue_running(&self) -> bool {
+ if self.downcast::<Window>().is_some() {
+ return ScriptThread::can_continue_running();
+ }
+ if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
+ return !worker.is_closing();
+ }
+
+ // TODO: plug worklets into this.
+ true
+ }
+
/// Returns the task canceller of this global to ensure that everything is
/// properly cancelled when the global scope is destroyed.
pub fn task_canceller(&self, name: TaskSourceName) -> TaskCanceller {
@@ -2730,11 +2752,14 @@ impl GlobalScope {
/// Perform a microtask checkpoint.
pub fn perform_a_microtask_checkpoint(&self) {
- self.microtask_queue.checkpoint(
- self.get_cx(),
- |_| Some(DomRoot::from_ref(self)),
- vec![DomRoot::from_ref(self)],
- );
+ // Only perform the checkpoint if we're not shutting down.
+ if self.can_continue_running() {
+ self.microtask_queue.checkpoint(
+ self.get_cx(),
+ |_| Some(DomRoot::from_ref(self)),
+ vec![DomRoot::from_ref(self)],
+ );
+ }
}
/// Enqueue a microtask for subsequent execution.
@@ -2761,8 +2786,9 @@ impl GlobalScope {
}
/// Process a single event as if it were the next event
- /// in the thread queue for this global scope.
- pub fn process_event(&self, msg: CommonScriptMsg) {
+ /// in the queue for the event-loop where this global scope is running on.
+ /// Returns a boolean indicating whether further events should be processed.
+ pub fn process_event(&self, msg: CommonScriptMsg) -> bool {
if self.is::<Window>() {
return ScriptThread::process_event(msg);
}
diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs
index 4bea29cda50..2e742ec9360 100644
--- a/components/script/dom/serviceworkerglobalscope.rs
+++ b/components/script/dom/serviceworkerglobalscope.rs
@@ -25,7 +25,8 @@ use crate::dom::workerglobalscope::WorkerGlobalScope;
use crate::fetch::load_whole_resource;
use crate::realms::{enter_realm, AlreadyInRealm, InRealm};
use crate::script_runtime::{
- new_rt_and_cx, CommonScriptMsg, JSContext as SafeJSContext, Runtime, ScriptChan,
+ new_rt_and_cx, CommonScriptMsg, ContextForRequestInterrupt, JSContext as SafeJSContext,
+ Runtime, ScriptChan,
};
use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
use crate::task_source::TaskSourceName;
@@ -44,6 +45,7 @@ use script_traits::{ScopeThings, ServiceWorkerMsg, WorkerGlobalScopeInit, Worker
use servo_config::pref;
use servo_rand::random;
use servo_url::ServoUrl;
+use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use std::thread::{self, JoinHandle};
use std::time::{Duration, Instant};
@@ -225,6 +227,7 @@ impl ServiceWorkerGlobalScope {
swmanager_sender: IpcSender<ServiceWorkerMsg>,
scope_url: ServoUrl,
control_receiver: Receiver<ServiceWorkerControlMsg>,
+ closing: Arc<AtomicBool>,
) -> ServiceWorkerGlobalScope {
ServiceWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited(
@@ -234,7 +237,7 @@ impl ServiceWorkerGlobalScope {
worker_url,
runtime,
from_devtools_receiver,
- None,
+ closing,
Arc::new(Mutex::new(Identities::new())),
),
task_queue: TaskQueue::new(receiver, own_sender.clone()),
@@ -258,6 +261,7 @@ impl ServiceWorkerGlobalScope {
swmanager_sender: IpcSender<ServiceWorkerMsg>,
scope_url: ServoUrl,
control_receiver: Receiver<ServiceWorkerControlMsg>,
+ closing: Arc<AtomicBool>,
) -> DomRoot<ServiceWorkerGlobalScope> {
let cx = runtime.cx();
let scope = Box::new(ServiceWorkerGlobalScope::new_inherited(
@@ -271,6 +275,7 @@ impl ServiceWorkerGlobalScope {
swmanager_sender,
scope_url,
control_receiver,
+ closing,
));
unsafe { ServiceWorkerGlobalScopeBinding::Wrap(SafeJSContext::from_ptr(cx), scope) }
}
@@ -285,6 +290,8 @@ impl ServiceWorkerGlobalScope {
swmanager_sender: IpcSender<ServiceWorkerMsg>,
scope_url: ServoUrl,
control_receiver: Receiver<ServiceWorkerControlMsg>,
+ context_sender: Sender<ContextForRequestInterrupt>,
+ closing: Arc<AtomicBool>,
) -> JoinHandle<()> {
let ScopeThings {
script_url,
@@ -300,6 +307,8 @@ impl ServiceWorkerGlobalScope {
.spawn(move || {
thread_state::initialize(ThreadState::SCRIPT | ThreadState::IN_WORKER);
let runtime = new_rt_and_cx(None);
+ let _ = context_sender.send(ContextForRequestInterrupt::new(runtime.cx()));
+
let roots = RootCollection::new();
let _stack_roots = ThreadLocalStackRoots::new(&roots);
@@ -330,6 +339,7 @@ impl ServiceWorkerGlobalScope {
swmanager_sender,
scope_url,
control_receiver,
+ closing,
);
let referrer = referrer_url
diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs
index ca3b811b042..563a04679ea 100644
--- a/components/script/dom/worker.rs
+++ b/components/script/dom/worker.rs
@@ -125,6 +125,7 @@ impl Worker {
let init = prepare_workerscope_init(global, Some(devtools_sender), Some(worker_id));
let (control_sender, control_receiver) = unbounded();
+ let (context_sender, context_receiver) = unbounded();
let join_handle = DedicatedWorkerGlobalScope::run_worker_scope(
init,
@@ -142,9 +143,14 @@ impl Worker {
browsing_context,
global.wgpu_id_hub(),
control_receiver,
+ context_sender,
);
- global.track_worker(closing, join_handle, control_sender);
+ let context = context_receiver
+ .recv()
+ .expect("Couldn't receive a context for worker.");
+
+ global.track_worker(closing, join_handle, control_sender, context);
Ok(worker)
}
diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs
index 3ff31542b56..aabda0ab248 100644
--- a/components/script/dom/workerglobalscope.rs
+++ b/components/script/dom/workerglobalscope.rs
@@ -98,7 +98,7 @@ pub struct WorkerGlobalScope {
worker_id: WorkerId,
worker_url: DomRefCell<ServoUrl>,
#[ignore_malloc_size_of = "Arc"]
- closing: Option<Arc<AtomicBool>>,
+ closing: Arc<AtomicBool>,
#[ignore_malloc_size_of = "Defined in js"]
runtime: DomRefCell<Option<Runtime>>,
location: MutNullableDom<WorkerLocation>,
@@ -126,7 +126,7 @@ impl WorkerGlobalScope {
worker_url: ServoUrl,
runtime: Runtime,
from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
- closing: Option<Arc<AtomicBool>>,
+ closing: Arc<AtomicBool>,
gpu_id_hub: Arc<Mutex<Identities>>,
) -> Self {
// Install a pipeline-namespace in the current thread.
@@ -193,11 +193,7 @@ impl WorkerGlobalScope {
}
pub fn is_closing(&self) -> bool {
- if let Some(ref closing) = self.closing {
- closing.load(Ordering::SeqCst)
- } else {
- false
- }
+ self.closing.load(Ordering::SeqCst)
}
pub fn get_url(&self) -> Ref<ServoUrl> {
@@ -494,7 +490,13 @@ impl WorkerGlobalScope {
}
}
- pub fn process_event(&self, msg: CommonScriptMsg) {
+ /// Process a single event as if it were the next event
+ /// in the queue for this worker event-loop.
+ /// Returns a boolean indicating whether further events should be processed.
+ pub fn process_event(&self, msg: CommonScriptMsg) -> bool {
+ if self.is_closing() {
+ return false;
+ }
match msg {
CommonScriptMsg::Task(_, task, _, _) => task.run_box(),
CommonScriptMsg::CollectReports(reports_chan) => {
@@ -504,11 +506,10 @@ impl WorkerGlobalScope {
reports_chan.send(reports);
},
}
+ true
}
pub fn close(&self) {
- if let Some(ref closing) = self.closing {
- closing.store(true, Ordering::SeqCst);
- }
+ self.closing.store(true, Ordering::SeqCst);
}
}
diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs
index 06b6699d3a9..842bf5f9441 100644
--- a/components/script/dom/xmlhttprequest.rs
+++ b/components/script/dom/xmlhttprequest.rs
@@ -1544,7 +1544,10 @@ impl XMLHttpRequest {
if let Some(script_port) = script_port {
loop {
- global.process_event(script_port.recv().unwrap());
+ if !global.process_event(script_port.recv().unwrap()) {
+ // We're exiting.
+ return Err(Error::Abort);
+ }
let context = context.lock().unwrap();
let sync_status = context.sync_status.borrow();
if let Some(ref status) = *sync_status {
diff --git a/components/script/microtask.rs b/components/script/microtask.rs
index ae4d3c6944e..b97368fcad5 100644
--- a/components/script/microtask.rs
+++ b/components/script/microtask.rs
@@ -147,4 +147,8 @@ impl MicrotaskQueue {
pub fn empty(&self) -> bool {
self.microtask_queue.borrow().is_empty()
}
+
+ pub fn clear(&self) {
+ self.microtask_queue.borrow_mut().clear();
+ }
}
diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs
index 4016f3b9858..f2d8bb73947 100644
--- a/components/script/script_runtime.rs
+++ b/components/script/script_runtime.rs
@@ -52,7 +52,10 @@ use js::jsapi::{BuildIdCharVector, DisableIncrementalGC, GCDescription, GCProgre
use js::jsapi::{Dispatchable as JSRunnable, Dispatchable_MaybeShuttingDown};
use js::jsapi::{HandleObject, Heap, JobQueue};
use js::jsapi::{JSContext as RawJSContext, JSTracer, SetDOMCallbacks, SetGCSliceCallback};
-use js::jsapi::{JSGCInvocationKind, JSGCStatus, JS_AddExtraGCRootsTracer, JS_SetGCCallback};
+use js::jsapi::{
+ JSGCInvocationKind, JSGCStatus, JS_AddExtraGCRootsTracer, JS_RequestInterruptCallback,
+ JS_SetGCCallback,
+};
use js::jsapi::{JSGCMode, JSGCParamKey, JS_SetGCParameter, JS_SetGlobalJitCompilerOption};
use js::jsapi::{
JSJitCompilerOption, JS_SetOffthreadIonCompilationEnabled, JS_SetParallelParsingEnabled,
@@ -845,6 +848,34 @@ unsafe fn set_gc_zeal_options(cx: *mut RawJSContext) {
#[cfg(not(feature = "debugmozjs"))]
unsafe fn set_gc_zeal_options(_: *mut RawJSContext) {}
+#[repr(transparent)]
+/// A wrapper around a JSContext that is Send,
+/// enabling an interrupt to be requested
+/// from a thread other than the one running JS using that context.
+pub struct ContextForRequestInterrupt(*mut RawJSContext);
+
+impl ContextForRequestInterrupt {
+ pub fn new(context: *mut RawJSContext) -> ContextForRequestInterrupt {
+ ContextForRequestInterrupt(context)
+ }
+
+ #[allow(unsafe_code)]
+ /// Can be called from any thread, to request the callback set by
+ /// JS_AddInterruptCallback to be called
+ /// on the thread where that context is running.
+ pub fn request_interrupt(&self) {
+ unsafe {
+ JS_RequestInterruptCallback(self.0);
+ }
+ }
+}
+
+#[allow(unsafe_code)]
+/// It is safe to call `JS_RequestInterruptCallback(cx)` from any thread.
+/// See the docs for the corresponding `requestInterrupt` method,
+/// at `mozjs/js/src/vm/JSContext.h`.
+unsafe impl Send for ContextForRequestInterrupt {}
+
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct JSContext(*mut RawJSContext);
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 89acc25e720..0ef48e78b17 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -63,7 +63,9 @@ use crate::dom::workletglobalscope::WorkletGlobalScopeInit;
use crate::fetch::FetchCanceller;
use crate::microtask::{Microtask, MicrotaskQueue};
use crate::realms::enter_realm;
-use crate::script_runtime::{get_reports, new_rt_and_cx, JSContext, Runtime, ScriptPort};
+use crate::script_runtime::{
+ get_reports, new_rt_and_cx, ContextForRequestInterrupt, JSContext, Runtime, ScriptPort,
+};
use crate::script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
use crate::task_manager::TaskManager;
use crate::task_queue::{QueuedTask, QueuedTaskConversion, TaskQueue};
@@ -97,14 +99,17 @@ use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use js::glue::GetWindowProxyClass;
use js::jsapi::JS_SetWrapObjectCallbacks;
-use js::jsapi::{JSTracer, SetWindowProxyClass};
+use js::jsapi::{
+ JSContext as UnsafeJSContext, JSTracer, JS_AddInterruptCallback, SetWindowProxyClass,
+};
use js::jsval::UndefinedValue;
use js::rust::ParentRuntime;
use media::WindowGLContext;
use metrics::{PaintTimeMetrics, MAX_TASK_NS};
use mime::{self, Mime};
use msg::constellation_msg::{
- BackgroundHangMonitor, BackgroundHangMonitorRegister, ScriptHangAnnotation,
+ BackgroundHangMonitor, BackgroundHangMonitorExitSignal, BackgroundHangMonitorRegister,
+ ScriptHangAnnotation,
};
use msg::constellation_msg::{BrowsingContextId, HistoryStateId, PipelineId};
use msg::constellation_msg::{HangAnnotation, MonitoredComponentId, MonitoredComponentType};
@@ -149,7 +154,7 @@ use std::option::Option;
use std::ptr;
use std::rc::Rc;
use std::result::Result;
-use std::sync::atomic::AtomicBool;
+use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
use std::time::{Duration, SystemTime};
@@ -531,9 +536,11 @@ pub struct ScriptThread {
task_queue: TaskQueue<MainThreadScriptMsg>,
/// A handle to register associated layout threads for hang-monitoring.
- background_hang_monitor_register: Option<Box<dyn BackgroundHangMonitorRegister>>,
+ background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
/// The dedicated means of communication with the background-hang-monitor for this script-thread.
- background_hang_monitor: Option<Box<dyn BackgroundHangMonitor>>,
+ background_hang_monitor: Box<dyn BackgroundHangMonitor>,
+ /// A flag set to `true` by the BHM on exit, and checked from within the interrupt handler.
+ closing: Arc<AtomicBool>,
/// A channel to hand out to script thread-based entities that need to be able to enqueue
/// events in the event queue.
@@ -686,6 +693,27 @@ pub struct ScriptThread {
webgpu_port: RefCell<Option<Receiver<WebGPUMsg>>>,
}
+struct BHMExitSignal {
+ closing: Arc<AtomicBool>,
+ js_context: ContextForRequestInterrupt,
+}
+
+impl BackgroundHangMonitorExitSignal for BHMExitSignal {
+ fn signal_to_exit(&self) {
+ self.closing.store(true, Ordering::SeqCst);
+ self.js_context.request_interrupt();
+ }
+}
+
+#[allow(unsafe_code)]
+unsafe extern "C" fn interrupt_callback(_cx: *mut UnsafeJSContext) -> bool {
+ let res = ScriptThread::can_continue_running();
+ if !res {
+ ScriptThread::prepare_for_shutdown();
+ }
+ res
+}
+
/// In the event of thread panic, all data on the stack runs its destructor. However, there
/// are no reachable, owning pointers to the DOM memory, so it never gets freed by default
/// when the script thread fails. The ScriptMemoryFailsafe uses the destructor bomb pattern
@@ -818,6 +846,20 @@ impl ScriptThread {
})
}
+ pub fn can_continue_running() -> bool {
+ SCRIPT_THREAD_ROOT.with(|root| {
+ let script_thread = unsafe { &*root.get().unwrap() };
+ script_thread.can_continue_running_inner()
+ })
+ }
+
+ pub fn prepare_for_shutdown() {
+ SCRIPT_THREAD_ROOT.with(|root| {
+ let script_thread = unsafe { &*root.get().unwrap() };
+ script_thread.prepare_for_shutdown_inner();
+ })
+ }
+
pub fn set_mutation_observer_microtask_queued(value: bool) {
SCRIPT_THREAD_ROOT.with(|root| {
let script_thread = unsafe { &*root.get().unwrap() };
@@ -876,13 +918,22 @@ impl ScriptThread {
})
}
- pub fn process_event(msg: CommonScriptMsg) {
+ /// Process a single event as if it were the next event
+ /// in the queue for this window event-loop.
+ /// Returns a boolean indicating whether further events should be processed.
+ pub fn process_event(msg: CommonScriptMsg) -> bool {
SCRIPT_THREAD_ROOT.with(|root| {
if let Some(script_thread) = root.get() {
let script_thread = unsafe { &*script_thread };
- script_thread.handle_msg_from_script(MainThreadScriptMsg::Common(msg));
+ if !script_thread.can_continue_running_inner() {
+ return false;
+ } else {
+ script_thread.handle_msg_from_script(MainThreadScriptMsg::Common(msg));
+ return true;
+ }
}
- });
+ false
+ })
}
// https://html.spec.whatwg.org/multipage/#await-a-stable-state
@@ -1231,6 +1282,7 @@ impl ScriptThread {
unsafe {
JS_SetWrapObjectCallbacks(cx, &WRAP_CALLBACKS);
SetWindowProxyClass(cx, GetWindowProxyClass());
+ JS_AddInterruptCallback(cx, Some(interrupt_callback));
}
// Ask the router to proxy IPC messages from the devtools to us.
@@ -1242,13 +1294,18 @@ impl ScriptThread {
let task_queue = TaskQueue::new(port, chan.clone());
- let background_hang_monitor = state.background_hang_monitor_register.clone().map(|bhm| {
- bhm.register_component(
- MonitoredComponentId(state.id.clone(), MonitoredComponentType::Script),
- Duration::from_millis(1000),
- Duration::from_millis(5000),
- )
- });
+ let closing = Arc::new(AtomicBool::new(false));
+ let background_hang_monitor_exit_signal = BHMExitSignal {
+ closing: closing.clone(),
+ js_context: ContextForRequestInterrupt::new(cx),
+ };
+
+ let background_hang_monitor = state.background_hang_monitor_register.register_component(
+ MonitoredComponentId(state.id.clone(), MonitoredComponentType::Script),
+ Duration::from_millis(1000),
+ Duration::from_millis(5000),
+ Some(Box::new(background_hang_monitor_exit_signal)),
+ );
// Ask the router to proxy IPC messages from the control port to us.
let control_port = ROUTER.route_ipc_receiver_to_new_crossbeam_receiver(state.control_port);
@@ -1270,6 +1327,7 @@ impl ScriptThread {
background_hang_monitor_register: state.background_hang_monitor_register,
background_hang_monitor,
+ closing,
chan: MainThreadScriptChan(chan.clone()),
dom_manipulation_task_sender: boxed_script_sender.clone(),
@@ -1349,6 +1407,23 @@ impl ScriptThread {
unsafe { JSContext::from_ptr(self.js_runtime.cx()) }
}
+ /// Check if we are closing.
+ fn can_continue_running_inner(&self) -> bool {
+ if self.closing.load(Ordering::SeqCst) {
+ return false;
+ }
+ true
+ }
+
+ /// We are closing, ensure no script can run and potentially hang.
+ fn prepare_for_shutdown_inner(&self) {
+ let docs = self.documents.borrow();
+ for (_, document) in docs.iter() {
+ let window = document.window();
+ window.ignore_all_tasks();
+ }
+ }
+
/// Starts the script thread. After calling this method, the script thread will loop receiving
/// messages on its port.
pub fn start(&self) {
@@ -1386,9 +1461,7 @@ impl ScriptThread {
let mut sequential = vec![];
// Notify the background-hang-monitor we are waiting for an event.
- self.background_hang_monitor
- .as_ref()
- .map(|bhm| bhm.notify_wait());
+ self.background_hang_monitor.notify_wait();
// Receive at least one message so we don't spinloop.
debug!("Waiting for event.");
@@ -1530,6 +1603,24 @@ impl ScriptThread {
let category = self.categorize_msg(&msg);
let pipeline_id = self.message_to_pipeline(&msg);
+ if self.closing.load(Ordering::SeqCst) {
+ // If we've received the closed signal from the BHM, only handle exit messages.
+ match msg {
+ FromConstellation(ConstellationControlMsg::ExitScriptThread) => {
+ self.handle_exit_script_thread_msg();
+ return false;
+ },
+ FromConstellation(ConstellationControlMsg::ExitPipeline(
+ pipeline_id,
+ discard_browsing_context,
+ )) => {
+ self.handle_exit_pipeline_msg(pipeline_id, discard_browsing_context);
+ },
+ _ => {},
+ }
+ continue;
+ }
+
let result = self.profile_event(category, pipeline_id, move || {
match msg {
FromConstellation(ConstellationControlMsg::ExitScriptThread) => {
@@ -1546,12 +1637,12 @@ impl ScriptThread {
None
});
- // https://html.spec.whatwg.org/multipage/#event-loop-processing-model step 6
- self.perform_a_microtask_checkpoint();
-
if let Some(retval) = result {
return retval;
}
+
+ // https://html.spec.whatwg.org/multipage/#event-loop-processing-model step 6
+ self.perform_a_microtask_checkpoint();
}
{
@@ -1665,8 +1756,7 @@ impl ScriptThread {
ScriptThreadEventCategory::WebGPUMsg => ScriptHangAnnotation::WebGPUMsg,
};
self.background_hang_monitor
- .as_ref()
- .map(|bhm| bhm.notify_activity(HangAnnotation::Script(hang_annotation)));
+ .notify_activity(HangAnnotation::Script(hang_annotation));
}
fn message_to_pipeline(&self, msg: &MixedMessage) -> Option<PipelineId> {
@@ -2855,9 +2945,7 @@ impl ScriptThread {
self.handle_exit_pipeline_msg(pipeline_id, DiscardBrowsingContext::Yes);
}
- self.background_hang_monitor
- .as_ref()
- .map(|bhm| bhm.unregister());
+ self.background_hang_monitor.unregister();
// If we're in multiprocess mode, shut-down the IPC router for this process.
if opts::multiprocess() {
@@ -3867,18 +3955,21 @@ impl ScriptThread {
}
fn perform_a_microtask_checkpoint(&self) {
- let globals = self
- .documents
- .borrow()
- .iter()
- .map(|(_id, document)| document.global())
- .collect();
+ // Only perform the checkpoint if we're not shutting down.
+ if self.can_continue_running_inner() {
+ let globals = self
+ .documents
+ .borrow()
+ .iter()
+ .map(|(_id, document)| document.global())
+ .collect();
- self.microtask_queue.checkpoint(
- self.get_cx(),
- |id| self.documents.borrow().find_global(id),
- globals,
- )
+ self.microtask_queue.checkpoint(
+ self.get_cx(),
+ |id| self.documents.borrow().find_global(id),
+ globals,
+ )
+ }
}
}
diff --git a/components/script/serviceworker_manager.rs b/components/script/serviceworker_manager.rs
index 1cbca40e8cc..46cf22531c8 100644
--- a/components/script/serviceworker_manager.rs
+++ b/components/script/serviceworker_manager.rs
@@ -12,6 +12,7 @@ use crate::dom::serviceworkerglobalscope::{
ServiceWorkerControlMsg, ServiceWorkerGlobalScope, ServiceWorkerScriptMsg,
};
use crate::dom::serviceworkerregistration::longest_prefix_match;
+use crate::script_runtime::ContextForRequestInterrupt;
use crossbeam_channel::{unbounded, Receiver, RecvError, Sender};
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
@@ -26,6 +27,8 @@ use servo_config::pref;
use servo_url::ImmutableOrigin;
use servo_url::ServoUrl;
use std::collections::HashMap;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
use std::thread::{self, JoinHandle};
enum Message {
@@ -93,6 +96,15 @@ impl Drop for ServiceWorkerRegistration {
warn!("Failed to send exit message to service worker scope.");
}
+ self.closing
+ .take()
+ .expect("No close flag for worker")
+ .store(true, Ordering::SeqCst);
+ self.context
+ .take()
+ .expect("No context to request interrupt.")
+ .request_interrupt();
+
// TODO: Step 1, 2 and 3.
if self
.join_handle
@@ -121,6 +133,10 @@ struct ServiceWorkerRegistration {
control_sender: Option<Sender<ServiceWorkerControlMsg>>,
/// A handle to join on the worker thread.
join_handle: Option<JoinHandle<()>>,
+ /// A context to request an interrupt.
+ context: Option<ContextForRequestInterrupt>,
+ /// The closing flag for the worker.
+ closing: Option<Arc<AtomicBool>>,
}
impl ServiceWorkerRegistration {
@@ -132,6 +148,8 @@ impl ServiceWorkerRegistration {
installing_worker: None,
join_handle: None,
control_sender: None,
+ context: None,
+ closing: None,
}
}
@@ -139,12 +157,20 @@ impl ServiceWorkerRegistration {
&mut self,
join_handle: JoinHandle<()>,
control_sender: Sender<ServiceWorkerControlMsg>,
+ context: ContextForRequestInterrupt,
+ closing: Arc<AtomicBool>,
) {
assert!(self.join_handle.is_none());
self.join_handle = Some(join_handle);
assert!(self.control_sender.is_none());
self.control_sender = Some(control_sender);
+
+ assert!(self.context.is_none());
+ self.context = Some(context);
+
+ assert!(self.closing.is_none());
+ self.closing = Some(closing);
}
/// <https://w3c.github.io/ServiceWorker/#get-newest-worker>
@@ -378,11 +404,11 @@ impl ServiceWorkerManager {
// Very roughly steps 5 to 18.
// TODO: implement all steps precisely.
- let (new_worker, join_handle, control_sender) =
+ let (new_worker, join_handle, control_sender, context, closing) =
update_serviceworker(self.own_sender.clone(), job.scope_url.clone(), scope_things);
// Since we've just started the worker thread, ensure we can shut it down later.
- registration.note_worker_thread(join_handle, control_sender);
+ registration.note_worker_thread(join_handle, control_sender, context, closing);
// Step 19, run Install.
@@ -422,12 +448,16 @@ fn update_serviceworker(
ServiceWorker,
JoinHandle<()>,
Sender<ServiceWorkerControlMsg>,
+ ContextForRequestInterrupt,
+ Arc<AtomicBool>,
) {
let (sender, receiver) = unbounded();
let (_devtools_sender, devtools_receiver) = ipc::channel().unwrap();
let worker_id = ServiceWorkerId::new();
let (control_sender, control_receiver) = unbounded();
+ let (context_sender, context_receiver) = unbounded();
+ let closing = Arc::new(AtomicBool::new(false));
let join_handle = ServiceWorkerGlobalScope::run_serviceworker_scope(
scope_things.clone(),
@@ -437,12 +467,20 @@ fn update_serviceworker(
own_sender,
scope_url.clone(),
control_receiver,
+ context_sender,
+ closing.clone(),
);
+ let context = context_receiver
+ .recv()
+ .expect("Couldn't receive a context for worker.");
+
(
ServiceWorker::new(scope_things.script_url, sender, worker_id),
join_handle,
control_sender,
+ context,
+ closing,
)
}
diff --git a/components/script/task.rs b/components/script/task.rs
index 27600fdf8d7..b3200e23afe 100644
--- a/components/script/task.rs
+++ b/components/script/task.rs
@@ -69,7 +69,7 @@ impl fmt::Debug for dyn TaskBox {
/// Encapsulated state required to create cancellable tasks from non-script threads.
#[derive(Clone)]
pub struct TaskCanceller {
- pub cancelled: Option<Arc<AtomicBool>>,
+ pub cancelled: Arc<AtomicBool>,
}
impl TaskCanceller {
@@ -88,7 +88,7 @@ impl TaskCanceller {
/// A task that can be cancelled by toggling a shared flag.
pub struct CancellableTask<T: TaskOnce> {
- cancelled: Option<Arc<AtomicBool>>,
+ cancelled: Arc<AtomicBool>,
inner: T,
}
@@ -97,9 +97,7 @@ where
T: TaskOnce,
{
fn is_cancelled(&self) -> bool {
- self.cancelled
- .as_ref()
- .map_or(false, |cancelled| cancelled.load(Ordering::SeqCst))
+ self.cancelled.load(Ordering::SeqCst)
}
}
diff --git a/components/script/task_manager.rs b/components/script/task_manager.rs
index 063b5700484..184b427cfcb 100644
--- a/components/script/task_manager.rs
+++ b/components/script/task_manager.rs
@@ -182,7 +182,7 @@ impl TaskManager {
let mut flags = self.task_cancellers.borrow_mut();
let cancel_flag = flags.entry(name).or_insert(Default::default());
TaskCanceller {
- cancelled: Some(cancel_flag.clone()),
+ cancelled: cancel_flag.clone(),
}
}
}
diff --git a/components/script/timers.rs b/components/script/timers.rs
index 5d8893c3055..5e236f07494 100644
--- a/components/script/timers.rs
+++ b/components/script/timers.rs
@@ -218,6 +218,13 @@ impl OneshotTimers {
}
for timer in timers_to_run {
+ // Since timers can be coalesced together inside a task,
+ // this loop can keep running, including after an interrupt of the JS,
+ // and prevent a clean-shutdown of a JS-running thread.
+ // This check prevents such a situation.
+ if !global.can_continue_running() {
+ return;
+ }
let callback = timer.callback;
callback.invoke(global, &self.js_timers);
}
diff --git a/components/script_layout_interface/message.rs b/components/script_layout_interface/message.rs
index 2d1ca34eef3..9e4312fd2d5 100644
--- a/components/script_layout_interface/message.rs
+++ b/components/script_layout_interface/message.rs
@@ -222,7 +222,7 @@ pub struct LayoutThreadInit {
pub is_parent: bool,
pub layout_pair: (Sender<Msg>, Receiver<Msg>),
pub pipeline_port: IpcReceiver<LayoutControlMsg>,
- pub background_hang_monitor_register: Option<Box<dyn BackgroundHangMonitorRegister>>,
+ pub background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
pub constellation_chan: IpcSender<ConstellationMsg>,
pub script_chan: IpcSender<ConstellationControlMsg>,
pub image_cache: Arc<dyn ImageCache>,
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index d50f34ed554..f885a042ddd 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -644,7 +644,7 @@ pub struct InitialScriptState {
/// A channel on which messages can be sent to the constellation from script.
pub script_to_constellation_chan: ScriptToConstellationChan,
/// A handle to register script-(and associated layout-)threads for hang monitoring.
- pub background_hang_monitor_register: Option<Box<dyn BackgroundHangMonitorRegister>>,
+ pub background_hang_monitor_register: Box<dyn BackgroundHangMonitorRegister>,
/// A sender for the layout thread to communicate to the constellation.
pub layout_to_constellation_chan: IpcSender<LayoutMsg>,
/// A channel to schedule timer events.
diff --git a/components/servo/lib.rs b/components/servo/lib.rs
index e518d1aaa3d..74e286c6e5e 100644
--- a/components/servo/lib.rs
+++ b/components/servo/lib.rs
@@ -1051,11 +1051,7 @@ pub fn run_content_process(token: String) {
set_logger(content.script_to_constellation_chan().clone());
- let background_hang_monitor_register = if opts::get().background_hang_monitor {
- content.register_with_background_hang_monitor()
- } else {
- None
- };
+ let background_hang_monitor_register = content.register_with_background_hang_monitor();
content.start_all::<script_layout_interface::message::Msg,
layout_thread::LayoutThread,