Skip to content

Commit

Permalink
core: fix warnings when compiling without std (tokio-rs#2022)
Browse files Browse the repository at this point in the history
Currently, compiling `tracing-core` with `default-features = false`
(i.e. for `no_std` targets) emits a few warnings. This is due to the
spinlock implementation's use of the deprecated `atomic::spin_loop_hint`
function (renamed to `hint::spin_loop`), and the use of deprecated
`compare_and_swap` instead of `compare_exchange` methods. Now that our
MSRV is 1.49 (the version in which `hint::spin_loop` was stabilized), we
can fix these warnings.

This branch replaces the deprecated APIs.

Also, I noticed that one of the tests emits unused-imports warnings with
`--no-default-features`. This is because the actual tests are feature
flagged to require `std`, but the module itself doesn't, so the imports
are just hanging out and not getting used for anything. I went ahead and
fixed that as well.

Signed-off-by: Eliza Weisman <[email protected]>
  • Loading branch information
hawkw authored and kaffarell committed May 22, 2024
1 parent d5bf5df commit 36056db
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 2 deletions.
118 changes: 118 additions & 0 deletions tracing-core/src/spin/mutex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use core::cell::UnsafeCell;
use core::default::Default;
use core::fmt;
use core::hint;
use core::marker::Sync;
use core::ops::{Deref, DerefMut, Drop};
use core::option::Option::{self, None, Some};
use core::sync::atomic::{AtomicBool, Ordering};

/// This type provides MUTual EXclusion based on spinning.
pub(crate) struct Mutex<T: ?Sized> {
lock: AtomicBool,
data: UnsafeCell<T>,
}

/// A guard to which the protected data can be accessed
///
/// When the guard falls out of scope it will release the lock.
#[derive(Debug)]
pub(crate) struct MutexGuard<'a, T: ?Sized> {
lock: &'a AtomicBool,
data: &'a mut T,
}

// Same unsafe impls as `std::sync::Mutex`
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}

impl<T> Mutex<T> {
/// Creates a new spinlock wrapping the supplied data.
pub(crate) const fn new(user_data: T) -> Mutex<T> {
Mutex {
lock: AtomicBool::new(false),
data: UnsafeCell::new(user_data),
}
}
}

impl<T: ?Sized> Mutex<T> {
fn obtain_lock(&self) {
while self
.lock
.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
// Wait until the lock looks unlocked before retrying
while self.lock.load(Ordering::Relaxed) {
hint::spin_loop();
}
}
}

/// Locks the spinlock and returns a guard.
///
/// The returned value may be dereferenced for data access
/// and the lock will be dropped when the guard falls out of scope.
pub(crate) fn lock(&self) -> MutexGuard<'_, T> {
self.obtain_lock();
MutexGuard {
lock: &self.lock,
data: unsafe { &mut *self.data.get() },
}
}

/// Tries to lock the mutex. If it is already locked, it will return None. Otherwise it returns
/// a guard within Some.
pub(crate) fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
if self
.lock
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
{
Some(MutexGuard {
lock: &self.lock,
data: unsafe { &mut *self.data.get() },
})
} else {
None
}
}
}

impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.try_lock() {
Some(guard) => write!(f, "Mutex {{ data: ")
.and_then(|()| (&*guard).fmt(f))
.and_then(|()| write!(f, "}}")),
None => write!(f, "Mutex {{ <locked> }}"),
}
}
}

impl<T: ?Sized + Default> Default for Mutex<T> {
fn default() -> Mutex<T> {
Mutex::new(Default::default())
}
}

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

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

impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> {
/// The dropping of the MutexGuard will release the lock it was created from.
fn drop(&mut self) {
self.lock.store(false, Ordering::Release);
}
}
2 changes: 0 additions & 2 deletions tracing-core/tests/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use common::*;
#[cfg(feature = "std")]
use tracing_core::dispatch::*;

#[cfg(feature = "std")]
#[test]
fn set_default_dispatch() {
set_global_default(Dispatch::new(TestCollectorA)).expect("global dispatch set failed");
Expand All @@ -21,7 +20,6 @@ fn set_default_dispatch() {
get_default(|current| assert!(current.is::<TestCollectorA>(), "global dispatch get failed"));
}

#[cfg(feature = "std")]
#[test]
fn nested_set_default() {
let _guard = set_default(&Dispatch::new(TestCollectorA));
Expand Down

0 comments on commit 36056db

Please sign in to comment.