mirror of
https://github.com/servo/servo.git
synced 2025-08-02 12:10:29 +01:00
Prevent zombie processes in multi-process mode. (#36329)
This introduces a process manager that holds for each process a "lifeline": this is the receiving end of a ipc channel that is not used to send anything, but only to monitor the process presence. We turn that ipc receiver into a crossbeam one to integrate the monitoring into the constellation run loop. The sender side is made part of the initial "UnprivilegedContent" data structure sent to the new process, both for content and for service worker processes. When a process dies we currently wait() on it to let the OS do a clean shutdown. Signed-off-by: webbeef <me@webbeef.org>
This commit is contained in:
parent
c09c31ef85
commit
c7a7862574
6 changed files with 177 additions and 44 deletions
|
@ -113,7 +113,7 @@ use constellation_traits::{
|
||||||
AnimationTickType, CompositorHitTestResult, ConstellationMsg as FromCompositorMsg, LogEntry,
|
AnimationTickType, CompositorHitTestResult, ConstellationMsg as FromCompositorMsg, LogEntry,
|
||||||
PaintMetricEvent, ScrollState, TraversalDirection, WindowSizeType,
|
PaintMetricEvent, ScrollState, TraversalDirection, WindowSizeType,
|
||||||
};
|
};
|
||||||
use crossbeam_channel::{Receiver, Sender, select, unbounded};
|
use crossbeam_channel::{Receiver, Select, Sender, unbounded};
|
||||||
use devtools_traits::{
|
use devtools_traits::{
|
||||||
ChromeToDevtoolsControlMsg, DevtoolsControlMsg, DevtoolsPageInfo, NavigationState,
|
ChromeToDevtoolsControlMsg, DevtoolsControlMsg, DevtoolsPageInfo, NavigationState,
|
||||||
ScriptToDevtoolsControlMsg,
|
ScriptToDevtoolsControlMsg,
|
||||||
|
@ -171,6 +171,7 @@ use crate::browsingcontext::{
|
||||||
};
|
};
|
||||||
use crate::event_loop::EventLoop;
|
use crate::event_loop::EventLoop;
|
||||||
use crate::pipeline::{InitialPipelineState, Pipeline};
|
use crate::pipeline::{InitialPipelineState, Pipeline};
|
||||||
|
use crate::process_manager::ProcessManager;
|
||||||
use crate::serviceworker::ServiceWorkerUnprivilegedContent;
|
use crate::serviceworker::ServiceWorkerUnprivilegedContent;
|
||||||
use crate::session_history::{
|
use crate::session_history::{
|
||||||
JointSessionHistory, NeedsToReload, SessionHistoryChange, SessionHistoryDiff,
|
JointSessionHistory, NeedsToReload, SessionHistoryChange, SessionHistoryDiff,
|
||||||
|
@ -469,6 +470,9 @@ pub struct Constellation<STF, SWF> {
|
||||||
|
|
||||||
/// User content manager
|
/// User content manager
|
||||||
user_content_manager: UserContentManager,
|
user_content_manager: UserContentManager,
|
||||||
|
|
||||||
|
/// The process manager.
|
||||||
|
process_manager: ProcessManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State needed to construct a constellation.
|
/// State needed to construct a constellation.
|
||||||
|
@ -735,6 +739,7 @@ where
|
||||||
active_media_session: None,
|
active_media_session: None,
|
||||||
rippy_data,
|
rippy_data,
|
||||||
user_content_manager: state.user_content_manager,
|
user_content_manager: state.user_content_manager,
|
||||||
|
process_manager: ProcessManager::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
constellation.run();
|
constellation.run();
|
||||||
|
@ -996,6 +1001,12 @@ where
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some((lifeline_receiver, process)) = pipeline.lifeline {
|
||||||
|
let crossbeam_receiver =
|
||||||
|
route_ipc_receiver_to_new_crossbeam_receiver_preserving_errors(lifeline_receiver);
|
||||||
|
self.process_manager.add(crossbeam_receiver, process);
|
||||||
|
}
|
||||||
|
|
||||||
assert!(!self.pipelines.contains_key(&pipeline_id));
|
assert!(!self.pipelines.contains_key(&pipeline_id));
|
||||||
self.pipelines.insert(pipeline_id, pipeline.pipeline);
|
self.pipelines.insert(pipeline_id, pipeline.pipeline);
|
||||||
}
|
}
|
||||||
|
@ -1114,6 +1125,7 @@ where
|
||||||
BackgroundHangMonitor(HangMonitorAlert),
|
BackgroundHangMonitor(HangMonitorAlert),
|
||||||
Compositor(FromCompositorMsg),
|
Compositor(FromCompositorMsg),
|
||||||
FromSWManager(SWManagerMsg),
|
FromSWManager(SWManagerMsg),
|
||||||
|
RemoveProcess(usize),
|
||||||
}
|
}
|
||||||
// Get one incoming request.
|
// Get one incoming request.
|
||||||
// This is one of the few places where the compositor is
|
// This is one of the few places where the compositor is
|
||||||
|
@ -1126,26 +1138,49 @@ where
|
||||||
// produces undefined behaviour, resulting in the destructor
|
// produces undefined behaviour, resulting in the destructor
|
||||||
// being called. If this happens, there's not much we can do
|
// being called. If this happens, there's not much we can do
|
||||||
// other than panic.
|
// other than panic.
|
||||||
|
let mut sel = Select::new();
|
||||||
|
sel.recv(&self.namespace_receiver);
|
||||||
|
sel.recv(&self.script_receiver);
|
||||||
|
sel.recv(&self.background_hang_monitor_receiver);
|
||||||
|
sel.recv(&self.compositor_receiver);
|
||||||
|
sel.recv(&self.swmanager_receiver);
|
||||||
|
|
||||||
|
self.process_manager.register(&mut sel);
|
||||||
|
|
||||||
let request = {
|
let request = {
|
||||||
|
let oper = sel.select();
|
||||||
|
let index = oper.index();
|
||||||
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _span =
|
let _span =
|
||||||
tracing::trace_span!("handle_request::select", servo_profiling = true).entered();
|
tracing::trace_span!("handle_request::select", servo_profiling = true).entered();
|
||||||
select! {
|
match index {
|
||||||
recv(self.namespace_receiver) -> msg => {
|
0 => oper
|
||||||
msg.expect("Unexpected script channel panic in constellation").map(Request::PipelineNamespace)
|
.recv(&self.namespace_receiver)
|
||||||
}
|
.expect("Unexpected script channel panic in constellation")
|
||||||
recv(self.script_receiver) -> msg => {
|
.map(Request::PipelineNamespace),
|
||||||
msg.expect("Unexpected script channel panic in constellation").map(Request::Script)
|
1 => oper
|
||||||
}
|
.recv(&self.script_receiver)
|
||||||
recv(self.background_hang_monitor_receiver) -> msg => {
|
.expect("Unexpected script channel panic in constellation")
|
||||||
msg.expect("Unexpected BHM channel panic in constellation").map(Request::BackgroundHangMonitor)
|
.map(Request::Script),
|
||||||
}
|
2 => oper
|
||||||
recv(self.compositor_receiver) -> msg => {
|
.recv(&self.background_hang_monitor_receiver)
|
||||||
Ok(Request::Compositor(msg.expect("Unexpected compositor channel panic in constellation")))
|
.expect("Unexpected BHM channel panic in constellation")
|
||||||
}
|
.map(Request::BackgroundHangMonitor),
|
||||||
recv(self.swmanager_receiver) -> msg => {
|
3 => Ok(Request::Compositor(
|
||||||
msg.expect("Unexpected SW channel panic in constellation").map(Request::FromSWManager)
|
oper.recv(&self.compositor_receiver)
|
||||||
}
|
.expect("Unexpected compositor channel panic in constellation"),
|
||||||
|
)),
|
||||||
|
4 => oper
|
||||||
|
.recv(&self.swmanager_receiver)
|
||||||
|
.expect("Unexpected SW channel panic in constellation")
|
||||||
|
.map(Request::FromSWManager),
|
||||||
|
_ => {
|
||||||
|
// This can only be a error reading on a closed lifeline receiver.
|
||||||
|
let process_index = index - 5;
|
||||||
|
let _ = oper.recv(self.process_manager.receiver_at(process_index));
|
||||||
|
Ok(Request::RemoveProcess(process_index))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1168,6 +1203,7 @@ where
|
||||||
Request::FromSWManager(message) => {
|
Request::FromSWManager(message) => {
|
||||||
self.handle_request_from_swmanager(message);
|
self.handle_request_from_swmanager(message);
|
||||||
},
|
},
|
||||||
|
Request::RemoveProcess(index) => self.process_manager.remove(index),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2398,13 +2434,24 @@ where
|
||||||
own_sender: own_sender.clone(),
|
own_sender: own_sender.clone(),
|
||||||
receiver,
|
receiver,
|
||||||
};
|
};
|
||||||
let content = ServiceWorkerUnprivilegedContent::new(sw_senders, origin);
|
|
||||||
|
|
||||||
if opts::get().multiprocess {
|
if opts::get().multiprocess {
|
||||||
if content.spawn_multiprocess().is_err() {
|
let (sender, receiver) =
|
||||||
|
ipc::channel().expect("Failed to create lifeline channel for sw");
|
||||||
|
let content =
|
||||||
|
ServiceWorkerUnprivilegedContent::new(sw_senders, origin, Some(sender));
|
||||||
|
|
||||||
|
if let Ok(process) = content.spawn_multiprocess() {
|
||||||
|
let crossbeam_receiver =
|
||||||
|
route_ipc_receiver_to_new_crossbeam_receiver_preserving_errors(
|
||||||
|
receiver,
|
||||||
|
);
|
||||||
|
self.process_manager.add(crossbeam_receiver, process);
|
||||||
|
} else {
|
||||||
return warn!("Failed to spawn process for SW manager.");
|
return warn!("Failed to spawn process for SW manager.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let content = ServiceWorkerUnprivilegedContent::new(sw_senders, origin, None);
|
||||||
content.start::<SWF>();
|
content.start::<SWF>();
|
||||||
}
|
}
|
||||||
entry.insert(own_sender)
|
entry.insert(own_sender)
|
||||||
|
|
|
@ -12,6 +12,7 @@ mod constellation;
|
||||||
mod event_loop;
|
mod event_loop;
|
||||||
mod logging;
|
mod logging;
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
|
mod process_manager;
|
||||||
mod sandboxing;
|
mod sandboxing;
|
||||||
mod serviceworker;
|
mod serviceworker;
|
||||||
mod session_history;
|
mod session_history;
|
||||||
|
|
|
@ -46,6 +46,7 @@ use webrender_api::DocumentId;
|
||||||
use webrender_traits::CrossProcessCompositorApi;
|
use webrender_traits::CrossProcessCompositorApi;
|
||||||
|
|
||||||
use crate::event_loop::EventLoop;
|
use crate::event_loop::EventLoop;
|
||||||
|
use crate::process_manager::Process;
|
||||||
use crate::sandboxing::{UnprivilegedContent, spawn_multiprocess};
|
use crate::sandboxing::{UnprivilegedContent, spawn_multiprocess};
|
||||||
|
|
||||||
/// A `Pipeline` is the constellation's view of a `Window`. Each pipeline has an event loop
|
/// A `Pipeline` is the constellation's view of a `Window`. Each pipeline has an event loop
|
||||||
|
@ -201,6 +202,7 @@ pub struct InitialPipelineState {
|
||||||
pub struct NewPipeline {
|
pub struct NewPipeline {
|
||||||
pub pipeline: Pipeline,
|
pub pipeline: Pipeline,
|
||||||
pub bhm_control_chan: Option<IpcSender<BackgroundHangMonitorControlMsg>>,
|
pub bhm_control_chan: Option<IpcSender<BackgroundHangMonitorControlMsg>>,
|
||||||
|
pub lifeline: Option<(IpcReceiver<()>, Process)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pipeline {
|
impl Pipeline {
|
||||||
|
@ -210,7 +212,7 @@ impl Pipeline {
|
||||||
) -> Result<NewPipeline, Error> {
|
) -> Result<NewPipeline, Error> {
|
||||||
// Note: we allow channel creation to panic, since recovering from this
|
// Note: we allow channel creation to panic, since recovering from this
|
||||||
// probably requires a general low-memory strategy.
|
// probably requires a general low-memory strategy.
|
||||||
let (script_chan, bhm_control_chan) = match state.event_loop {
|
let (script_chan, (bhm_control_chan, lifeline)) = match state.event_loop {
|
||||||
Some(script_chan) => {
|
Some(script_chan) => {
|
||||||
let new_layout_info = NewLayoutInfo {
|
let new_layout_info = NewLayoutInfo {
|
||||||
parent_info: state.parent_pipeline_id,
|
parent_info: state.parent_pipeline_id,
|
||||||
|
@ -226,7 +228,7 @@ impl Pipeline {
|
||||||
{
|
{
|
||||||
warn!("Sending to script during pipeline creation failed ({})", e);
|
warn!("Sending to script during pipeline creation failed ({})", e);
|
||||||
}
|
}
|
||||||
(script_chan, None)
|
(script_chan, (None, None))
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let (script_chan, script_port) = ipc::channel().expect("Pipeline script chan");
|
let (script_chan, script_port) = ipc::channel().expect("Pipeline script chan");
|
||||||
|
@ -292,17 +294,21 @@ impl Pipeline {
|
||||||
player_context: state.player_context,
|
player_context: state.player_context,
|
||||||
rippy_data: state.rippy_data,
|
rippy_data: state.rippy_data,
|
||||||
user_content_manager: state.user_content_manager,
|
user_content_manager: state.user_content_manager,
|
||||||
|
lifeline_sender: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Spawn the child process.
|
// Spawn the child process.
|
||||||
//
|
//
|
||||||
// Yes, that's all there is to it!
|
// Yes, that's all there is to it!
|
||||||
let bhm_control_chan = if opts::get().multiprocess {
|
let multiprocess_data = if opts::get().multiprocess {
|
||||||
let (bhm_control_chan, bhm_control_port) =
|
let (bhm_control_chan, bhm_control_port) =
|
||||||
ipc::channel().expect("Sampler chan");
|
ipc::channel().expect("Sampler chan");
|
||||||
unprivileged_pipeline_content.bhm_control_port = Some(bhm_control_port);
|
unprivileged_pipeline_content.bhm_control_port = Some(bhm_control_port);
|
||||||
unprivileged_pipeline_content.spawn_multiprocess()?;
|
let (sender, receiver) =
|
||||||
Some(bhm_control_chan)
|
ipc::channel().expect("Failed to create lifeline channel");
|
||||||
|
unprivileged_pipeline_content.lifeline_sender = Some(sender);
|
||||||
|
let process = unprivileged_pipeline_content.spawn_multiprocess()?;
|
||||||
|
(Some(bhm_control_chan), Some((receiver, process)))
|
||||||
} else {
|
} else {
|
||||||
// Should not be None in single-process mode.
|
// Should not be None in single-process mode.
|
||||||
let register = state
|
let register = state
|
||||||
|
@ -313,10 +319,10 @@ impl Pipeline {
|
||||||
state.layout_factory,
|
state.layout_factory,
|
||||||
register,
|
register,
|
||||||
);
|
);
|
||||||
None
|
(None, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
(EventLoop::new(script_chan), bhm_control_chan)
|
(EventLoop::new(script_chan), multiprocess_data)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -333,6 +339,7 @@ impl Pipeline {
|
||||||
Ok(NewPipeline {
|
Ok(NewPipeline {
|
||||||
pipeline,
|
pipeline,
|
||||||
bhm_control_chan,
|
bhm_control_chan,
|
||||||
|
lifeline,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,6 +505,7 @@ pub struct UnprivilegedPipelineContent {
|
||||||
player_context: WindowGLContext,
|
player_context: WindowGLContext,
|
||||||
rippy_data: Vec<u8>,
|
rippy_data: Vec<u8>,
|
||||||
user_content_manager: UserContentManager,
|
user_content_manager: UserContentManager,
|
||||||
|
lifeline_sender: Option<IpcSender<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnprivilegedPipelineContent {
|
impl UnprivilegedPipelineContent {
|
||||||
|
@ -558,7 +566,7 @@ impl UnprivilegedPipelineContent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_multiprocess(self) -> Result<(), Error> {
|
pub fn spawn_multiprocess(self) -> Result<Process, Error> {
|
||||||
spawn_multiprocess(UnprivilegedContent::Pipeline(self))
|
spawn_multiprocess(UnprivilegedContent::Pipeline(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
68
components/constellation/process_manager.rs
Normal file
68
components/constellation/process_manager.rs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use std::process::Child;
|
||||||
|
|
||||||
|
use crossbeam_channel::{Receiver, Select};
|
||||||
|
use log::{debug, warn};
|
||||||
|
|
||||||
|
pub enum Process {
|
||||||
|
Unsandboxed(Child),
|
||||||
|
Sandboxed(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Process {
|
||||||
|
fn pid(&self) -> u32 {
|
||||||
|
match self {
|
||||||
|
Self::Unsandboxed(child) => child.id(),
|
||||||
|
Self::Sandboxed(pid) => *pid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait(&mut self) {
|
||||||
|
match self {
|
||||||
|
Self::Unsandboxed(child) => {
|
||||||
|
let _ = child.wait();
|
||||||
|
},
|
||||||
|
Self::Sandboxed(_pid) => {
|
||||||
|
// TODO: use nix::waitpid() on supported platforms.
|
||||||
|
warn!("wait() is not yet implemented for sandboxed processes.");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProcessReceiver = Receiver<Result<(), ipc_channel::Error>>;
|
||||||
|
|
||||||
|
pub(crate) struct ProcessManager {
|
||||||
|
processes: Vec<(Process, ProcessReceiver)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { processes: vec![] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, receiver: ProcessReceiver, process: Process) {
|
||||||
|
debug!("Adding process pid={}", process.pid());
|
||||||
|
self.processes.push((process, receiver));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register<'a>(&'a self, select: &mut Select<'a>) {
|
||||||
|
for (_, receiver) in &self.processes {
|
||||||
|
select.recv(receiver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receiver_at(&self, index: usize) -> &ProcessReceiver {
|
||||||
|
let (_, receiver) = &self.processes[index];
|
||||||
|
receiver
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, index: usize) {
|
||||||
|
let (mut process, _) = self.processes.swap_remove(index);
|
||||||
|
debug!("Removing process pid={}", process.pid());
|
||||||
|
process.wait();
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ use servo_config::opts::Opts;
|
||||||
use servo_config::prefs::Preferences;
|
use servo_config::prefs::Preferences;
|
||||||
|
|
||||||
use crate::pipeline::UnprivilegedPipelineContent;
|
use crate::pipeline::UnprivilegedPipelineContent;
|
||||||
|
use crate::process_manager::Process;
|
||||||
use crate::serviceworker::ServiceWorkerUnprivilegedContent;
|
use crate::serviceworker::ServiceWorkerUnprivilegedContent;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
|
@ -146,7 +147,7 @@ pub fn content_process_sandbox_profile() {
|
||||||
target_arch = "arm",
|
target_arch = "arm",
|
||||||
all(target_arch = "aarch64", not(target_os = "windows"))
|
all(target_arch = "aarch64", not(target_os = "windows"))
|
||||||
))]
|
))]
|
||||||
pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<(), Error> {
|
pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<Process, Error> {
|
||||||
use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
|
use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
|
||||||
// Note that this function can panic, due to process creation,
|
// Note that this function can panic, due to process creation,
|
||||||
// avoiding this panic would require a mechanism for dealing
|
// avoiding this panic would require a mechanism for dealing
|
||||||
|
@ -158,15 +159,14 @@ pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<(), Error> {
|
||||||
let mut child_process = process::Command::new(path_to_self);
|
let mut child_process = process::Command::new(path_to_self);
|
||||||
setup_common(&mut child_process, token);
|
setup_common(&mut child_process, token);
|
||||||
|
|
||||||
#[allow(clippy::zombie_processes)]
|
let child = child_process
|
||||||
let _ = child_process
|
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("Failed to start unsandboxed child process!");
|
.expect("Failed to start unsandboxed child process!");
|
||||||
|
|
||||||
let (_receiver, sender) = server.accept().expect("Server failed to accept.");
|
let (_receiver, sender) = server.accept().expect("Server failed to accept.");
|
||||||
sender.send(content)?;
|
sender.send(content)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(Process::Unsandboxed(child))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
|
@ -177,7 +177,7 @@ pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<(), Error> {
|
||||||
not(target_arch = "arm"),
|
not(target_arch = "arm"),
|
||||||
not(target_arch = "aarch64")
|
not(target_arch = "aarch64")
|
||||||
))]
|
))]
|
||||||
pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<(), Error> {
|
pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<Process, Error> {
|
||||||
use gaol::sandbox::{self, Sandbox, SandboxMethods};
|
use gaol::sandbox::{self, Sandbox, SandboxMethods};
|
||||||
use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
|
use ipc_channel::ipc::{IpcOneShotServer, IpcSender};
|
||||||
|
|
||||||
|
@ -208,33 +208,37 @@ pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<(), Error> {
|
||||||
.expect("Failed to create IPC one-shot server.");
|
.expect("Failed to create IPC one-shot server.");
|
||||||
|
|
||||||
// If there is a sandbox, use the `gaol` API to create the child process.
|
// If there is a sandbox, use the `gaol` API to create the child process.
|
||||||
if content.opts().sandbox {
|
let process = if content.opts().sandbox {
|
||||||
let mut command = sandbox::Command::me().expect("Failed to get current sandbox.");
|
let mut command = sandbox::Command::me().expect("Failed to get current sandbox.");
|
||||||
setup_common(&mut command, token);
|
setup_common(&mut command, token);
|
||||||
|
|
||||||
let profile = content_process_sandbox_profile();
|
let profile = content_process_sandbox_profile();
|
||||||
let _ = Sandbox::new(profile)
|
Process::Sandboxed(
|
||||||
.start(&mut command)
|
Sandbox::new(profile)
|
||||||
.expect("Failed to start sandboxed child process!");
|
.start(&mut command)
|
||||||
|
.expect("Failed to start sandboxed child process!")
|
||||||
|
.pid as u32,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
let path_to_self = env::current_exe().expect("Failed to get current executor.");
|
let path_to_self = env::current_exe().expect("Failed to get current executor.");
|
||||||
let mut child_process = process::Command::new(path_to_self);
|
let mut child_process = process::Command::new(path_to_self);
|
||||||
setup_common(&mut child_process, token);
|
setup_common(&mut child_process, token);
|
||||||
|
|
||||||
#[allow(clippy::zombie_processes)]
|
Process::Unsandboxed(
|
||||||
let _ = child_process
|
child_process
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("Failed to start unsandboxed child process!");
|
.expect("Failed to start unsandboxed child process!"),
|
||||||
}
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let (_receiver, sender) = server.accept().expect("Server failed to accept.");
|
let (_receiver, sender) = server.accept().expect("Server failed to accept.");
|
||||||
sender.send(content)?;
|
sender.send(content)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(process)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(target_os = "windows", target_os = "ios"))]
|
#[cfg(any(target_os = "windows", target_os = "ios"))]
|
||||||
pub fn spawn_multiprocess(_content: UnprivilegedContent) -> Result<(), Error> {
|
pub fn spawn_multiprocess(_content: UnprivilegedContent) -> Result<Process, Error> {
|
||||||
log::error!("Multiprocess is not supported on Windows or iOS.");
|
log::error!("Multiprocess is not supported on Windows or iOS.");
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use ipc_channel::Error;
|
use ipc_channel::Error;
|
||||||
|
use ipc_channel::ipc::IpcSender;
|
||||||
use script_traits::{SWManagerSenders, ServiceWorkerManagerFactory};
|
use script_traits::{SWManagerSenders, ServiceWorkerManagerFactory};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use servo_config::opts::{self, Opts};
|
use servo_config::opts::{self, Opts};
|
||||||
|
@ -10,6 +11,7 @@ use servo_config::prefs;
|
||||||
use servo_config::prefs::Preferences;
|
use servo_config::prefs::Preferences;
|
||||||
use servo_url::ImmutableOrigin;
|
use servo_url::ImmutableOrigin;
|
||||||
|
|
||||||
|
use crate::process_manager::Process;
|
||||||
use crate::sandboxing::{UnprivilegedContent, spawn_multiprocess};
|
use crate::sandboxing::{UnprivilegedContent, spawn_multiprocess};
|
||||||
|
|
||||||
/// Conceptually, this is glue to start an agent-cluster for a service worker agent.
|
/// Conceptually, this is glue to start an agent-cluster for a service worker agent.
|
||||||
|
@ -20,18 +22,21 @@ pub struct ServiceWorkerUnprivilegedContent {
|
||||||
prefs: Box<Preferences>,
|
prefs: Box<Preferences>,
|
||||||
senders: SWManagerSenders,
|
senders: SWManagerSenders,
|
||||||
origin: ImmutableOrigin,
|
origin: ImmutableOrigin,
|
||||||
|
lifeline_sender: Option<IpcSender<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServiceWorkerUnprivilegedContent {
|
impl ServiceWorkerUnprivilegedContent {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
senders: SWManagerSenders,
|
senders: SWManagerSenders,
|
||||||
origin: ImmutableOrigin,
|
origin: ImmutableOrigin,
|
||||||
|
lifeline_sender: Option<IpcSender<()>>,
|
||||||
) -> ServiceWorkerUnprivilegedContent {
|
) -> ServiceWorkerUnprivilegedContent {
|
||||||
ServiceWorkerUnprivilegedContent {
|
ServiceWorkerUnprivilegedContent {
|
||||||
opts: (*opts::get()).clone(),
|
opts: (*opts::get()).clone(),
|
||||||
prefs: Box::new(prefs::get().clone()),
|
prefs: Box::new(prefs::get().clone()),
|
||||||
senders,
|
senders,
|
||||||
origin,
|
origin,
|
||||||
|
lifeline_sender,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +49,7 @@ impl ServiceWorkerUnprivilegedContent {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start the agent-cluster in it's own process.
|
/// Start the agent-cluster in it's own process.
|
||||||
pub fn spawn_multiprocess(self) -> Result<(), Error> {
|
pub fn spawn_multiprocess(self) -> Result<Process, Error> {
|
||||||
spawn_multiprocess(UnprivilegedContent::ServiceWorker(self))
|
spawn_multiprocess(UnprivilegedContent::ServiceWorker(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue