Skip to content

Commit 13009fe

Browse files
author
bors-servo
authored
Auto merge of #14052 - asajeffrey:constellation-manages-script-thread-lifetime, r=jdm
Constellation manages script thread lifetime <!-- Please describe your changes on the following line: --> Yet another step towards fixing #633... At the moment, script threads are responsible for killing themselves when they have no more pipelines to manage. This is fine right now, because script threads have a root pipeline: once it is closed, everything is closed. It's not fine once we fix #633 because there's a race condition where the constellation could kill the last pipeline in a script thread, then open a new one in the same script thread. We can't have the constellation block on the script thread, waiting to make sure a pipeline is created, since this could introduce deadlock. The easiest solution is to have the constellation manage the script thread lifetime: when the constellation discovers that a script thread is not managing any live pipelines, it closes the script thread, and updates its own state. Most of the commits are from #14013, its just "Script thread lifetime is now managed by the constellation." (9ac6e4d) that's new. cc @jdm @ConnorGBrewster --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes do not require tests because refactoring <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/14052) <!-- Reviewable:end -->
2 parents 3ba56df + 0e7ffaf commit 13009fe

File tree

4 files changed

+61
-22
lines changed

4 files changed

+61
-22
lines changed

components/constellation/constellation.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ use std::iter::once;
5353
use std::marker::PhantomData;
5454
use std::mem::replace;
5555
use std::process;
56+
use std::rc::Rc;
5657
use std::sync::Arc;
5758
use std::sync::mpsc::{Receiver, Sender, channel};
5859
use std::thread;
@@ -365,6 +366,30 @@ enum ExitPipelineMode {
365366
Force,
366367
}
367368

