Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't inherit Send from parking_lot::*Guard #4359

Merged
merged 6 commits into from Dec 31, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Expand Up @@ -20,6 +20,7 @@ jobs:
needs:
- test
- test-unstable
- test-parking_lot
- miri
- cross
- features
Expand Down Expand Up @@ -77,6 +78,27 @@ jobs:
# bench.yml workflow runs benchmarks only on linux.
if: startsWith(matrix.os, 'ubuntu')

test-parking_lot:
# The parking_lot crate has a feature called send_guard which changes when
# some of its types are Send. Tokio has some measures in place to prevent
# this from affecting when Tokio types are Send, and this test exists to
# ensure that those measures are working.
#
# This relies on the potentially affected Tokio type being listed in
# `tokio/tokio/tests/async_send_sync.rs`.
name: compile tests with parking lot send guards
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust
run: rustup update stable
- uses: Swatinem/rust-cache@v1
- name: Enable parking_lot send_guard feature
# Inserts the line "plsend = ["parking_lot/send_guard"]" right after [features]
run: sed -i '/\[features\]/a plsend = ["parking_lot/send_guard"]' tokio/Cargo.toml
- name: Compile tests with all features enabled
run: cargo build --workspace --all-features --tests

valgrind:
name: valgrind
runs-on: ubuntu-latest
Expand Down
116 changes: 97 additions & 19 deletions tokio/src/loom/std/parking_lot.rs
Expand Up @@ -3,91 +3,151 @@
//!
//! This can be extended to additional types/methods as required.

use std::fmt;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::sync::LockResult;
use std::time::Duration;

// All types in this file are marked with PhantomData to ensure that
// parking_lot's send_guard feature does not leak through and affect when Tokio
// types are Send.
//
// See <https://github.com/tokio-rs/tokio/pull/4359> for more info.

// Types that do not need wrapping
pub(crate) use parking_lot::{MutexGuard, RwLockReadGuard, RwLockWriteGuard, WaitTimeoutResult};
pub(crate) use parking_lot::WaitTimeoutResult;

#[derive(Debug)]
pub(crate) struct Mutex<T: ?Sized>(PhantomData<std::sync::Mutex<T>>, parking_lot::Mutex<T>);

#[derive(Debug)]
pub(crate) struct RwLock<T>(PhantomData<std::sync::RwLock<T>>, parking_lot::RwLock<T>);

#[derive(Debug)]
pub(crate) struct Condvar(PhantomData<std::sync::Condvar>, parking_lot::Condvar);

/// Adapter for `parking_lot::Mutex` to the `std::sync::Mutex` interface.
#[derive(Debug)]
pub(crate) struct Mutex<T: ?Sized>(parking_lot::Mutex<T>);
pub(crate) struct MutexGuard<'a, T: ?Sized>(
PhantomData<std::sync::MutexGuard<'a, T>>,
hawkw marked this conversation as resolved.
Show resolved Hide resolved
parking_lot::MutexGuard<'a, T>,
);

#[derive(Debug)]
pub(crate) struct RwLock<T>(parking_lot::RwLock<T>);
pub(crate) struct RwLockReadGuard<'a, T: ?Sized>(
PhantomData<std::sync::RwLockReadGuard<'a, T>>,
parking_lot::RwLockReadGuard<'a, T>,
);

/// Adapter for `parking_lot::Condvar` to the `std::sync::Condvar` interface.
#[derive(Debug)]
pub(crate) struct Condvar(parking_lot::Condvar);
pub(crate) struct RwLockWriteGuard<'a, T: ?Sized>(
PhantomData<std::sync::RwLockWriteGuard<'a, T>>,
parking_lot::RwLockWriteGuard<'a, T>,
);

impl<T> Mutex<T> {
#[inline]
pub(crate) fn new(t: T) -> Mutex<T> {
Mutex(parking_lot::Mutex::new(t))
Mutex(PhantomData, parking_lot::Mutex::new(t))
}

#[inline]
#[cfg(all(feature = "parking_lot", not(all(loom, test)),))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "parking_lot",))))]
pub(crate) const fn const_new(t: T) -> Mutex<T> {
Mutex(parking_lot::const_mutex(t))
Mutex(PhantomData, parking_lot::const_mutex(t))
}

#[inline]
pub(crate) fn lock(&self) -> MutexGuard<'_, T> {
self.0.lock()
MutexGuard(PhantomData, self.1.lock())
}

#[inline]
pub(crate) fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
self.0.try_lock()
self.1
.try_lock()
.map(|guard| MutexGuard(PhantomData, guard))
}

#[inline]
pub(crate) fn get_mut(&mut self) -> &mut T {
self.0.get_mut()
self.1.get_mut()
}

// Note: Additional methods `is_poisoned` and `into_inner`, can be
// provided here as needed.
}

impl<'a, T: ?Sized> Deref for MutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.1.deref()
}
}

impl<'a, T: ?Sized> DerefMut for MutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut T {
self.1.deref_mut()
}
}

impl<T> RwLock<T> {
pub(crate) fn new(t: T) -> RwLock<T> {
RwLock(parking_lot::RwLock::new(t))
RwLock(PhantomData, parking_lot::RwLock::new(t))
}

pub(crate) fn read(&self) -> LockResult<RwLockReadGuard<'_, T>> {
Ok(self.0.read())
Ok(RwLockReadGuard(PhantomData, self.1.read()))
}

pub(crate) fn write(&self) -> LockResult<RwLockWriteGuard<'_, T>> {
Ok(self.0.write())
Ok(RwLockWriteGuard(PhantomData, self.1.write()))
}
}

impl<'a, T: ?Sized> Deref for RwLockReadGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.1.deref()
}
}

impl<'a, T: ?Sized> Deref for RwLockWriteGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.1.deref()
}
}

impl<'a, T: ?Sized> DerefMut for RwLockWriteGuard<'a, T> {
fn deref_mut(&mut self) -> &mut T {
self.1.deref_mut()
}
}

impl Condvar {
#[inline]
pub(crate) fn new() -> Condvar {
Condvar(parking_lot::Condvar::new())
Condvar(PhantomData, parking_lot::Condvar::new())
}

#[inline]
pub(crate) fn notify_one(&self) {
self.0.notify_one();
self.1.notify_one();
}

#[inline]
pub(crate) fn notify_all(&self) {
self.0.notify_all();
self.1.notify_all();
}

#[inline]
pub(crate) fn wait<'a, T>(
&self,
mut guard: MutexGuard<'a, T>,
) -> LockResult<MutexGuard<'a, T>> {
self.0.wait(&mut guard);
self.1.wait(&mut guard.1);
Ok(guard)
}

Expand All @@ -97,10 +157,28 @@ impl Condvar {
mut guard: MutexGuard<'a, T>,
timeout: Duration,
) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> {
let wtr = self.0.wait_for(&mut guard, timeout);
let wtr = self.1.wait_for(&mut guard.1, timeout);
Ok((guard, wtr))
}

// Note: Additional methods `wait_timeout_ms`, `wait_timeout_until`,
// `wait_until` can be provided here as needed.
}

impl<'a, T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.1, f)
}
}

impl<'a, T: ?Sized + fmt::Display> fmt::Display for RwLockReadGuard<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.1, f)
}
}

impl<'a, T: ?Sized + fmt::Display> fmt::Display for RwLockWriteGuard<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.1, f)
}
}