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.
This commit is contained in:
David Kellum 2020-01-24 10:02:19 -08:00 committed by Carl Lerche
parent f9ddb93604
commit e35038ed79
4 changed files with 118 additions and 11 deletions

View File

@ -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 }

View File

@ -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.
//!

View File

@ -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;

View File

@ -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<T: ?Sized>(pl::Mutex<T>);
impl<T> Mutex<T> {
#[inline]
pub(crate) fn new(t: T) -> Mutex<T> {
Mutex(pl::Mutex::new(t))
}
#[inline]
pub(crate) fn lock(&self) -> LockResult<pl::MutexGuard<'_, T>> {
Ok(self.0.lock())
}
#[inline]
pub(crate) fn try_lock(&self) -> TryLockResult<pl::MutexGuard<'_, T>> {
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<pl::MutexGuard<'a, T>> {
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.
}