diff --git a/Cargo.lock b/Cargo.lock index c42885fed85..af695a18963 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -613,10 +613,10 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -648,7 +648,7 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1658,6 +1658,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1921,6 +1922,7 @@ dependencies = [ "canvas_traits 0.0.1", "compositing 0.0.1", "constellation 0.0.1", + "crossbeam-channel 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "debugger 0.0.1", "devtools 0.0.1", "devtools_traits 0.0.1", @@ -1941,6 +1943,7 @@ dependencies = [ "script 0.0.1", "script_layout_interface 0.0.1", "script_traits 0.0.1", + "servo_channel 0.0.1", "servo_config 0.0.1", "servo_geometry 0.0.1", "servo_url 0.0.1", @@ -3211,6 +3214,8 @@ name = "servo" version = "0.0.1" dependencies = [ "backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "gleam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3361,6 +3366,15 @@ dependencies = [ "string_cache_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "servo_channel" +version = "0.0.1" +dependencies = [ + "crossbeam-channel 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ipc-channel 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "servo_config" version = "0.0.1" @@ -4362,10 +4376,10 @@ dependencies = [ "checksum core-graphics 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92801c908ea6301ae619ed842a72e01098085fc321b9c2f3f833dad555bba055" "checksum core-text 11.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "157ff38a92496dc676ce36d9124554e9ac66f1c1039f952690ac64f71cfa5968" "checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" -"checksum crossbeam-channel 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6c0a94250b0278d7fc5a894c3d276b11ea164edc8bf8feb10ca1ea517b44a649" +"checksum crossbeam-channel 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a5716fadb87a5633db34c5e83ee6e036e6edc229f8a6bfb7c7c84ed340ba95df" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" -"checksum crossbeam-epoch 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30fecfcac6abfef8771151f8be4abc9e4edc112c2bcb233314cafde2680536e9" +"checksum crossbeam-epoch 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c90f1474584f38e270b5b613e898c8c328aa4f3dea85e0a27ac2e642f009416" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" "checksum cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)" = "495beddc39b1987b8e9f029354eccbd5ef88eb5f1cd24badb764dce338acf2e0" diff --git a/components/channel/Cargo.toml b/components/channel/Cargo.toml new file mode 100644 index 00000000000..0eb5655eaaa --- /dev/null +++ b/components/channel/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "servo_channel" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +publish = false + +[lib] +name = "servo_channel" +path = "lib.rs" +test = false +doctest = false + +[dependencies] +crossbeam-channel = "0.2.5" +ipc-channel = "0.11" +serde = "1.0" + +[[test]] +name = "main" +path = "tests/disconnect.rs" diff --git a/components/channel/lib.rs b/components/channel/lib.rs new file mode 100644 index 00000000000..87950fbe22a --- /dev/null +++ b/components/channel/lib.rs @@ -0,0 +1,163 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +extern crate crossbeam_channel; +extern crate ipc_channel; +extern crate serde; + +pub mod base_channel { + pub use crossbeam_channel::*; +} +// Needed to re-export the select macro. +pub use crossbeam_channel::*; + +use ipc_channel::ipc::IpcReceiver; +use ipc_channel::router::ROUTER; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; + + +pub fn route_ipc_receiver_to_new_servo_receiver(ipc_receiver: IpcReceiver) -> Receiver +where + T: for<'de> Deserialize<'de> + Serialize + Send + 'static +{ + let (servo_sender, servo_receiver) = channel(); + ROUTER.add_route( + ipc_receiver.to_opaque(), + Box::new(move |message| { + drop(servo_sender.send(message.to::().unwrap())) + }), + ); + servo_receiver +} + +pub fn route_ipc_receiver_to_new_servo_sender(ipc_receiver: IpcReceiver, servo_sender: Sender) +where + T: for<'de> Deserialize<'de> + Serialize + Send + 'static +{ + ROUTER.add_route( + ipc_receiver.to_opaque(), + Box::new(move |message| { + drop(servo_sender.send(message.to::().unwrap())) + }), + ) +} + +pub fn channel() -> (Sender, Receiver) { + let (base_sender, base_receiver) = crossbeam_channel::unbounded::(); + let is_disconnected = Arc::new(AtomicBool::new(false)); + (Sender::new(base_sender, is_disconnected.clone()), + Receiver::new(base_receiver, is_disconnected)) +} + +#[derive(Debug, PartialEq)] +pub enum ChannelError { + ChannelClosedError +} + +pub struct Receiver { + receiver: crossbeam_channel::Receiver, + is_disconnected: Arc, +} + +impl Drop for Receiver { + fn drop(&mut self) { + self.is_disconnected.store(true, Ordering::SeqCst); + } +} + +impl Clone for Receiver { + fn clone(&self) -> Self { + Receiver { + receiver: self.receiver.clone(), + is_disconnected: self.is_disconnected.clone(), + } + } +} + +impl Receiver { + pub fn new(receiver: crossbeam_channel::Receiver, is_disconnected: Arc) -> Receiver { + Receiver { + receiver, + is_disconnected, + } + } + + pub fn recv(&self) -> Option { + self.receiver.recv() + } + + pub fn try_recv(&self) -> Option { + self.receiver.try_recv() + } + + pub fn len(&self) -> usize { + self.receiver.len() + } + + pub fn select(&self) -> &crossbeam_channel::Receiver { + &self.receiver + } +} + +impl Iterator for Receiver { + type Item = T; + + fn next(&mut self) -> Option { + self.receiver.recv() + } +} + +impl<'a, T> IntoIterator for &'a Receiver { + type Item = T; + type IntoIter = crossbeam_channel::Receiver; + + fn into_iter(self) -> Self::IntoIter { + self.receiver.clone() + } +} + +pub struct Sender { + sender: crossbeam_channel::Sender, + is_disconnected: Arc, +} + +impl Clone for Sender { + fn clone(&self) -> Self { + Sender { + sender: self.sender.clone(), + is_disconnected: self.is_disconnected.clone(), + } + } +} + +impl Sender { + pub fn new(sender: crossbeam_channel::Sender, is_disconnected: Arc) -> Sender { + Sender { + sender, + is_disconnected, + } + } + + pub fn send(&self, msg: T) -> Result<(), ChannelError> { + if self.is_disconnected.load(Ordering::SeqCst) { + Err(ChannelError::ChannelClosedError) + } else { + Ok(self.sender.send(msg)) + } + } + + pub fn len(&self) -> usize { + self.sender.len() + } + + pub fn select(&self) -> Option<&crossbeam_channel::Sender> { + if self.is_disconnected.load(Ordering::SeqCst) { + None + } else { + Some(&self.sender) + } + } +} diff --git a/components/channel/tests/disconnect.rs b/components/channel/tests/disconnect.rs new file mode 100644 index 00000000000..1ba87287cfa --- /dev/null +++ b/components/channel/tests/disconnect.rs @@ -0,0 +1,31 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +#[macro_use] +extern crate servo_channel; + +use servo_channel::{channel, ChannelError}; + +#[test] +fn send_after_receiver_dropped() { + let (sender, receiver) = channel(); + drop(receiver); + assert_eq!(sender.send(1), Err(ChannelError::ChannelClosedError)); + let sent = select! { + send(sender.select(), 1) => true, + default => false + }; + assert_eq!(sent, false); +} + +#[test] +fn send_with_receiver_connected() { + let (sender, _receiver) = channel(); + assert_eq!(sender.send(1), Ok(())); + let sent = select! { + send(sender.select(), 1) => true, + default => false + }; + assert_eq!(sent, true); +} diff --git a/components/constellation/lib.rs b/components/constellation/lib.rs index 1af7a3bfc4a..ab6b8722236 100644 --- a/components/constellation/lib.rs +++ b/components/constellation/lib.rs @@ -34,6 +34,8 @@ extern crate profile_traits; extern crate script_traits; #[macro_use] extern crate serde; +#[macro_use] +extern crate servo_channel; extern crate servo_config; extern crate servo_rand; extern crate servo_remutex; diff --git a/components/constellation/timer_scheduler.rs b/components/constellation/timer_scheduler.rs index 989ea77e0af..8872204374e 100644 --- a/components/constellation/timer_scheduler.rs +++ b/components/constellation/timer_scheduler.rs @@ -6,8 +6,7 @@ use ipc_channel::ipc::{self, IpcSender}; use script_traits::{TimerEvent, TimerEventRequest, TimerSchedulerMsg}; use std::cmp::{self, Ord}; use std::collections::BinaryHeap; -use std::sync::mpsc; -use std::sync::mpsc::TryRecvError::{Disconnected, Empty}; +use servo_channel::base_channel; use std::thread; use std::time::{Duration, Instant}; @@ -40,7 +39,7 @@ impl PartialEq for ScheduledEvent { impl TimerScheduler { pub fn start() -> IpcSender { let (req_ipc_sender, req_ipc_receiver) = ipc::channel().expect("Channel creation failed."); - let (req_sender, req_receiver) = mpsc::sync_channel(1); + let (req_sender, req_receiver) = base_channel::bounded(1); // We could do this much more directly with recv_timeout // (https://github.com/rust-lang/rfcs/issues/962). diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index aa520215cd2..a193cab566f 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -44,6 +44,8 @@ extern crate serde_json; extern crate servo_allocator; extern crate servo_arc; extern crate servo_atoms; +#[macro_use] +extern crate servo_channel; extern crate servo_config; extern crate servo_geometry; extern crate servo_url; diff --git a/components/script/dom/paintworkletglobalscope.rs b/components/script/dom/paintworkletglobalscope.rs index 4900c2dd96d..bf07610719f 100644 --- a/components/script/dom/paintworkletglobalscope.rs +++ b/components/script/dom/paintworkletglobalscope.rs @@ -49,6 +49,8 @@ use profile_traits::ipc; use script_traits::{DrawAPaintImageResult, PaintWorkletError}; use script_traits::Painter; use servo_atoms::Atom; +use servo_channel::{channel, Sender}; +use servo_channel::base_channel; use servo_config::prefs::PREFS; use servo_url::ServoUrl; use std::cell::Cell; @@ -364,9 +366,12 @@ impl PaintWorkletGlobalScope { .as_u64() .unwrap_or(10u64); - let timeout_duration = Duration::from_millis(timeout); - receiver.recv_timeout(timeout_duration) - .map_err(|e| PaintWorkletError::from(e)) + select! { + recv(base_channel::after(Duration::from_millis(timeout))) => { + Err(PaintWorkletError::Timeout) + } + recv(receiver.select(), msg) => msg.ok_or(PaintWorkletError::Timeout) + } } } Box::new(WorkletPainter { diff --git a/components/script/lib.rs b/components/script/lib.rs index 416eaec06f7..6f4ec73272a 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -84,6 +84,7 @@ extern crate serde_bytes; extern crate servo_allocator; extern crate servo_arc; #[macro_use] extern crate servo_atoms; +#[macro_use] extern crate servo_channel; extern crate servo_config; extern crate servo_geometry; extern crate servo_media; diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index 34a75fa4292..816025e1071 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -51,6 +51,7 @@ profile_traits = {path = "../profile_traits"} script = {path = "../script"} script_layout_interface = {path = "../script_layout_interface"} script_traits = {path = "../script_traits"} +servo_channel = {path = "../channel"} servo_config = {path = "../config"} servo_geometry = {path = "../geometry"} servo_url = {path = "../url"} diff --git a/components/servo/lib.rs b/components/servo/lib.rs index f6edcc45bde..e5fc7107322 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -28,6 +28,7 @@ pub extern crate bluetooth; pub extern crate bluetooth_traits; pub extern crate canvas; pub extern crate canvas_traits; +pub extern crate servo_channel; pub extern crate compositing; pub extern crate constellation; pub extern crate debugger; diff --git a/python/servo/testing_commands.py b/python/servo/testing_commands.py index 1cfddcc1ac7..d3d12361cc8 100644 --- a/python/servo/testing_commands.py +++ b/python/servo/testing_commands.py @@ -253,6 +253,7 @@ class MachCommands(CommandBase): "selectors", "servo_config", "servo_remutex", + "servo_channel", ] if not packages: packages = set(os.listdir(path.join(self.context.topdir, "tests", "unit"))) - set(['.DS_Store'])