Skip to content

Commit

Permalink
Add support for atomic variant of Instant (#29)
Browse files Browse the repository at this point in the history
Signed-off-by: Ash Manning <[email protected]>
  • Loading branch information
A-Manning authored Oct 20, 2023
1 parent 408e084 commit c87ba0d
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 0 deletions.
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ repository = "https://github.com/tikv/minstant"
documentation = "https://docs.rs/minstant"
readme = "README.md"
keywords = ["TSC", "clock", "rdtsc", "timing", "nanosecond"]
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
ctor = "0.1.20"

[features]
atomic = []

[target.'cfg(not(target_os = "wasi"))'.dependencies]
libc = "0.2"

Expand Down
121 changes: 121 additions & 0 deletions src/instant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::{
/// [`std::time::Instant`](std::time::Instant) but is faster and more
/// accurate if TSC is available.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Instant(u64);

impl Instant {
Expand Down Expand Up @@ -251,3 +252,123 @@ impl Anchor {
}
}
}

#[cfg(all(feature = "atomic", target_has_atomic = "64"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "atomic", target_has_atomic = "64"))))]
mod atomic {
use super::Instant;
use std::sync::atomic::{AtomicU64, Ordering};
#[cfg(doc)]
use Ordering::*;

/// Atomic variant of [`Instant`].
#[derive(Debug)]
#[repr(transparent)]
pub struct Atomic(AtomicU64);

impl Atomic {
/// Maximum with the current value.
///
/// Finds the maximum of the current value and the argument `val`, and
/// sets the new value to the result.
///
/// Returns the previous value.
///
/// `fetch_max` takes an [`Ordering`] argument which describes the memory ordering
/// of this operation. All ordering modes are possible. Note that using
/// [`Acquire`] makes the store part of this operation [`Relaxed`], and
/// using [`Release`] makes the load part [`Relaxed`].
///
/// **Note**: This method is only available on platforms that support atomic operations on
/// `[u64]`.
#[inline]
pub fn fetch_max(&self, val: Instant, order: Ordering) -> Instant {
Instant(self.0.fetch_max(val.0, order))
}

/// Minimum with the current value.
///
/// Finds the minimum of the current value and the argument `val`, and
/// sets the new value to the result.
///
/// Returns the previous value.
///
/// `fetch_min` takes an [`Ordering`] argument which describes the memory ordering
/// of this operation. All ordering modes are possible. Note that using
/// [`Acquire`] makes the store part of this operation [`Relaxed`], and
/// using [`Release`] makes the load part [`Relaxed`].
///
/// **Note**: This method is only available on platforms that support atomic operations on
/// `[u64]`.
#[inline]
pub fn fetch_min(&self, val: Instant, order: Ordering) -> Instant {
Instant(self.0.fetch_min(val.0, order))
}

/// Consumes the atomic and returns the contained [`Instant`].
///
/// This is safe because passing `self` by value guarantees that no other threads are
/// concurrently accessing the atomic data.
#[inline]
pub fn into_instant(self) -> Instant {
Instant(self.0.into_inner())
}

/// Loads a value from the [`Atomic`].
///
/// `load` takes an [`Ordering`] argument which describes the memory ordering of this operation.
/// Possible values are [`SeqCst`], [`Acquire`] and [`Relaxed`].
///
/// # Panics
///
/// Panics if `order` is [`Release`] or [`AcqRel`].
#[inline]
pub fn load(&self, order: Ordering) -> Instant {
Instant(self.0.load(order))
}

/// Creates a new [`Atomic`].
#[inline]
pub fn new(v: Instant) -> Self {
Self(AtomicU64::new(v.0))
}

/// Stores a value into the [`Atomic`].
///
/// `store` takes an [`Ordering`] argument which describes the memory ordering of this operation.
/// Possible values are [`SeqCst`], [`Release`] and [`Relaxed`].
///
/// # Panics
///
/// Panics if `order` is [`Acquire`] or [`AcqRel`].
#[inline]
pub fn store(&self, val: Instant, order: Ordering) {
self.0.store(val.0, order)
}

/// Stores a value into the [`Atomic`], returning the previous value.
///
/// `swap` takes an [`Ordering`] argument which describes the memory ordering
/// of this operation. All ordering modes are possible. Note that using
/// [`Acquire`] makes the store part of this operation [`Relaxed`], and
/// using [`Release`] makes the load part [`Relaxed`].
///
/// **Note**: This method is only available on platforms that support atomic operations on
/// `u64`
#[inline]
pub fn swap(&self, val: Instant, order: Ordering) -> Instant {
Instant(self.0.swap(val.0, order))
}
}

impl From<Instant> for Atomic {
#[inline]
fn from(instant: Instant) -> Self {
Self::new(instant)
}
}
}

#[cfg(all(feature = "atomic", target_has_atomic = "64"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "atomic", target_has_atomic = "64"))))]
pub use atomic::Atomic;
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,16 @@
//!
//! *[See also the `Instant` type](crate::Instant).*
#![cfg_attr(docsrs, feature(doc_cfg))]

mod coarse_now;
mod instant;
#[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
mod tsc_now;

#[cfg(all(feature = "atomic", target_has_atomic = "64"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "atomic", target_has_atomic = "64"))))]
pub use instant::Atomic;
pub use instant::{Anchor, Instant};

/// Return `true` if the current platform supports [TSC](https://en.wikipedia.org/wiki/Time_Stamp_Counter),
Expand Down

0 comments on commit c87ba0d

Please sign in to comment.