use core::sync::atomic::Ordering;
use std::time::SystemTime;
use crate::AtomicOptionDuration;
#[repr(transparent)]
pub struct AtomicOptionSystemTime(AtomicOptionDuration);
impl core::fmt::Debug for AtomicOptionSystemTime {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("AtomicOptionSystemTime")
.field(&self.load(Ordering::SeqCst))
.finish()
}
}
impl Default for AtomicOptionSystemTime {
#[cfg_attr(not(tarpaulin), inline(always))]
fn default() -> Self {
Self::none()
}
}
impl From<Option<SystemTime>> for AtomicOptionSystemTime {
#[cfg_attr(not(tarpaulin), inline(always))]
fn from(system_time: Option<SystemTime>) -> Self {
Self::new(system_time)
}
}
impl AtomicOptionSystemTime {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn none() -> Self {
Self(AtomicOptionDuration::new(None))
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn now() -> Self {
Self::new(Some(SystemTime::now()))
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn new(system_time: Option<SystemTime>) -> Self {
Self(AtomicOptionDuration::new(
system_time.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
))
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn load(&self, order: Ordering) -> Option<SystemTime> {
self.0.load(order).map(|val| SystemTime::UNIX_EPOCH + val)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn store(&self, system_time: Option<SystemTime>, order: Ordering) {
self.0.store(
system_time.map(|val| val.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
order,
)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn swap(&self, system_time: Option<SystemTime>, order: Ordering) -> Option<SystemTime> {
self
.0
.swap(
system_time.map(|val| val.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
order,
)
.map(|val| SystemTime::UNIX_EPOCH + val)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn compare_exchange(
&self,
current: Option<SystemTime>,
new: Option<SystemTime>,
success: Ordering,
failure: Ordering,
) -> Result<Option<SystemTime>, Option<SystemTime>> {
match self.0.compare_exchange(
current.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
new.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
success,
failure,
) {
Ok(duration) => Ok(duration.map(|d| SystemTime::UNIX_EPOCH + d)),
Err(duration) => Err(duration.map(|d| SystemTime::UNIX_EPOCH + d)),
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn compare_exchange_weak(
&self,
current: Option<SystemTime>,
new: Option<SystemTime>,
success: Ordering,
failure: Ordering,
) -> Result<Option<SystemTime>, Option<SystemTime>> {
match self.0.compare_exchange_weak(
current.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
new.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
success,
failure,
) {
Ok(duration) => Ok(duration.map(|d| SystemTime::UNIX_EPOCH + d)),
Err(duration) => Err(duration.map(|d| SystemTime::UNIX_EPOCH + d)),
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn fetch_update<F>(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: F,
) -> Result<Option<SystemTime>, Option<SystemTime>>
where
F: FnMut(Option<SystemTime>) -> Option<Option<SystemTime>>,
{
self
.0
.fetch_update(set_order, fetch_order, |duration| {
f(duration.map(|d| SystemTime::UNIX_EPOCH + d))
.map(|system_time| system_time.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()))
})
.map(|duration| duration.map(|d| SystemTime::UNIX_EPOCH + d))
.map_err(|duration| duration.map(|d| SystemTime::UNIX_EPOCH + d))
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn is_lock_free() -> bool {
AtomicOptionDuration::is_lock_free()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn into_inner(self) -> Option<SystemTime> {
self.0.into_inner().map(|d| SystemTime::UNIX_EPOCH + d)
}
}
#[cfg(feature = "serde")]
const _: () = {
use serde::{Deserialize, Serialize};
impl Serialize for AtomicOptionSystemTime {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.load(Ordering::SeqCst).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for AtomicOptionSystemTime {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
Ok(Self::new(Option::<SystemTime>::deserialize(deserializer)?))
}
}
};
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_atomic_option_system_time_now() {
let atomic_time = AtomicOptionSystemTime::now();
assert!(atomic_time.load(Ordering::SeqCst).is_some());
}
#[test]
fn test_atomic_option_system_time_none() {
let atomic_time = AtomicOptionSystemTime::none();
assert_eq!(atomic_time.load(Ordering::SeqCst), None);
}
#[test]
fn test_atomic_option_system_time_new_and_load() {
let now = SystemTime::now();
let atomic_time = AtomicOptionSystemTime::new(Some(now));
assert_eq!(atomic_time.load(Ordering::SeqCst), Some(now));
}
#[test]
fn test_atomic_option_system_time_store_and_load() {
let now = SystemTime::now();
let after_one_sec = now + Duration::from_secs(1);
let atomic_time = AtomicOptionSystemTime::new(Some(now));
atomic_time.store(Some(after_one_sec), Ordering::SeqCst);
assert_eq!(atomic_time.load(Ordering::SeqCst), Some(after_one_sec));
}
#[test]
fn test_atomic_option_system_time_swap() {
let now = SystemTime::now();
let after_one_sec = now + Duration::from_secs(1);
let atomic_time = AtomicOptionSystemTime::new(Some(now));
let prev_time = atomic_time.swap(Some(after_one_sec), Ordering::SeqCst);
assert_eq!(prev_time, Some(now));
assert_eq!(atomic_time.load(Ordering::SeqCst), Some(after_one_sec));
}
#[test]
fn test_atomic_option_system_time_compare_exchange() {
let now = SystemTime::now();
let after_one_sec = now + Duration::from_secs(1);
let atomic_time = AtomicOptionSystemTime::new(Some(now));
let result = atomic_time.compare_exchange(
Some(now),
Some(after_one_sec),
Ordering::SeqCst,
Ordering::SeqCst,
);
assert!(result.is_ok());
assert_eq!(atomic_time.load(Ordering::SeqCst), Some(after_one_sec));
}
#[test]
fn test_atomic_option_system_time_compare_exchange_weak() {
let now = SystemTime::now();
let after_one_sec = now + Duration::from_secs(1);
let atomic_time = AtomicOptionSystemTime::new(Some(now));
let mut result;
loop {
result = atomic_time.compare_exchange_weak(
Some(now),
Some(after_one_sec),
Ordering::SeqCst,
Ordering::SeqCst,
);
if result.is_ok() {
break;
}
}
assert!(result.is_ok());
assert_eq!(atomic_time.load(Ordering::SeqCst), Some(after_one_sec));
}
#[test]
fn test_atomic_option_system_time_fetch_update() {
let now = SystemTime::now();
let atomic_time = AtomicOptionSystemTime::new(Some(now));
let result = atomic_time.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
Some(prev.map(|val| val + Duration::from_secs(1)))
});
assert!(result.is_ok());
assert_eq!(result.unwrap(), Some(now));
assert_eq!(
atomic_time.load(Ordering::SeqCst),
Some(now + Duration::from_secs(1))
);
}
#[test]
fn test_atomic_option_system_time_thread_safety() {
use std::sync::Arc;
use std::thread;
let start = SystemTime::now();
let atomic_time = Arc::new(AtomicOptionSystemTime::new(Some(start)));
let mut handles = vec![];
for _ in 0..4 {
let atomic_clone = Arc::clone(&atomic_time);
let handle = thread::spawn(move || {
atomic_clone
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |current| {
current.map(|t| Some(t + Duration::from_secs(1)))
})
.expect("atomic is always Some in this test");
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
assert_eq!(
atomic_time.load(Ordering::SeqCst),
Some(start + Duration::from_secs(4))
);
}
#[test]
fn test_atomic_option_system_time_debug() {
let atomic_time = AtomicOptionSystemTime::now();
let debug_str = format!("{:?}", atomic_time);
assert!(debug_str.contains("AtomicOptionSystemTime"));
}
#[test]
fn test_atomic_option_system_time_default() {
let atomic_time = AtomicOptionSystemTime::default();
assert_eq!(atomic_time.load(Ordering::SeqCst), None);
}
#[test]
fn test_atomic_option_system_time_from() {
let now = Some(SystemTime::now());
let atomic_time = AtomicOptionSystemTime::from(now);
assert_eq!(atomic_time.load(Ordering::SeqCst), now);
}
#[test]
fn test_atomic_option_system_time_from_none() {
let atomic_time = AtomicOptionSystemTime::from(None);
assert_eq!(atomic_time.load(Ordering::SeqCst), None);
}
#[test]
fn test_atomic_option_system_time_into_inner() {
let now = Some(SystemTime::now());
let atomic_time = AtomicOptionSystemTime::new(now);
assert_eq!(atomic_time.into_inner(), now);
}
#[test]
fn test_atomic_option_system_time_into_inner_none() {
let atomic_time = AtomicOptionSystemTime::none();
assert_eq!(atomic_time.into_inner(), None);
}
#[test]
fn test_atomic_option_system_time_compare_exchange_failure() {
let now = Some(SystemTime::now());
let other = now.map(|t| t + Duration::from_secs(5));
let atomic_time = AtomicOptionSystemTime::new(now);
let result = atomic_time.compare_exchange(other, other, Ordering::SeqCst, Ordering::SeqCst);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), now);
}
#[test]
fn test_atomic_option_system_time_compare_exchange_weak_failure() {
let now = Some(SystemTime::now());
let other = now.map(|t| t + Duration::from_secs(5));
let atomic_time = AtomicOptionSystemTime::new(now);
let result =
atomic_time.compare_exchange_weak(other, other, Ordering::SeqCst, Ordering::SeqCst);
assert!(result.is_err());
}
#[test]
fn test_atomic_option_system_time_fetch_update_failure() {
let now = Some(SystemTime::now());
let atomic_time = AtomicOptionSystemTime::new(now);
let result = atomic_time.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), now);
}
#[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));
}
}