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

Add support for atomic variant of Instant #29

Merged
merged 1 commit into from
Oct 20, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading