From c87ba0d5373dc9aba45154b5f143acf2e954a015 Mon Sep 17 00:00:00 2001 From: A L Manning <10554686+A-Manning@users.noreply.github.com> Date: Fri, 20 Oct 2023 23:49:20 +0800 Subject: [PATCH] Add support for atomic variant of Instant (#29) Signed-off-by: Ash Manning <10554686+A-Manning@users.noreply.github.com> --- Cargo.toml | 6 +++ src/instant.rs | 121 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 ++ 3 files changed, 132 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 90c5d8a..3741753 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/instant.rs b/src/instant.rs index 85fcd7e..9ae2726 100644 --- a/src/instant.rs +++ b/src/instant.rs @@ -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 { @@ -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 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; diff --git a/src/lib.rs b/src/lib.rs index c636b7c..174c180 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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),