use std::time::Instant;
use super::*;
#[repr(transparent)]
pub struct AtomicInstant(AtomicDuration);
impl core::fmt::Debug for AtomicInstant {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("AtomicInstant")
.field(&self.load(Ordering::SeqCst))
.finish()
}
}
impl From<Instant> for AtomicInstant {
#[cfg_attr(not(tarpaulin), inline(always))]
fn from(instant: Instant) -> Self {
Self::new(instant)
}
}
impl AtomicInstant {
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn now() -> Self {
Self::new(Instant::now())
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn new(instant: Instant) -> Self {
Self(AtomicDuration::new(encode_instant_to_duration(instant)))
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn load(&self, order: Ordering) -> Instant {
decode_instant_from_duration(self.0.load(order))
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn store(&self, instant: Instant, order: Ordering) {
self.0.store(encode_instant_to_duration(instant), order)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn swap(&self, instant: Instant, order: Ordering) -> Instant {
decode_instant_from_duration(self.0.swap(encode_instant_to_duration(instant), order))
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn compare_exchange(
&self,
current: Instant,
new: Instant,
success: Ordering,
failure: Ordering,
) -> Result<Instant, Instant> {
match self.0.compare_exchange(
encode_instant_to_duration(current),
encode_instant_to_duration(new),
success,
failure,
) {
Ok(duration) => Ok(decode_instant_from_duration(duration)),
Err(duration) => Err(decode_instant_from_duration(duration)),
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn compare_exchange_weak(
&self,
current: Instant,
new: Instant,
success: Ordering,
failure: Ordering,
) -> Result<Instant, Instant> {
match self.0.compare_exchange_weak(
encode_instant_to_duration(current),
encode_instant_to_duration(new),
success,
failure,
) {
Ok(duration) => Ok(decode_instant_from_duration(duration)),
Err(duration) => Err(decode_instant_from_duration(duration)),
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn fetch_update<F>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: F,
) -> Result<Instant, Instant>
where
F: FnMut(Instant) -> Option<Instant>,
{
self
.0
.fetch_update(set_order, fetch_order, |duration| {
f(decode_instant_from_duration(duration)).map(encode_instant_to_duration)
})
.map(decode_instant_from_duration)
.map_err(decode_instant_from_duration)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn is_lock_free() -> bool {
AtomicU128::is_lock_free()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn into_inner(self) -> Instant {
decode_instant_from_duration(self.0.into_inner())
}
}
#[cfg(feature = "serde")]
const _: () = {
use core::time::Duration;
use serde::{Deserialize, Serialize};
impl Serialize for AtomicInstant {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.load(Ordering::SeqCst).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for AtomicInstant {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Ok(Self::new(decode_instant_from_duration(
Duration::deserialize(deserializer)?,
)))
}
}
};
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_atomic_instant_now() {
let atomic_instant = AtomicInstant::now();
let now = Instant::now();
let loaded_instant = atomic_instant.load(Ordering::SeqCst);
assert!(loaded_instant <= now);
assert!(loaded_instant >= now - Duration::from_secs(1));
}
#[test]
fn test_atomic_instant_new_and_load() {
let now = Instant::now();
let atomic_instant = AtomicInstant::new(now);
assert_eq!(atomic_instant.load(Ordering::SeqCst), now);
}
#[test]
fn test_atomic_instant_store_and_load() {
let now = Instant::now();
let after_one_sec = now + Duration::from_secs(1);
let atomic_instant = AtomicInstant::new(now);
atomic_instant.store(after_one_sec, Ordering::SeqCst);
assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
}
#[test]
fn test_atomic_instant_swap() {
let now = Instant::now();
let after_one_sec = now + Duration::from_secs(1);
let atomic_instant = AtomicInstant::new(now);
let prev_instant = atomic_instant.swap(after_one_sec, Ordering::SeqCst);
assert_eq!(prev_instant, now);
assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
}
#[test]
fn test_atomic_instant_compare_exchange() {
let now = Instant::now();
let after_one_sec = now + Duration::from_secs(1);
let atomic_instant = AtomicInstant::new(now);
let result =
atomic_instant.compare_exchange(now, after_one_sec, Ordering::SeqCst, Ordering::SeqCst);
assert!(result.is_ok());
assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
}
#[test]
fn test_atomic_instant_compare_exchange_weak() {
let now = Instant::now();
let after_one_sec = now + Duration::from_secs(1);
let atomic_instant = AtomicInstant::new(now);
let mut result;
loop {
result = atomic_instant.compare_exchange_weak(
now,
after_one_sec,
Ordering::SeqCst,
Ordering::SeqCst,
);
if result.is_ok() {
break;
}
}
assert!(result.is_ok());
assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
}
#[test]
fn test_atomic_instant_fetch_update() {
let now = Instant::now();
let atomic_instant = AtomicInstant::new(now);
let result = atomic_instant.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
Some(prev + Duration::from_secs(1))
});
assert!(result.is_ok());
assert_eq!(result.unwrap(), now);
assert_eq!(
atomic_instant.load(Ordering::SeqCst),
now + Duration::from_secs(1)
);
}
#[test]
fn test_atomic_instant_thread_safety() {
use std::sync::Arc;
use std::thread;
let start = Instant::now();
let atomic_instant = Arc::new(AtomicInstant::new(start));
let mut handles = vec![];
for _ in 0..4 {
let atomic_clone = atomic_instant.clone();
let handle = thread::spawn(move || {
atomic_clone
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |current| {
Some(current + Duration::from_millis(50))
})
.expect("closure never returns None");
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
assert_eq!(
atomic_instant.load(Ordering::SeqCst),
start + Duration::from_millis(200)
);
}
#[test]
fn test_atomic_instant_debug() {
let atomic_instant = AtomicInstant::now();
let debug_str = format!("{:?}", atomic_instant);
assert!(debug_str.contains("AtomicInstant"));
}
#[test]
fn test_atomic_instant_from() {
let now = Instant::now();
let atomic_instant = AtomicInstant::from(now);
assert_eq!(atomic_instant.load(Ordering::SeqCst), now);
}
#[test]
fn test_atomic_instant_into_inner() {
let now = Instant::now();
let atomic_instant = AtomicInstant::new(now);
assert_eq!(atomic_instant.into_inner(), now);
}
#[test]
fn test_atomic_instant_compare_exchange_failure() {
let now = Instant::now();
let other = now + Duration::from_secs(5);
let atomic_instant = AtomicInstant::new(now);
let result = atomic_instant.compare_exchange(other, other, Ordering::SeqCst, Ordering::SeqCst);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), now);
}
#[test]
fn test_atomic_instant_fetch_update_failure() {
let now = Instant::now();
let atomic_instant = AtomicInstant::new(now);
let result = atomic_instant.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), now);
}
#[cfg(feature = "serde")]
#[test]
fn test_atomic_instant_serde() {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct Test {
time: AtomicInstant,
}
let now = Instant::now();
let test = Test {
time: AtomicInstant::new(now),
};
let serialized = serde_json::to_string(&test).unwrap();
let deserialized: Test = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized.time.load(Ordering::SeqCst), now);
}
#[test]
fn test_atomic_instant_past_value() {
use std::thread;
let past = Instant::now();
thread::sleep(Duration::from_millis(10));
let now = Instant::now();
let atomic = AtomicInstant::new(now);
atomic.store(past, Ordering::SeqCst);
let loaded = atomic.load(Ordering::SeqCst);
assert!(loaded < now);
assert_eq!(loaded, past);
}
#[test]
fn decode_instant_from_extreme_duration_does_not_panic() {
let max_dur = Duration::new(u64::MAX, 999_999_999);
let decoded = crate::utils::decode_instant_from_duration(max_dur);
let _ = decoded;
}
#[cfg(feature = "serde")]
#[test]
fn deserialize_extreme_instant_does_not_panic() {
let json = r#"{"secs":18446744073709551615,"nanos":999999999}"#;
let result: Result<AtomicInstant, _> = serde_json::from_str(json);
assert!(
result.is_ok(),
"deserialization of extreme Instant should not panic"
);
}
}