mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Auto merge of #8599 - jdm:e10s-redux, r=metajack
compositing: Split Servo up into multiple sandboxed processes. Multiprocess mode is enabled with the `-M` switch, and sandboxing is enabled with the `-S` switch. Rebase of #6884. <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/8599) <!-- Reviewable:end -->
This commit is contained in:
commit
8b39b9afed
33 changed files with 688 additions and 265 deletions
|
@ -2,18 +2,20 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use ipc_channel::ipc::{self, IpcSender};
|
||||
use ipc_channel::ipc::{self, IpcSender, OpaqueIpcSender};
|
||||
use ipc_channel::router::ROUTER;
|
||||
use opts;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::any::Any;
|
||||
use std::any::{Any, TypeId};
|
||||
use std::collections::HashMap;
|
||||
use std::marker::Reflect;
|
||||
use std::mem;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
|
||||
lazy_static! {
|
||||
static ref IN_PROCESS_SENDERS: Mutex<HashMap<usize, Box<Any + Send>>> =
|
||||
static ref IN_PROCESS_SENDERS: Mutex<HashMap<usize, OpaqueSender>> =
|
||||
Mutex::new(HashMap::new());
|
||||
}
|
||||
|
||||
|
@ -31,6 +33,17 @@ impl<T> OptionalIpcSender<T> where T: Deserialize + Serialize + Send + Any {
|
|||
OptionalIpcSender::InProcess(ref sender) => sender.send(value).map_err(|_| ()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_opaque(self) -> OptionalOpaqueIpcSender {
|
||||
match self {
|
||||
OptionalIpcSender::OutOfProcess(ipc_sender) => {
|
||||
OptionalOpaqueIpcSender::OutOfProcess(ipc_sender.to_opaque())
|
||||
}
|
||||
OptionalIpcSender::InProcess(sender) => {
|
||||
OptionalOpaqueIpcSender::InProcess(OpaqueSender::new(sender))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for OptionalIpcSender<T> where T: Deserialize + Serialize + Send + Any {
|
||||
|
@ -49,18 +62,13 @@ impl<T> Clone for OptionalIpcSender<T> where T: Deserialize + Serialize + Send +
|
|||
impl<T> Deserialize for OptionalIpcSender<T> where T: Deserialize + Serialize + Send + Any {
|
||||
fn deserialize<D>(deserializer: &mut D)
|
||||
-> Result<OptionalIpcSender<T>, D::Error> where D: Deserializer {
|
||||
if opts::get().multiprocess {
|
||||
if opts::multiprocess() {
|
||||
return Ok(OptionalIpcSender::OutOfProcess(try!(Deserialize::deserialize(
|
||||
deserializer))))
|
||||
}
|
||||
let id: usize = try!(Deserialize::deserialize(deserializer));
|
||||
let sender = (*IN_PROCESS_SENDERS.lock()
|
||||
.unwrap()
|
||||
.remove(&id)
|
||||
.unwrap()
|
||||
.downcast_ref::<Sender<T>>()
|
||||
.unwrap()).clone();
|
||||
Ok(OptionalIpcSender::InProcess(sender))
|
||||
let sender = IN_PROCESS_SENDERS.lock().unwrap().remove(&id).unwrap();
|
||||
Ok(OptionalIpcSender::InProcess(sender.to().unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,16 +80,91 @@ impl<T> Serialize for OptionalIpcSender<T> where T: Deserialize + Serialize + Se
|
|||
let id = NEXT_SENDER_ID.fetch_add(1, Ordering::SeqCst);
|
||||
IN_PROCESS_SENDERS.lock()
|
||||
.unwrap()
|
||||
.insert(id, Box::new((*sender).clone()) as Box<Any + Send>);
|
||||
.insert(id, OpaqueSender::new((*sender).clone()));
|
||||
id.serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum OptionalOpaqueIpcSender {
|
||||
OutOfProcess(OpaqueIpcSender),
|
||||
InProcess(OpaqueSender),
|
||||
}
|
||||
|
||||
impl OptionalOpaqueIpcSender {
|
||||
pub fn to<T>(self) -> OptionalIpcSender<T>
|
||||
where T: Deserialize + Serialize + Send + Any + 'static {
|
||||
match self {
|
||||
OptionalOpaqueIpcSender::OutOfProcess(ipc_sender) => {
|
||||
OptionalIpcSender::OutOfProcess(ipc_sender.to())
|
||||
}
|
||||
OptionalOpaqueIpcSender::InProcess(sender) => {
|
||||
OptionalIpcSender::InProcess(sender.to().unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deserialize for OptionalOpaqueIpcSender {
|
||||
fn deserialize<D>(deserializer: &mut D)
|
||||
-> Result<OptionalOpaqueIpcSender, D::Error> where D: Deserializer {
|
||||
if opts::multiprocess() {
|
||||
return Ok(OptionalOpaqueIpcSender::OutOfProcess(try!(Deserialize::deserialize(
|
||||
deserializer))))
|
||||
}
|
||||
let id: usize = try!(Deserialize::deserialize(deserializer));
|
||||
let sender = IN_PROCESS_SENDERS.lock().unwrap().remove(&id).unwrap();
|
||||
Ok(OptionalOpaqueIpcSender::InProcess(sender))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for OptionalOpaqueIpcSender {
|
||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
|
||||
match *self {
|
||||
OptionalOpaqueIpcSender::OutOfProcess(ref ipc_sender) => {
|
||||
ipc_sender.serialize(serializer)
|
||||
}
|
||||
OptionalOpaqueIpcSender::InProcess(ref sender) => {
|
||||
let id = NEXT_SENDER_ID.fetch_add(1, Ordering::SeqCst);
|
||||
IN_PROCESS_SENDERS.lock().unwrap().insert(id, (*sender).clone());
|
||||
id.serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OpaqueSender {
|
||||
sender: Sender<()>,
|
||||
id: TypeId,
|
||||
}
|
||||
|
||||
impl OpaqueSender {
|
||||
fn new<T>(sender: Sender<T>) -> OpaqueSender where T: 'static + Reflect + Send {
|
||||
unsafe {
|
||||
OpaqueSender {
|
||||
sender: mem::transmute::<_, Sender<()>>(sender),
|
||||
id: TypeId::of::<T>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to<T>(self) -> Option<Sender<T>> where T: 'static + Reflect + Send {
|
||||
unsafe {
|
||||
if self.id != TypeId::of::<T>() {
|
||||
None
|
||||
} else {
|
||||
Some(mem::transmute::<_, Sender<T>>(self.sender))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn optional_ipc_channel<T>() -> (OptionalIpcSender<T>, Receiver<T>)
|
||||
where T: Deserialize + Serialize + Send + Any {
|
||||
if opts::get().multiprocess {
|
||||
if opts::multiprocess() {
|
||||
let (ipc_sender, ipc_receiver) = ipc::channel().unwrap();
|
||||
let receiver = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_receiver);
|
||||
(OptionalIpcSender::OutOfProcess(ipc_sender), receiver)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#![feature(optin_builtin_traits)]
|
||||
#![cfg_attr(not(target_os = "android"), feature(path_ext))]
|
||||
#![feature(plugin)]
|
||||
#![feature(reflect_marker)]
|
||||
#![feature(slice_splits)]
|
||||
#![feature(step_by)]
|
||||
#![feature(step_trait)]
|
||||
|
|
|
@ -18,10 +18,11 @@ use std::fs::File;
|
|||
use std::io::{self, Read, Write};
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
|
||||
use url::{self, Url};
|
||||
|
||||
/// Global flags for Servo, currently set on the command line.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct Opts {
|
||||
pub is_running_problem_test: bool,
|
||||
|
||||
|
@ -143,9 +144,12 @@ pub struct Opts {
|
|||
/// An optional string allowing the user agent to be set for testing.
|
||||
pub user_agent: String,
|
||||
|
||||
/// Whether to run in multiprocess mode.
|
||||
/// Whether we're running in multiprocess mode.
|
||||
pub multiprocess: bool,
|
||||
|
||||
/// Whether we're running inside the sandbox.
|
||||
pub sandbox: bool,
|
||||
|
||||
/// Dumps the flow tree after a layout.
|
||||
pub dump_flow_tree: bool,
|
||||
|
||||
|
@ -375,6 +379,13 @@ static FORCE_CPU_PAINTING: bool = true;
|
|||
#[cfg(not(target_os = "android"))]
|
||||
static FORCE_CPU_PAINTING: bool = false;
|
||||
|
||||
static MULTIPROCESS: AtomicBool = ATOMIC_BOOL_INIT;
|
||||
|
||||
#[inline]
|
||||
pub fn multiprocess() -> bool {
|
||||
MULTIPROCESS.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
enum UserAgent {
|
||||
Desktop,
|
||||
Android,
|
||||
|
@ -460,6 +471,7 @@ pub fn default_opts() -> Opts {
|
|||
initial_window_size: Size2D::typed(800, 600),
|
||||
user_agent: default_user_agent_string(DEFAULT_USER_AGENT),
|
||||
multiprocess: false,
|
||||
sandbox: false,
|
||||
dump_flow_tree: false,
|
||||
dump_display_list: false,
|
||||
dump_display_list_json: false,
|
||||
|
@ -479,7 +491,7 @@ pub fn default_opts() -> Opts {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_cmdline_args(args: &[String]) {
|
||||
pub fn from_cmdline_args(args: &[String]) -> ArgumentParsingResult {
|
||||
let (app_name, args) = args.split_first().unwrap();
|
||||
|
||||
let mut opts = Options::new();
|
||||
|
@ -509,11 +521,14 @@ pub fn from_cmdline_args(args: &[String]) {
|
|||
"Set custom user agent string (or android / gonk / desktop for platform default)",
|
||||
"NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)");
|
||||
opts.optflag("M", "multiprocess", "Run in multiprocess mode");
|
||||
opts.optflag("S", "sandbox", "Run in a sandbox if multiprocess");
|
||||
opts.optopt("Z", "debug",
|
||||
"A comma-separated string of debug options. Pass help to show available options.", "");
|
||||
opts.optflag("h", "help", "Print this message");
|
||||
opts.optopt("", "resources-path", "Path to find static resources", "/home/servo/resources");
|
||||
opts.optflag("", "sniff-mime-types" , "Enable MIME sniffing");
|
||||
opts.optopt("", "content-process" , "Run as a content process and connect to the given pipe",
|
||||
"servo-ipc-channel.abcdefg");
|
||||
opts.optmulti("", "pref",
|
||||
"A preference to set to enable", "dom.mozbrowser.enabled");
|
||||
opts.optflag("b", "no-native-titlebar", "Do not use native titlebar");
|
||||
|
@ -530,6 +545,13 @@ pub fn from_cmdline_args(args: &[String]) {
|
|||
process::exit(0);
|
||||
};
|
||||
|
||||
// If this is the content process, we'll receive the real options over IPC. So just fill in
|
||||
// some dummy options for now.
|
||||
if let Some(content_process) = opt_match.opt_str("content-process") {
|
||||
MULTIPROCESS.store(true, Ordering::SeqCst);
|
||||
return ArgumentParsingResult::ContentProcess(content_process);
|
||||
}
|
||||
|
||||
let debug_string = match opt_match.opt_str("Z") {
|
||||
Some(string) => string,
|
||||
None => String::new()
|
||||
|
@ -627,6 +649,10 @@ pub fn from_cmdline_args(args: &[String]) {
|
|||
}
|
||||
};
|
||||
|
||||
if opt_match.opt_present("M") {
|
||||
MULTIPROCESS.store(true, Ordering::SeqCst)
|
||||
}
|
||||
|
||||
let user_agent = match opt_match.opt_str("u") {
|
||||
Some(ref ua) if ua == "android" => default_user_agent_string(UserAgent::Android),
|
||||
Some(ref ua) if ua == "gonk" => default_user_agent_string(UserAgent::Gonk),
|
||||
|
@ -675,6 +701,7 @@ pub fn from_cmdline_args(args: &[String]) {
|
|||
initial_window_size: initial_window_size,
|
||||
user_agent: user_agent,
|
||||
multiprocess: opt_match.opt_present("M"),
|
||||
sandbox: opt_match.opt_present("S"),
|
||||
show_debug_borders: debug_options.show_compositor_borders,
|
||||
show_debug_fragment_borders: debug_options.show_fragment_borders,
|
||||
show_debug_parallel_paint: debug_options.show_parallel_paint,
|
||||
|
@ -704,6 +731,22 @@ pub fn from_cmdline_args(args: &[String]) {
|
|||
for pref in opt_match.opt_strs("pref").iter() {
|
||||
prefs::set_pref(pref, PrefValue::Boolean(true));
|
||||
}
|
||||
|
||||
ArgumentParsingResult::ChromeProcess
|
||||
}
|
||||
|
||||
pub enum ArgumentParsingResult {
|
||||
ChromeProcess,
|
||||
ContentProcess(String),
|
||||
}
|
||||
|
||||
static EXPERIMENTAL_ENABLED: AtomicBool = ATOMIC_BOOL_INIT;
|
||||
|
||||
/// Turn on experimental features globally. Normally this is done
|
||||
/// during initialization by `set` or `from_cmdline_args`, but
|
||||
/// tests that require experimental features will also set it.
|
||||
pub fn set_experimental_enabled(new_value: bool) {
|
||||
EXPERIMENTAL_ENABLED.store(new_value, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
// Make Opts available globally. This saves having to clone and pass
|
||||
|
|
|
@ -558,7 +558,7 @@ pub fn parse_legacy_color(mut input: &str) -> Result<RGBA, ()> {
|
|||
}
|
||||
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize, Serialize)]
|
||||
pub struct LowercaseString {
|
||||
inner: String,
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use serde::Serialize;
|
||||
use std::borrow::ToOwned;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::thread;
|
||||
|
@ -15,15 +17,36 @@ pub fn spawn_named<F>(name: String, f: F)
|
|||
builder.spawn(f).unwrap();
|
||||
}
|
||||
|
||||
/// An abstraction over `Sender<T>` and `IpcSender<T>`, for use in
|
||||
/// `spawn_named_with_send_on_failure`.
|
||||
pub trait SendOnFailure {
|
||||
type Value;
|
||||
fn send_on_failure(&mut self, value: Self::Value);
|
||||
}
|
||||
|
||||
impl<T> SendOnFailure for Sender<T> where T: Send + 'static {
|
||||
type Value = T;
|
||||
fn send_on_failure(&mut self, value: T) {
|
||||
self.send(value).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SendOnFailure for IpcSender<T> where T: Send + Serialize + 'static {
|
||||
type Value = T;
|
||||
fn send_on_failure(&mut self, value: T) {
|
||||
self.send(value).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Arrange to send a particular message to a channel if the task fails.
|
||||
pub fn spawn_named_with_send_on_failure<F, T>(name: String,
|
||||
state: task_state::TaskState,
|
||||
f: F,
|
||||
msg: T,
|
||||
dest: Sender<T>)
|
||||
where F: FnOnce() + Send + 'static,
|
||||
T: Send + 'static
|
||||
{
|
||||
pub fn spawn_named_with_send_on_failure<F, T, S>(name: String,
|
||||
state: task_state::TaskState,
|
||||
f: F,
|
||||
msg: T,
|
||||
mut dest: S)
|
||||
where F: FnOnce() + Send + 'static,
|
||||
T: Send + 'static,
|
||||
S: Send + SendOnFailure<Value=T> + 'static {
|
||||
let future_handle = thread::Builder::new().name(name.to_owned()).spawn(move || {
|
||||
task_state::initialize(state);
|
||||
f()
|
||||
|
@ -35,8 +58,9 @@ pub fn spawn_named_with_send_on_failure<F, T>(name: String,
|
|||
Ok(()) => (),
|
||||
Err(..) => {
|
||||
debug!("{} failed, notifying constellation", name);
|
||||
dest.send(msg).unwrap();
|
||||
dest.send_on_failure(msg);
|
||||
}
|
||||
}
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue