14 Commits

Author SHA1 Message Date
003f6a02d7 Prepare for release v0.3.2
This is a bugfix release and contains no new functionality.
2025-09-04 18:49:51 +02:00
d72827a74d Add missing fmt impls 2025-09-03 09:02:33 +02:00
df0f28b386 Relax sized bounds for std::sync wrappers 2025-09-03 09:02:33 +02:00
dependabot[bot]
8dec9db799 Bump actions/checkout from 4 to 5
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-18 12:58:16 +02:00
28d67e871c Make elided lifetimes explicit 2025-08-18 12:51:08 +02:00
90843416e3 Missing section header 2025-05-13 16:09:39 +02:00
522ee3ffe5 Prepare release v0.3.1 2025-05-13 16:04:47 +02:00
1791ef5243 Update changelog 2025-05-13 15:57:30 +02:00
4b872c1262 Remove deprecated Cargo.toml options 2025-05-13 15:57:30 +02:00
99bca9852c Opt in to edition 2024 formatting 2025-05-13 15:57:30 +02:00
b396016224 Remove bors config 2025-05-13 15:57:30 +02:00
c29ccc4f4d Avoid updating deps on older versions 2025-04-10 20:58:45 +02:00
8ec32bdf16 Reorganise features
Now the features do not directly enable each other, which should silence
extraneous deprecation warnings while running tests. This can be cleaned
up in the next BC break when the old features are removed.
2025-04-10 20:58:45 +02:00
ccc4c98791 Rename dependencies to their crate names
Keep the old names but deprecate them so we can remove them in 0.4.
2025-04-10 20:58:45 +02:00
13 changed files with 146 additions and 69 deletions

View File

@@ -21,7 +21,7 @@ jobs:
- nightly
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@v1
with:
@@ -30,30 +30,34 @@ jobs:
# Make sure we test with recent deps
- run: cargo update
# Note: some crates broke BC with 1.74 so we use the locked deps
if: "${{ matrix.rust != '1.74' }}"
- run: cargo build --all-features --all-targets
- run: cargo test --all-features
- run: cargo fmt --all -- --check
# Note: Rust 1.74 doesn't understand edition 2024 formatting so no point
if: "${{ matrix.rust != '1.74' }}"
- run: cargo clippy --all-features --all-targets -- -D warnings
msrv:
name: MSRV
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@v1
with:
toolchain: "1.70"
# Test everything except experimental features.
- run: cargo test --features backtraces,lockapi,parkinglot
- run: cargo test --features backtraces,lock_api,parking_lot
docs:
name: Documentation build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@v1
with:

View File

@@ -6,6 +6,16 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.3.2]
### Fixed
- `std::sync` wrappers now no longer incorrectly require `T: Sized`
- Added missing `Display` and `Debug` implementations to `RwLock(Read|Write)Guard`.
## [0.3.1]
### Added
- On Rust 1.80 or newer, a wrapper for `std::sync::LazyLock` is now available. The MSRV has not been
@@ -27,6 +37,16 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Reworked CI to better test continued support for the minimum supported Rust version
### Fixed
- Support for `parking_lot` and `lock_api` can now be enabled properly with the matching feature
names.
### Deprecated
- The `parkinglot` and `lockapi` features have been deprecated. They will be removed in version 0.4.
To fix it, you can use the `parking_lot` and `lock_api` features respectively.
## [0.3.0] - 2023-09-09
### Added
@@ -114,7 +134,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
Initial release.
[Unreleased]: https://github.com/bertptrs/tracing-mutex/compare/v0.3.0...HEAD
[Unreleased]: https://github.com/bertptrs/tracing-mutex/compare/v0.3.2...HEAD
[0.3.2]: https://github.com/bertptrs/tracing-mutex/compare/v0.3.1...v0.3.2
[0.3.1]: https://github.com/bertptrs/tracing-mutex/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/bertptrs/tracing-mutex/compare/v0.2.1...v0.3.0
[0.2.1]: https://github.com/bertptrs/tracing-mutex/compare/v0.2.0...v0.2.1
[0.2.0]: https://github.com/bertptrs/tracing-mutex/compare/v0.1.2...v0.2.0

2
Cargo.lock generated
View File

@@ -597,7 +597,7 @@ dependencies = [
[[package]]
name = "tracing-mutex"
version = "0.3.0"
version = "0.3.2"
dependencies = [
"autocfg",
"criterion",

View File

@@ -1,10 +1,8 @@
[package]
name = "tracing-mutex"
version = "0.3.0"
authors = ["Bert Peters <bert@bertptrs.nl>"]
version = "0.3.2"
edition = "2021"
license = "MIT OR Apache-2.0"
documentation = "https://docs.rs/tracing-mutex"
categories = ["concurrency", "development-tools::debugging"]
keywords = ["mutex", "rwlock", "once", "thread"]
description = "Ensure deadlock-free mutexes by allocating in order, or else."
@@ -38,9 +36,12 @@ required-features = ["parkinglot"]
default = ["backtraces"]
backtraces = []
experimental = []
# Feature names do not match crate names pending namespaced features.
lockapi = ["lock_api"]
parkinglot = ["parking_lot", "lockapi"]
lock_api = ["dep:lock_api"]
parking_lot = ["dep:parking_lot", "lock_api"]
# Deprecated feature names from when cargo couldn't distinguish between dep and feature
lockapi = ["dep:lock_api"]
parkinglot = ["dep:parking_lot", "lock_api"]
[build-dependencies]
autocfg = "1.4.0"

View File

@@ -1,11 +1,11 @@
use std::sync::Arc;
use std::sync::Mutex;
use criterion::criterion_group;
use criterion::criterion_main;
use criterion::BenchmarkId;
use criterion::Criterion;
use criterion::Throughput;
use criterion::criterion_group;
use criterion::criterion_main;
use rand::prelude::*;
use tracing_mutex::stdsync::tracing::Mutex as TracingMutex;

View File

@@ -1,6 +0,0 @@
status = [
'Rust project (1.70)',
'Rust project (stable)',
'Rust project (beta)',
'Documentation build',
]

1
rustfmt.toml Normal file
View File

@@ -0,0 +1 @@
style_edition="2024"

View File

@@ -1,7 +1,7 @@
use std::cell::Cell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::hash_map::Entry;
use std::hash::Hash;
type Order = usize;

View File

@@ -77,30 +77,40 @@ use std::fmt;
use std::marker::PhantomData;
use std::ops::Deref;
use std::ops::DerefMut;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Mutex;
use std::sync::MutexGuard;
use std::sync::OnceLock;
use std::sync::PoisonError;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
#[cfg(feature = "lockapi")]
#[cfg(feature = "lock_api")]
#[cfg_attr(docsrs, doc(cfg(feature = "lockapi")))]
#[deprecated = "The top-level re-export `lock_api` is deprecated. Use `tracing_mutex::lockapi::raw` instead"]
pub use lock_api;
#[cfg(feature = "parkinglot")]
#[cfg(feature = "parking_lot")]
#[cfg_attr(docsrs, doc(cfg(feature = "parkinglot")))]
#[deprecated = "The top-level re-export `parking_lot` is deprecated. Use `tracing_mutex::parkinglot::raw` instead"]
pub use parking_lot;
use graph::DiGraph;
use reporting::Dep;
use reporting::Reportable;
use crate::graph::DiGraph;
mod graph;
#[cfg(feature = "lockapi")]
#[cfg_attr(docsrs, doc(cfg(feature = "lockapi")))]
#[cfg(any(feature = "lock_api", feature = "lockapi"))]
#[cfg_attr(docsrs, doc(cfg(feature = "lock_api")))]
#[cfg_attr(
all(not(docsrs), feature = "lockapi", not(feature = "lock_api")),
deprecated = "The `lockapi` feature has been renamed `lock_api`"
)]
pub mod lockapi;
#[cfg(feature = "parkinglot")]
#[cfg_attr(docsrs, doc(cfg(feature = "parkinglot")))]
#[cfg(any(feature = "parking_lot", feature = "parkinglot"))]
#[cfg_attr(docsrs, doc(cfg(feature = "parking_lot")))]
#[cfg_attr(
all(not(docsrs), feature = "parkinglot", not(feature = "parking_lot")),
deprecated = "The `parkinglot` feature has been renamed `parking_lot`"
)]
pub mod parkinglot;
mod reporting;
pub mod stdsync;
@@ -153,7 +163,7 @@ impl MutexId {
/// # Panics
///
/// This method panics if the new dependency would introduce a cycle.
pub fn get_borrowed(&self) -> BorrowedMutex {
pub fn get_borrowed(&self) -> BorrowedMutex<'_> {
self.mark_held();
BorrowedMutex {
id: self,
@@ -341,9 +351,11 @@ mod tests {
drop(b);
// If b's destructor correctly ran correctly we can now add an edge from c to a.
assert!(get_dependency_graph()
.add_edge(c.value(), a.value(), Dep::capture)
.is_ok());
assert!(
get_dependency_graph()
.add_edge(c.value(), a.value(), Dep::capture)
.is_ok()
);
}
/// Test creating a cycle, then panicking.

View File

@@ -8,6 +8,7 @@
//! Wrapped mutexes are at least one `usize` larger than the types they wrapped, and must be aligned
//! to `usize` boundaries. As such, libraries with many mutexes may want to consider the additional
//! required memory.
pub use lock_api as raw;
use lock_api::GuardNoSend;
use lock_api::RawMutex;
use lock_api::RawMutexFair;
@@ -23,9 +24,9 @@ use lock_api::RawRwLockUpgradeDowngrade;
use lock_api::RawRwLockUpgradeFair;
use lock_api::RawRwLockUpgradeTimed;
use crate::util::PrivateTraced;
use crate::LazyMutexId;
use crate::MutexId;
use crate::util::PrivateTraced;
/// Tracing wrapper for all [`lock_api`] traits.
///

View File

@@ -49,18 +49,18 @@ pub mod tracing;
#[cfg(debug_assertions)]
pub use tracing::{
const_fair_mutex, const_mutex, const_reentrant_mutex, const_rwlock, FairMutex, FairMutexGuard,
MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard, MappedRwLockReadGuard,
MappedRwLockWriteGuard, Mutex, MutexGuard, Once, RawFairMutex, RawMutex, RawRwLock,
ReentrantMutex, ReentrantMutexGuard, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard,
RwLockWriteGuard,
FairMutex, FairMutexGuard, MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard,
MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, MutexGuard, Once, RawFairMutex, RawMutex,
RawRwLock, ReentrantMutex, ReentrantMutexGuard, RwLock, RwLockReadGuard,
RwLockUpgradableReadGuard, RwLockWriteGuard, const_fair_mutex, const_mutex,
const_reentrant_mutex, const_rwlock,
};
#[cfg(not(debug_assertions))]
pub use parking_lot::{
const_fair_mutex, const_mutex, const_reentrant_mutex, const_rwlock, FairMutex, FairMutexGuard,
MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard, MappedRwLockReadGuard,
MappedRwLockWriteGuard, Mutex, MutexGuard, Once, RawFairMutex, RawMutex, RawRwLock,
ReentrantMutex, ReentrantMutexGuard, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard,
RwLockWriteGuard,
FairMutex, FairMutexGuard, MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard,
MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, MutexGuard, Once, RawFairMutex, RawMutex,
RawRwLock, ReentrantMutex, ReentrantMutexGuard, RwLock, RwLockReadGuard,
RwLockUpgradableReadGuard, RwLockWriteGuard, const_fair_mutex, const_mutex,
const_reentrant_mutex, const_rwlock,
};

View File

@@ -2,8 +2,8 @@
pub use parking_lot::OnceState;
pub use parking_lot::RawThreadId;
use crate::lockapi::TracingWrapper;
use crate::LazyMutexId;
use crate::lockapi::TracingWrapper;
pub type RawFairMutex = TracingWrapper<::parking_lot::RawFairMutex>;
pub type RawMutex = TracingWrapper<::parking_lot::RawMutex>;

View File

@@ -10,9 +10,9 @@ use std::sync::TryLockResult;
use std::sync::WaitTimeoutResult;
use std::time::Duration;
use crate::util::PrivateTraced;
use crate::BorrowedMutex;
use crate::LazyMutexId;
use crate::util::PrivateTraced;
#[cfg(has_std__sync__LazyLock)]
pub use lazy_lock::LazyLock;
@@ -25,17 +25,16 @@ mod lazy_lock;
/// Refer to the [crate-level][`crate`] documentation for the differences between this struct and
/// the one it wraps.
#[derive(Debug, Default)]
pub struct Mutex<T> {
inner: sync::Mutex<T>,
pub struct Mutex<T: ?Sized> {
id: LazyMutexId,
inner: sync::Mutex<T>,
}
/// Wrapper for [`std::sync::MutexGuard`].
///
/// Refer to the [crate-level][`crate`] documentation for the differences between this struct and
/// the one it wraps.
#[derive(Debug)]
pub struct MutexGuard<'a, T> {
pub struct MutexGuard<'a, T: ?Sized> {
inner: sync::MutexGuard<'a, T>,
_mutex: BorrowedMutex<'a>,
}
@@ -71,7 +70,9 @@ impl<T> Mutex<T> {
id: LazyMutexId::new(),
}
}
}
impl<T: ?Sized> Mutex<T> {
/// Wrapper for [`std::sync::Mutex::lock`].
///
/// # Panics
@@ -79,7 +80,7 @@ impl<T> Mutex<T> {
/// This method participates in lock dependency tracking. If acquiring this lock introduces a
/// dependency cycle, this method will panic.
#[track_caller]
pub fn lock(&self) -> LockResult<MutexGuard<T>> {
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
let mutex = self.id.get_borrowed();
let result = self.inner.lock();
@@ -98,7 +99,7 @@ impl<T> Mutex<T> {
/// This method participates in lock dependency tracking. If acquiring this lock introduces a
/// dependency cycle, this method will panic.
#[track_caller]
pub fn try_lock(&self) -> TryLockResult<MutexGuard<T>> {
pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> {
let mutex = self.id.get_borrowed();
let result = self.inner.try_lock();
@@ -123,12 +124,15 @@ impl<T> Mutex<T> {
}
/// Unwrap the mutex and return its inner value.
pub fn into_inner(self) -> LockResult<T> {
pub fn into_inner(self) -> LockResult<T>
where
T: Sized,
{
self.inner.into_inner()
}
}
impl<T> PrivateTraced for Mutex<T> {
impl<T: ?Sized> PrivateTraced for Mutex<T> {
fn get_id(&self) -> &crate::MutexId {
&self.id
}
@@ -140,21 +144,31 @@ impl<T> From<T> for Mutex<T> {
}
}
impl<T> Deref for MutexGuard<'_, T> {
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for MutexGuard<'_, T> {
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: fmt::Display> fmt::Display for MutexGuard<'_, T> {
impl<T: ?Sized + fmt::Debug> fmt::Debug for MutexGuard<'_, T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl<T: fmt::Display + ?Sized> fmt::Display for MutexGuard<'_, T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
@@ -274,15 +288,14 @@ impl Condvar {
/// Wrapper for [`std::sync::RwLock`].
#[derive(Debug, Default)]
pub struct RwLock<T> {
inner: sync::RwLock<T>,
pub struct RwLock<T: ?Sized> {
id: LazyMutexId,
inner: sync::RwLock<T>,
}
/// Hybrid wrapper for both [`std::sync::RwLockReadGuard`] and [`std::sync::RwLockWriteGuard`].
///
/// Please refer to [`RwLockReadGuard`] and [`RwLockWriteGuard`] for usable types.
#[derive(Debug)]
pub struct TracingRwLockGuard<'a, L> {
inner: L,
_mutex: BorrowedMutex<'a>,
@@ -300,7 +313,9 @@ impl<T> RwLock<T> {
id: LazyMutexId::new(),
}
}
}
impl<T: ?Sized> RwLock<T> {
/// Wrapper for [`std::sync::RwLock::read`].
///
/// # Panics
@@ -308,7 +323,7 @@ impl<T> RwLock<T> {
/// This method participates in lock dependency tracking. If acquiring this lock introduces a
/// dependency cycle, this method will panic.
#[track_caller]
pub fn read(&self) -> LockResult<RwLockReadGuard<T>> {
pub fn read(&self) -> LockResult<RwLockReadGuard<'_, T>> {
let mutex = self.id.get_borrowed();
let result = self.inner.read();
@@ -325,7 +340,7 @@ impl<T> RwLock<T> {
/// This method participates in lock dependency tracking. If acquiring this lock introduces a
/// dependency cycle, this method will panic.
#[track_caller]
pub fn write(&self) -> LockResult<RwLockWriteGuard<T>> {
pub fn write(&self) -> LockResult<RwLockWriteGuard<'_, T>> {
let mutex = self.id.get_borrowed();
let result = self.inner.write();
@@ -342,7 +357,7 @@ impl<T> RwLock<T> {
/// This method participates in lock dependency tracking. If acquiring this lock introduces a
/// dependency cycle, this method will panic.
#[track_caller]
pub fn try_read(&self) -> TryLockResult<RwLockReadGuard<T>> {
pub fn try_read(&self) -> TryLockResult<RwLockReadGuard<'_, T>> {
let mutex = self.id.get_borrowed();
let result = self.inner.try_read();
@@ -359,7 +374,7 @@ impl<T> RwLock<T> {
/// This method participates in lock dependency tracking. If acquiring this lock introduces a
/// dependency cycle, this method will panic.
#[track_caller]
pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<T>> {
pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<'_, T>> {
let mutex = self.id.get_borrowed();
let result = self.inner.try_write();
@@ -377,12 +392,15 @@ impl<T> RwLock<T> {
}
/// Unwrap the mutex and return its inner value.
pub fn into_inner(self) -> LockResult<T> {
pub fn into_inner(self) -> LockResult<T>
where
T: Sized,
{
self.inner.into_inner()
}
}
impl<T> PrivateTraced for RwLock<T> {
impl<T: ?Sized> PrivateTraced for RwLock<T> {
fn get_id(&self) -> &crate::MutexId {
&self.id
}
@@ -396,24 +414,48 @@ impl<T> From<T> for RwLock<T> {
impl<L, T> Deref for TracingRwLockGuard<'_, L>
where
T: ?Sized,
L: Deref<Target = T>,
{
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
self.inner.deref()
}
}
impl<T, L> DerefMut for TracingRwLockGuard<'_, L>
impl<L, T> DerefMut for TracingRwLockGuard<'_, L>
where
T: ?Sized,
L: Deref<Target = T> + DerefMut,
{
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.deref_mut()
}
}
impl<L> fmt::Debug for TracingRwLockGuard<'_, L>
where
L: fmt::Debug,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl<L> fmt::Display for TracingRwLockGuard<'_, L>
where
L: fmt::Display,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
/// Wrapper around [`std::sync::Once`].
///
/// Refer to the [crate-level][`crate`] documentaiton for the differences between this struct