al8n/atomic-time

Lock-free, thread-safe atomic versions of Duration, SystemTime, Instant and their Option variants

Rust

11

24 commits

updated Oct 7, 2026

See the code

See what people are saying

SourceMessageScoreDate

atomic-time 1.0.0 — atomic Duration, SystemTime, and Instant types for Rust (r/rust)

Hi r/rust, I’ve released [`atomic-time` 1.0.0](https://crates.io/crates/atomic-time), a crate providing thread-safe atomic versions of Rust’s time types: - `AtomicDuration` - `AtomicOptionDuration` - `AtomicSystemTime` - `AtomicOptionSystemTime` - `AtomicInstant` - `AtomicOptionInstant` The types…

10

Oct 9, 2026

README

Atomic Time

Thread-safe atomic versions of Duration, SystemTime, Instant and their Option variants; lock-free on platforms with native AtomicU128 support, otherwise portable-atomic may fall back to a global lock

github LoC Build codecov

docs.rs crates.io crates.io license

English | 简体中文

Introduction

atomic-time provides thread-safe atomic versions of Rust's standard time types. They are lock-free on platforms with native AtomicU128 support; otherwise, portable-atomic may fall back to a global lock. All types use AtomicU128 (via portable-atomic) under the hood and expose the same API patterns as the standard std::sync::atomic types (load, store, swap, compare_exchange, compare_exchange_weak, fetch_update).

Types

TypeWrapsno_stdarbitraryquickcheck / proptest
AtomicDurationDurationYesWith stdWith std
AtomicOptionDurationOption<Duration>YesWith stdWith std
AtomicSystemTimeSystemTimeNoWith stdWith std
AtomicOptionSystemTimeOption<SystemTime>NoWith stdWith std
AtomicInstantInstantNoNoNo
AtomicOptionInstantOption<Instant>NoNoNo

Installation

[dependencies]
atomic-time = "1"

Feature Flags

FeatureDefaultDescription
stdYesEnables SystemTime and Instant types
serdeNoEnables Serialize/Deserialize for all types, including no_std builds
arbitraryNoEnables arbitrary for duration and system-time types; requires std
quickcheckNoEnables quickcheck for duration and system-time types, and their snapshot Clone implementations
proptestNoEnables proptest strategies for duration and system-time types

For no_std environments (only AtomicDuration and AtomicOptionDuration are available):

[dependencies]
atomic-time = { version = "1", default-features = false }

The optional serde feature can also be enabled in a no_std build with features = ["serde"].

All three property-testing integrations require std. This includes arbitrary, because the current upstream arbitrary v1 crate itself uses std. Opt into one of the integrations with:

[dependencies]
atomic-time = { version = "1", features = ["arbitrary"] }
# or: atomic-time = { version = "1", features = ["quickcheck"] }
# or: atomic-time = { version = "1", features = ["proptest"] }

The quickcheck feature implements Clone for AtomicDuration, AtomicOptionDuration, AtomicSystemTime, and AtomicOptionSystemTime. Cloning performs a SeqCst load, so under concurrent writes the cloned value is the value observed at that load's atomic linearization point.

AtomicInstant and AtomicOptionInstant intentionally do not implement arbitrary, quickcheck, or proptest traits (and do not gain Clone from these features). Their encodings use a process-local baseline, so a seed or corpus cannot produce portable, deterministic values across process runs.

On some bare-metal targets without native compare-and-swap support, the final application must use Cargo feature unification to enable portable-atomic's critical-section feature and provide a target-appropriate critical-section implementation, or adopt another safe configuration from the official portable-atomic guide.

Atomic Operations

All six atomic types provide is_always_lock_free, try_update, update, fetch_min, and fetch_max; the existing fetch_update remains available for compatibility. fetch_min and fetch_max return the value observed before the operation. For Option types, ordering is None < Some; Instant comparisons are meaningful only for values using the same process-local baseline.

AtomicDuration and AtomicOptionDuration also provide fetch_saturating_add and fetch_saturating_sub, returning the old value. For AtomicDuration, these saturate at Duration::MAX and Duration::ZERO. For AtomicOptionDuration, Some values saturate while None remains None (it is not treated as zero). These names intentionally use fetch_saturating_add/sub, rather than fetch_add/sub, to avoid implying integer wrapping; saturating add/sub helpers apply only to the Duration types.

Time Semantics

AtomicSystemTime and AtomicOptionSystemTime accept only values at or after SystemTime::UNIX_EPOCH; earlier values panic.

AtomicInstant and AtomicOptionInstant encode Instant relative to a process-local baseline initialized from SystemTime::now() and Instant::now(). Within the same process, values within the platform's representable Instant range round-trip precisely. Across processes or restarts, the encoding is not portable and decoding only approximates wall-clock time. System clock adjustments can change that cross-process or restart interpretation. Behavior across sleep follows the platform's Instant semantics. Encoded instants must not be persisted as deadlines. If an extreme Duration cannot be represented by the platform's Instant, decoding falls back to the process baseline instead of panicking.

Example

use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::time::Duration;
use atomic_time::AtomicDuration;

let timeout = Arc::new(AtomicDuration::new(Duration::from_secs(30)));

// Update from another thread
let timeout_clone = timeout.clone();
std::thread::spawn(move || {
    timeout_clone.store(Duration::from_secs(60), Ordering::Release);
});
use std::sync::atomic::Ordering;
use std::time::Instant;
use atomic_time::AtomicOptionInstant;

// Track the last time an event occurred
let last_event = AtomicOptionInstant::none();
assert_eq!(last_event.load(Ordering::Relaxed), None);

last_event.store(Some(Instant::now()), Ordering::Release);
assert!(last_event.load(Ordering::Acquire).is_some());

Benchmarks

Run with cargo bench in the benchmark/ directory. Apple M4 Pro.

Duration (cargo bench --bench duration)

ImplementationSingle-thread loadSingle-thread storeContended readLoad under write contentionContended store
AtomicDuration1.04 ns0.72 ns1.08 ns4.44 ns10.7 ns
AtomicOptionDuration1.16 ns0.74 ns1.20 ns4.73 ns14.5 ns
ArcSwap<Duration>2.30 ns86.5 ns2.38 ns15.5 ns1,400 ns
parking_lot::RwLock3.40 ns2.09 ns9.3 ns241.8 ns37.7 ns
std::sync::RwLock4.44 ns2.26 ns411.2 ns89.0 ns36.8 ns

Instant (cargo bench --bench instant)

ImplementationSingle-thread loadSingle-thread storeContended readLoad under write contentionContended store
AtomicInstant2.16 ns3.52 ns2.17 ns14.97 ns22.7 ns
AtomicOptionInstant2.36 ns3.51 ns2.40 ns17.63 ns21.2 ns
ArcSwap<Instant>2.95 ns74.9 ns2.92 ns17.53 ns972 ns
parking_lot::RwLock3.43 ns2.10 ns81.3 ns213.9 ns26.9 ns
std::sync::RwLock4.48 ns2.28 ns422.3 ns87.7 ns76.9 ns

SystemTime (cargo bench --bench system_time)

ImplementationSingle-thread loadSingle-thread storeContended readLoad under write contentionContended store
AtomicSystemTime1.26 ns3.41 ns1.28 ns8.89 ns27.1 ns
AtomicOptionSystemTime1.25 ns3.47 ns1.30 ns9.17 ns25.2 ns
ArcSwap<SystemTime>2.29 ns83.7 ns2.39 ns16.4 ns1,227 ns
parking_lot::RwLock3.50 ns2.13 ns22.0 ns204.5 ns31.0 ns
std::sync::RwLock4.69 ns2.31 ns554.2 ns84.4 ns38.7 ns

Contended read = 4 background threads also reading; Load under write contention = 4 background threads writing, load measured on main thread; Contended store = 4 background threads writing, store measured on main thread. All numbers from Apple M4 Pro.

MSRV

The minimum supported Rust version is 1.85.

License

atomic-time is under the terms of both the MIT license and the Apache License (Version 2.0).

See LICENSE-APACHE, LICENSE-MIT for details.

Copyright (c) 2026 Al Liu.

atomic
rust

al8n/atomic-time

Lock-free, thread-safe atomic versions of Duration, SystemTime, Instant and their Option variants

Rust

11

24 commits

updated Oct 7, 2026

See the code

See what people are saying

SourceMessageScoreDate

atomic-time 1.0.0 — atomic Duration, SystemTime, and Instant types for Rust (r/rust)

Hi r/rust, I’ve released [`atomic-time` 1.0.0](https://crates.io/crates/atomic-time), a crate providing thread-safe atomic versions of Rust’s time types: - `AtomicDuration` - `AtomicOptionDuration` - `AtomicSystemTime` - `AtomicOptionSystemTime` - `AtomicInstant` - `AtomicOptionInstant` The types…

10

Oct 9, 2026

README

Atomic Time

Thread-safe atomic versions of Duration, SystemTime, Instant and their Option variants; lock-free on platforms with native AtomicU128 support, otherwise portable-atomic may fall back to a global lock

github LoC Build codecov

docs.rs crates.io crates.io license

English | 简体中文

Introduction

atomic-time provides thread-safe atomic versions of Rust's standard time types. They are lock-free on platforms with native AtomicU128 support; otherwise, portable-atomic may fall back to a global lock. All types use AtomicU128 (via portable-atomic) under the hood and expose the same API patterns as the standard std::sync::atomic types (load, store, swap, compare_exchange, compare_exchange_weak, fetch_update).

Types

TypeWrapsno_stdarbitraryquickcheck / proptest
AtomicDurationDurationYesWith stdWith std
AtomicOptionDurationOption<Duration>YesWith stdWith std
AtomicSystemTimeSystemTimeNoWith stdWith std
AtomicOptionSystemTimeOption<SystemTime>NoWith stdWith std
AtomicInstantInstantNoNoNo
AtomicOptionInstantOption<Instant>NoNoNo

Installation

[dependencies]
atomic-time = "1"

Feature Flags

FeatureDefaultDescription
stdYesEnables SystemTime and Instant types
serdeNoEnables Serialize/Deserialize for all types, including no_std builds
arbitraryNoEnables arbitrary for duration and system-time types; requires std
quickcheckNoEnables quickcheck for duration and system-time types, and their snapshot Clone implementations
proptestNoEnables proptest strategies for duration and system-time types

For no_std environments (only AtomicDuration and AtomicOptionDuration are available):

[dependencies]
atomic-time = { version = "1", default-features = false }

The optional serde feature can also be enabled in a no_std build with features = ["serde"].

All three property-testing integrations require std. This includes arbitrary, because the current upstream arbitrary v1 crate itself uses std. Opt into one of the integrations with:

[dependencies]
atomic-time = { version = "1", features = ["arbitrary"] }
# or: atomic-time = { version = "1", features = ["quickcheck"] }
# or: atomic-time = { version = "1", features = ["proptest"] }

The quickcheck feature implements Clone for AtomicDuration, AtomicOptionDuration, AtomicSystemTime, and AtomicOptionSystemTime. Cloning performs a SeqCst load, so under concurrent writes the cloned value is the value observed at that load's atomic linearization point.

AtomicInstant and AtomicOptionInstant intentionally do not implement arbitrary, quickcheck, or proptest traits (and do not gain Clone from these features). Their encodings use a process-local baseline, so a seed or corpus cannot produce portable, deterministic values across process runs.

On some bare-metal targets without native compare-and-swap support, the final application must use Cargo feature unification to enable portable-atomic's critical-section feature and provide a target-appropriate critical-section implementation, or adopt another safe configuration from the official portable-atomic guide.

Atomic Operations

All six atomic types provide is_always_lock_free, try_update, update, fetch_min, and fetch_max; the existing fetch_update remains available for compatibility. fetch_min and fetch_max return the value observed before the operation. For Option types, ordering is None < Some; Instant comparisons are meaningful only for values using the same process-local baseline.

AtomicDuration and AtomicOptionDuration also provide fetch_saturating_add and fetch_saturating_sub, returning the old value. For AtomicDuration, these saturate at Duration::MAX and Duration::ZERO. For AtomicOptionDuration, Some values saturate while None remains None (it is not treated as zero). These names intentionally use fetch_saturating_add/sub, rather than fetch_add/sub, to avoid implying integer wrapping; saturating add/sub helpers apply only to the Duration types.

Time Semantics

AtomicSystemTime and AtomicOptionSystemTime accept only values at or after SystemTime::UNIX_EPOCH; earlier values panic.

AtomicInstant and AtomicOptionInstant encode Instant relative to a process-local baseline initialized from SystemTime::now() and Instant::now(). Within the same process, values within the platform's representable Instant range round-trip precisely. Across processes or restarts, the encoding is not portable and decoding only approximates wall-clock time. System clock adjustments can change that cross-process or restart interpretation. Behavior across sleep follows the platform's Instant semantics. Encoded instants must not be persisted as deadlines. If an extreme Duration cannot be represented by the platform's Instant, decoding falls back to the process baseline instead of panicking.

Example

use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::time::Duration;
use atomic_time::AtomicDuration;

let timeout = Arc::new(AtomicDuration::new(Duration::from_secs(30)));

// Update from another thread
let timeout_clone = timeout.clone();
std::thread::spawn(move || {
    timeout_clone.store(Duration::from_secs(60), Ordering::Release);
});
use std::sync::atomic::Ordering;
use std::time::Instant;
use atomic_time::AtomicOptionInstant;

// Track the last time an event occurred
let last_event = AtomicOptionInstant::none();
assert_eq!(last_event.load(Ordering::Relaxed), None);

last_event.store(Some(Instant::now()), Ordering::Release);
assert!(last_event.load(Ordering::Acquire).is_some());

Benchmarks

Run with cargo bench in the benchmark/ directory. Apple M4 Pro.

Duration (cargo bench --bench duration)

ImplementationSingle-thread loadSingle-thread storeContended readLoad under write contentionContended store
AtomicDuration1.04 ns0.72 ns1.08 ns4.44 ns10.7 ns
AtomicOptionDuration1.16 ns0.74 ns1.20 ns4.73 ns14.5 ns
ArcSwap<Duration>2.30 ns86.5 ns2.38 ns15.5 ns1,400 ns
parking_lot::RwLock3.40 ns2.09 ns9.3 ns241.8 ns37.7 ns
std::sync::RwLock4.44 ns2.26 ns411.2 ns89.0 ns36.8 ns

Instant (cargo bench --bench instant)

ImplementationSingle-thread loadSingle-thread storeContended readLoad under write contentionContended store
AtomicInstant2.16 ns3.52 ns2.17 ns14.97 ns22.7 ns
AtomicOptionInstant2.36 ns3.51 ns2.40 ns17.63 ns21.2 ns
ArcSwap<Instant>2.95 ns74.9 ns2.92 ns17.53 ns972 ns
parking_lot::RwLock3.43 ns2.10 ns81.3 ns213.9 ns26.9 ns
std::sync::RwLock4.48 ns2.28 ns422.3 ns87.7 ns76.9 ns

SystemTime (cargo bench --bench system_time)

ImplementationSingle-thread loadSingle-thread storeContended readLoad under write contentionContended store
AtomicSystemTime1.26 ns3.41 ns1.28 ns8.89 ns27.1 ns
AtomicOptionSystemTime1.25 ns3.47 ns1.30 ns9.17 ns25.2 ns
ArcSwap<SystemTime>2.29 ns83.7 ns2.39 ns16.4 ns1,227 ns
parking_lot::RwLock3.50 ns2.13 ns22.0 ns204.5 ns31.0 ns
std::sync::RwLock4.69 ns2.31 ns554.2 ns84.4 ns38.7 ns

Contended read = 4 background threads also reading; Load under write contention = 4 background threads writing, load measured on main thread; Contended store = 4 background threads writing, store measured on main thread. All numbers from Apple M4 Pro.

MSRV

The minimum supported Rust version is 1.85.

License

atomic-time is under the terms of both the MIT license and the Apache License (Version 2.0).

See LICENSE-APACHE, LICENSE-MIT for details.

Copyright (c) 2026 Al Liu.

atomic
rust

Project health

crates.io

Latest release

1.0.0 · Oct 7, 2026

Releases this year

3