369+
/// A script channel, that closes the script thread down when it is dropped
370+
pub struct ScriptChan {
371+
chan: IpcSender<ConstellationControlMsg>,
372+
dont_send_or_sync: PhantomData<Rc<()>>,
373+
}
374+
375+
impl Drop for ScriptChan {
376+
fn drop(&mut self) {
377+
let _ = self.chan.send(ConstellationControlMsg::ExitScriptThread);
378+
}
379+
}
380+
381+
impl ScriptChan {
382+
pub fn send(&self, msg: ConstellationControlMsg) -> Result<(), IOError> {
383+
self.chan.send(msg)
384+
}
385+
pub fn new(chan: IpcSender<ConstellationControlMsg>) -> Rc<ScriptChan> {
386+
Rc::new(ScriptChan { chan: chan, dont_send_or_sync: PhantomData })
387+
}
388+
pub fn sender(&self) -> IpcSender<ConstellationControlMsg> {
389+
self.chan.clone()
390+
}
391+
}
392+
368393
/// A logger directed at the constellation from content processes
369394
#[derive(Clone)]
370395
pub struct FromScriptLogger {
@@ -561,7 +586,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
561586
parent_info: Option<(PipelineId, FrameType)>,
562587
old_pipeline_id: Option<PipelineId>,
563588
initial_window_size: Option<TypedSize2D<f32, PagePx>>,
564-
script_channel: Option<IpcSender<ConstellationControlMsg>>,
589+
script_channel: Option<Rc<ScriptChan>>,
565590
load_data: LoadData,
566591
is_private: bool) {
567592
if self.shutting_down { return; }

components/constellation/pipeline.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use bluetooth_traits::BluetoothRequest;
66
use compositing::CompositionPipeline;
77
use compositing::CompositorProxy;
88
use compositing::compositor_thread::Msg as CompositorMsg;
9+
use constellation::ScriptChan;
910
use devtools_traits::{DevtoolsControlMsg, ScriptToDevtoolsControlMsg};
1011
use euclid::scale_factor::ScaleFactor;
1112
use euclid::size::TypedSize2D;
@@ -30,6 +31,7 @@ use std::env;
3031
use std::ffi::OsStr;
3132
use std::io::Error as IOError;
3233
use std::process;
34+
use std::rc::Rc;
3335
use std::sync::mpsc::Sender;
3436
use style_traits::{PagePx, ViewportPx};
3537
use url::Url;
@@ -50,7 +52,7 @@ pub struct Pipeline {
5052
/// The ID of the frame that contains this Pipeline.
5153
pub frame_id: FrameId,
5254
pub parent_info: Option<(PipelineId, FrameType)>,
53-
pub script_chan: IpcSender<ConstellationControlMsg>,
55+
pub script_chan: Rc<ScriptChan>,
5456
/// A channel to layout, for performing reflows and shutdown.
5557
pub layout_chan: IpcSender<LayoutControlMsg>,
5658
/// A channel to the compositor.
@@ -113,7 +115,7 @@ pub struct InitialPipelineState {
113115
pub device_pixel_ratio: ScaleFactor<f32, ViewportPx, DevicePixel>,
114116
/// A channel to the script thread, if applicable. If this is `Some`,
115117
/// then `parent_info` must also be `Some`.
116-
pub script_chan: Option<IpcSender<ConstellationControlMsg>>,
118+
pub script_chan: Option<Rc<ScriptChan>>,
117119
/// Information about the page to load.
118120
pub load_data: LoadData,
119121
/// The ID of the pipeline namespace for this script thread.
@@ -165,7 +167,7 @@ impl Pipeline {
165167
}
166168
None => {
167169
let (script_chan, script_port) = ipc::channel().expect("Pipeline script chan");
168-
(script_chan, Some((script_port, pipeline_port)))
170+
(ScriptChan::new(script_chan), Some((script_port, pipeline_port)))
169171
}
170172
};
171173

@@ -215,7 +217,7 @@ impl Pipeline {
215217
mem_profiler_chan: state.mem_profiler_chan,
216218
window_size: window_size,
217219
layout_to_constellation_chan: state.layout_to_constellation_chan,
218-
script_chan: script_chan.clone(),
220+
script_chan: script_chan.sender(),
219221
load_data: state.load_data.clone(),
220222
script_port: script_port,
221223
opts: (*opts::get()).clone(),
@@ -258,7 +260,7 @@ impl Pipeline {
258260
fn new(id: PipelineId,
259261
frame_id: FrameId,
260262
parent_info: Option<(PipelineId, FrameType)>,
261-
script_chan: IpcSender<ConstellationControlMsg>,
263+
script_chan: Rc<ScriptChan>,
262264
layout_chan: IpcSender<LayoutControlMsg>,
263265
compositor_proxy: Box<CompositorProxy + 'static + Send>,
264266
is_private: bool,
@@ -329,7 +331,7 @@ impl Pipeline {
329331
pub fn to_sendable(&self) -> CompositionPipeline {
330332
CompositionPipeline {
331333
id: self.id.clone(),
332-
script_chan: self.script_chan.clone(),
334+
script_chan: self.script_chan.sender(),
333335
layout_chan: self.layout_chan.clone(),
334336
}
335337
}

components/script/script_thread.rs

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -840,10 +840,9 @@ impl ScriptThread {
840840

841841
let result = self.profile_event(category, move || {
842842
match msg {
843-
FromConstellation(ConstellationControlMsg::ExitPipeline(id)) => {
844-
if self.handle_exit_pipeline_msg(id) {
845-
return Some(false)
846-
}
843+
FromConstellation(ConstellationControlMsg::ExitScriptThread) => {
844+
self.handle_exit_script_thread_msg();
845+
return Some(false);
847846
},
848847
FromConstellation(inner_msg) => self.handle_msg_from_constellation(inner_msg),
849848
FromScript(inner_msg) => self.handle_msg_from_script(inner_msg),
@@ -990,11 +989,13 @@ impl ScriptThread {
990989
self.handle_css_error_reporting(pipeline_id, filename, line, column, msg),
991990
ConstellationControlMsg::Reload(pipeline_id) =>
992991
self.handle_reload(pipeline_id),
992+
ConstellationControlMsg::ExitPipeline(pipeline_id) =>
993+
self.handle_exit_pipeline_msg(pipeline_id),
993994
msg @ ConstellationControlMsg::AttachLayout(..) |
994995
msg @ ConstellationControlMsg::Viewport(..) |
995996
msg @ ConstellationControlMsg::SetScrollState(..) |
996997
msg @ ConstellationControlMsg::Resize(..) |
997-
msg @ ConstellationControlMsg::ExitPipeline(..) =>
998+
msg @ ConstellationControlMsg::ExitScriptThread =>
998999
panic!("should have handled {:?} already", msg),
9991000
}
10001001
}
@@ -1487,10 +1488,9 @@ impl ScriptThread {
14871488
document.send_title_to_compositor();
14881489
}
14891490

1490-
/// Handles a request to exit the script thread and shut down layout.
1491-
/// Returns true if the script thread should shut down and false otherwise.
1492-
fn handle_exit_pipeline_msg(&self, id: PipelineId) -> bool {
1493-
debug!("Exiting pipeline {:?}.", id);
1491+
/// Handles a request to exit a pipeline and shut down layout.
1492+
fn handle_exit_pipeline_msg(&self, id: PipelineId) {
1493+
debug!("Exiting pipeline {}.", id);
14941494

14951495
self.closed_pipelines.borrow_mut().insert(id);
14961496

@@ -1507,7 +1507,7 @@ impl ScriptThread {
15071507
let (response_chan, response_port) = channel();
15081508
let chan = &load.layout_chan;
15091509
if chan.send(message::Msg::PrepareToExit(response_chan)).is_ok() {
1510-
debug!("shutting down layout for page {:?}", id);
1510+
debug!("shutting down layout for page {}", id);
15111511
response_port.recv().unwrap();
15121512
chan.send(message::Msg::ExitNow).ok();
15131513
}
@@ -1518,13 +1518,22 @@ impl ScriptThread {
15181518
let _ = self.constellation_chan.send(ConstellationMsg::PipelineExited(id));
15191519
}
15201520

1521-
let no_pending_loads = self.incomplete_loads.borrow().is_empty();
1522-
let no_remaining_contexts = self.documents.borrow().is_empty();
1521+
debug!("Exited pipeline {}.", id);
1522+
}
15231523

1524-
debug!("Exited pipeline {:?} ({}&{}).", id, no_pending_loads, no_remaining_contexts);
1524+
/// Handles a request to exit the script thread and shut down layout.
1525+
fn handle_exit_script_thread_msg(&self) {
1526+
debug!("Exiting script thread.");
1527+
1528+
let mut pipeline_ids = Vec::new();
1529+
pipeline_ids.extend(self.incomplete_loads.borrow().iter().next().map(|load| load.pipeline_id));
1530+
pipeline_ids.extend(self.documents.borrow().iter().next().map(|(pipeline_id, _)| pipeline_id));
1531+
1532+
for pipeline_id in pipeline_ids {
1533+
self.handle_exit_pipeline_msg(pipeline_id);
1534+
}
15251535

1526-
// Exit if no pending loads and no remaining contexts
1527-
no_pending_loads && no_remaining_contexts
1536+
debug!("Exited script thread.");
15281537
}
15291538

15301539
/// Handles when layout thread finishes all animation in one tick

components/script_traits/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ pub enum ConstellationControlMsg {
195195
ResizeInactive(PipelineId, WindowSizeData),
196196
/// Notifies the script that a pipeline should be closed.
197197
ExitPipeline(PipelineId),
198+
/// Notifies the script that the whole thread should be closed.
199+
ExitScriptThread,
198200
/// Sends a DOM event.
199201
SendEvent(PipelineId, CompositorEvent),
200202
/// Notifies script of the viewport.
@@ -259,6 +261,7 @@ impl fmt::Debug for ConstellationControlMsg {
259261
Resize(..) => "Resize",
260262
ResizeInactive(..) => "ResizeInactive",
261263
ExitPipeline(..) => "ExitPipeline",
264+
ExitScriptThread => "ExitScriptThread",
262265
SendEvent(..) => "SendEvent",
263266
Viewport(..) => "Viewport",
264267
SetScrollState(..) => "SetScrollState",

0 commit comments

Comments
 (0)