use std::{sync::atomic::Ordering, time::Instant};
use super::*;
#[repr(transparent)]
pub struct AtomicOptionInstant(AtomicOptionDuration);
impl Default for AtomicOptionInstant {
#[inline]
fn default() -> Self {
Self::none()
}
}
impl AtomicOptionInstant {
#[inline]
pub const fn none() -> Self {
Self(AtomicOptionDuration::new(None))
}
pub fn now() -> Self {
Self::new(Some(Instant::now()))
}
pub fn new(instant: Option<Instant>) -> Self {
Self(AtomicOptionDuration::new(
instant.map(encode_instant_to_duration),
))
}
pub fn load(&self, order: Ordering) -> Option<Instant> {
self.0.load(order).map(decode_instant_from_duration)
}
pub fn store(&self, instant: Option<Instant>, order: Ordering) {
self.0.store(instant.map(encode_instant_to_duration), order)
}
pub fn swap(&self, instant: Option<Instant>, order: Ordering) -> Option<Instant> {
self
.0
.swap(instant.map(encode_instant_to_duration), order)
.map(decode_instant_from_duration)
}
pub fn compare_exchange(
&self,
current: Option<Instant>,
new: Option<Instant>,
success: Ordering,
failure: Ordering,
) -> Result<Option<Instant>, Option<Instant>> {
match self.0.compare_exchange(
current.map(encode_instant_to_duration),
new.map(encode_instant_to_duration),
success,
failure,
) {
Ok(duration) => Ok(duration.map(decode_instant_from_duration)),
Err(duration) => Err(duration.map(decode_instant_from_duration)),
}
}
pub fn compare_exchange_weak(
&self,
current: Option<Instant>,
new: Option<Instant>,
success: Ordering,
failure: Ordering,
) -> Result<Option<Instant>, Option<Instant>> {
match self.0.compare_exchange_weak(
current.map(encode_instant_to_duration),
new.map(encode_instant_to_duration),
success,
failure,
) {
Ok(duration) => Ok(duration.map(decode_instant_from_duration)),
Err(duration) => Err(duration.map(decode_instant_from_duration)),
}
}
pub fn fetch_update<F>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: F,
) -> Result<Option<Instant>, Option<Instant>>
where
F: FnMut(Option<Instant>) -> Option<Option<Instant>>,
{
self
.0
.fetch_update(set_order, fetch_order, |duration| {
f(duration.map(decode_instant_from_duration))
.map(|system_time| system_time.map(encode_instant_to_duration))
})
.map(|duration| duration.map(decode_instant_from_duration))
.map_err(|duration| duration.map(decode_instant_from_duration))
}
}
#[cfg(feature = "serde")]
const _: () = {
use serde::{Deserialize, Serialize};
impl Serialize for AtomicOptionInstant {
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 AtomicOptionInstant {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Ok(Self::new(
Option::<Duration>::deserialize(deserializer)?.map(decode_instant_from_duration),
))
}
}
};
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::Ordering;
use std::time::{Duration, Instant};
#[test]
fn test_atomic_option_instant_none() {
let atomic_instant = AtomicOptionInstant::none();
assert_eq!(atomic_instant.load(Ordering::SeqCst), None);
}
#[test]
fn test_atomic_option_instant_now() {
let atomic_instant = AtomicOptionInstant::now();
let now = Instant::now();
if let Some(loaded_instant) = atomic_instant.load(Ordering::SeqCst) {
assert!(loaded_instant <= now);
assert!(loaded_instant >= now - Duration::from_secs(1));
} else {
panic!("AtomicOptionInstant::now() should not be None");
}
}
#[test]
fn test_atomic_option_instant_new_and_load() {
let now = Some(Instant::now());
let atomic_instant = AtomicOptionInstant::new(now);
assert_eq!(atomic_instant.load(Ordering::SeqCst), now);
}
#[test]
fn test_atomic_option_instant_store_and_load() {
let now = Some(Instant::now());
let after_one_sec = now.map(|t| t + Duration::from_secs(1));
let atomic_instant = AtomicOptionInstant::new(now);
atomic_instant.store(after_one_sec, Ordering::SeqCst);
assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
}
#[test]
fn test_atomic_option_instant_swap() {
let now = Some(Instant::now());
let after_one_sec = now.map(|t| t + Duration::from_secs(1));
let atomic_instant = AtomicOptionInstant::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_option_instant_compare_exchange() {
let now = Some(Instant::now());
let after_one_sec = now.map(|t| t + Duration::from_secs(1));
let atomic_instant = AtomicOptionInstant::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_option_instant_compare_exchange_weak() {
let now = Some(Instant::now());
let after_one_sec = now.map(|t| t + Duration::from_secs(1));
let atomic_instant = AtomicOptionInstant::new(now);
let mut result;
loop {
result = atomic_instant.compare_exchange_weak(
now,
after_one_sec,
Ordering::SeqCst,
Ordering::SeqCst,
);
if result.is_ok() || result.unwrap_err() != now {
break;
}
}
assert!(result.is_ok());
assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
}
#[test]
fn test_atomic_option_instant_fetch_update() {
let now = Some(Instant::now());
let atomic_instant = AtomicOptionInstant::new(now);
let result = atomic_instant.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
Some(prev.map(|t| t + Duration::from_secs(1)))
});
assert!(result.is_ok());
assert_eq!(result.unwrap(), now);
assert_eq!(
atomic_instant.load(Ordering::SeqCst),
now.map(|t| t + Duration::from_secs(1))
);
}
#[test]
fn test_atomic_option_instant_thread_safety() {
use std::sync::Arc;
use std::thread;
let atomic_time = Arc::new(AtomicOptionInstant::now());
let mut handles = vec![];
for _ in 0..4 {
let atomic_clone = Arc::clone(&atomic_time);
let handle = thread::spawn(move || {
let current = atomic_clone.load(Ordering::SeqCst);
if let Some(current_time) = current {
let new_time = current_time + Duration::from_secs(1);
atomic_clone.store(Some(new_time), Ordering::SeqCst);
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
if let Some(updated_time) = atomic_time.load(Ordering::SeqCst) {
assert!(updated_time > Instant::now() - Duration::from_secs(4));
} else {
panic!("AtomicOptionInstant should not be None");
}
}
#[cfg(feature = "serde")]
#[test]
fn test_atomic_system_time_serde() {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct Test {
time: AtomicOptionSystemTime,
}
let now = SystemTime::now();
let test = Test {
time: AtomicOptionSystemTime::new(Some(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), Some(now));
}
}