From e35038ed79f31ed31050dbb4e16b8714014a63a4 Mon Sep 17 00:00:00 2001 From: David Kellum Date: Fri, 24 Jan 2020 10:02:19 -0800 Subject: [PATCH] rt: add feature flag for using `parking_lot` internally (#2164) `parking_lot` provides synchronization primitives that tend to be more efficient than the ones in `std`. However, depending on `parking_lot` pulls in a number of dependencies resulting in additional compilation time. Adding *optional* support for `parking_lot` allows the end user to opt-in when the trade offs make sense for their case. --- tokio/Cargo.toml | 5 +- tokio/src/lib.rs | 24 +++++--- tokio/src/loom/std/mod.rs | 21 ++++++- tokio/src/loom/std/sync/pl_wrappers.rs | 79 ++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 11 deletions(-) create mode 100644 tokio/src/loom/std/sync/pl_wrappers.rs diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index d701e5116..9cf41355c 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -90,7 +90,6 @@ time = ["slab"] udp = ["io-driver"] uds = ["io-driver", "mio-uds", "libc"] - [dependencies] tokio-macros = { version = "0.2.3", path = "../tokio-macros", optional = true } @@ -105,8 +104,8 @@ memchr = { version = "2.2", optional = true } mio = { version = "0.6.20", optional = true } iovec = { version = "0.1.4", optional = true } num_cpus = { version = "1.8.0", optional = true } -# Backs `DelayQueue` -slab = { version = "0.4.1", optional = true } +parking_lot = { version = "0.10.0", optional = true } # Not in full +slab = { version = "0.4.1", optional = true } # Backs `DelayQueue` [target.'cfg(unix)'.dependencies] mio-uds = { version = "0.6.5", optional = true } diff --git a/tokio/src/lib.rs b/tokio/src/lib.rs index 7989d5b34..6b67b4ce4 100644 --- a/tokio/src/lib.rs +++ b/tokio/src/lib.rs @@ -66,13 +66,13 @@ //! is possible to just enable certain features over others. By default, Tokio //! does not enable any features but allows one to enable a subset for their use //! case. Below is a list of the available feature flags. You may also notice -//! above each function, struct and trait there is a set of feature flags -//! that are required for that item to be enabled. If you are new to Tokio it is -//! recommended that you use the `full` feature flag which will enable everything. +//! above each function, struct and trait there is listed one or more feature flags +//! that are required for that item to be used. If you are new to Tokio it is +//! recommended that you use the `full` feature flag which will enable all public APIs. //! Beware though that this will pull in many extra dependencies that you may not //! need. //! -//! - `full`: Enables all Tokio features and every API will be available. +//! - `full`: Enables all Tokio public API features listed below. //! - `rt-core`: Enables `tokio::spawn` and the basic (single-threaded) scheduler. //! - `rt-threaded`: Enables the heavier, multi-threaded, work-stealing scheduler. //! - `rt-util`: Enables non-scheduler utilities. @@ -95,15 +95,25 @@ //! - `test-util`: Enables testing based infrastructure for the Tokio runtime. //! - `blocking`: Enables `block_in_place` and `spawn_blocking`. //! -//! _Note: `AsyncRead` and `AsyncWrite` do not require any features and are -//! enabled by default._ +//! _Note: `AsyncRead` and `AsyncWrite` traits do not require any features and are +//! always available._ +//! +//! ### Internal features +//! +//! These features do not expose any new API, but influence internal +//! implementation aspects of Tokio, and can pull in additional +//! dependencies. They are not included in `full`: +//! +//! - `parking_lot`: As a potential optimization, use the _parking_lot_ crate's +//! synchronization primitives internally. MSRV may increase according to the +//! _parking_lot_ release in use. //! //! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section //! //! ### Authoring applications //! //! Tokio is great for writing applications and most users in this case shouldn't -//! worry to much about what features they should pick. If you're unsure, we suggest +//! worry too much about what features they should pick. If you're unsure, we suggest //! going with `full` to ensure that you don't run into any road blocks while you're //! building your application. //! diff --git a/tokio/src/loom/std/mod.rs b/tokio/src/loom/std/mod.rs index e6faa3b16..d5e057e51 100644 --- a/tokio/src/loom/std/mod.rs +++ b/tokio/src/loom/std/mod.rs @@ -38,7 +38,26 @@ pub(crate) mod rand { } pub(crate) mod sync { - pub(crate) use std::sync::*; + pub(crate) use std::sync::Arc; + + #[cfg(feature = "parking_lot")] + mod pl_wrappers; + + // Below, make sure all the feature-influenced types are exported for + // internal use. Note however that some are not _currently_ named by + // consuming code. + + #[cfg(feature = "parking_lot")] + #[allow(unused_imports)] + pub(crate) use pl_wrappers::{Condvar, Mutex}; + + #[cfg(feature = "parking_lot")] + #[allow(unused_imports)] + pub(crate) use parking_lot::{MutexGuard, WaitTimeoutResult}; + + #[cfg(not(feature = "parking_lot"))] + #[allow(unused_imports)] + pub(crate) use std::sync::{Condvar, Mutex, MutexGuard, WaitTimeoutResult}; pub(crate) mod atomic { pub(crate) use crate::loom::std::atomic_u32::AtomicU32; diff --git a/tokio/src/loom/std/sync/pl_wrappers.rs b/tokio/src/loom/std/sync/pl_wrappers.rs new file mode 100644 index 000000000..3be8ba1c1 --- /dev/null +++ b/tokio/src/loom/std/sync/pl_wrappers.rs @@ -0,0 +1,79 @@ +//! A minimal adaption of the `parking_lot` synchronization primitives to the +//! equivalent `std::sync` types. +//! +//! This can be extended to additional types/methods as required. + +use std::sync::{LockResult, TryLockError, TryLockResult}; +use std::time::Duration; + +use parking_lot as pl; + +/// Adapter for `parking_lot::Mutex` to the `std::sync::Mutex` interface. +#[derive(Debug)] +pub(crate) struct Mutex(pl::Mutex); + +impl Mutex { + #[inline] + pub(crate) fn new(t: T) -> Mutex { + Mutex(pl::Mutex::new(t)) + } + + #[inline] + pub(crate) fn lock(&self) -> LockResult> { + Ok(self.0.lock()) + } + + #[inline] + pub(crate) fn try_lock(&self) -> TryLockResult> { + match self.0.try_lock() { + Some(guard) => Ok(guard), + None => Err(TryLockError::WouldBlock), + } + } + + // Note: Additional methods `is_poisoned` and `into_inner`, can be + // provided here as needed. +} + +/// Adapter for `parking_lot::Condvar` to the `std::sync::Condvar` interface. +#[derive(Debug)] +pub(crate) struct Condvar(pl::Condvar); + +impl Condvar { + #[inline] + pub(crate) fn new() -> Condvar { + Condvar(pl::Condvar::new()) + } + + #[inline] + pub(crate) fn notify_one(&self) { + self.0.notify_one(); + } + + #[inline] + pub(crate) fn notify_all(&self) { + self.0.notify_all(); + } + + #[inline] + pub(crate) fn wait<'a, T>( + &self, + mut guard: pl::MutexGuard<'a, T>, + ) -> LockResult> { + self.0.wait(&mut guard); + Ok(guard) + } + + #[inline] + pub(crate) fn wait_timeout<'a, T>( + &self, + mut guard: pl::MutexGuard<'a, T>, + timeout: Duration, + ) -> LockResult<(pl::MutexGuard<'a, T>, pl::WaitTimeoutResult)> { + let wtr = self.0.wait_for(&mut guard, timeout); + Ok((guard, wtr)) + } + + // Note: Additional methods `wait_timeout_ms`, `wait_timeout_until`, + // `wait_until` can be provided here as needed. +}