atomic-progress
A high-performance, thread-safe, headless progress tracking library for Rust.
atomic-progress provides the state management primitives for tracking long-running operations in concurrent applications. Unlike other libraries that couple state tracking with terminal output, atomic-progress is headless. It stores progress state (position, total, timing) efficiently and lets you decide how to render itโwhether to a CLI, a GUI, a web interface, or a structured log.
Key Features
- ๐ High Performance: Uses
AtomicU64for "hot" path updates (incrementing position), ensuring minimal overhead in tight loops. - ๐ Concurrency Native: All progress handles are cheap-to-clone
Arcstructs. Share them across threads without fighting the borrow checker. - ๐จ Renderer Agnostic: "Pull-based" architecture. Your worker threads push updates; your UI thread pulls snapshots.
- ๐ ๏ธ Ergonomic Integrations:
- Iterators:
iter.progress()extension trait. - I/O:
ProgressReaderandProgressWriterwrappers. - Multi-Task:
ProgressStackfor managing dynamic lists of bars.
- Iterators:
- โฑ๏ธ Accurate Timing: Built-in ETA, throughput, and elapsed time calculations handling edge cases (zero-division, pauses).
- ๐ Broad Platform Compatibility: First-class support for
wasm32(viaweb-time) and optimisations forx86_64intrinsics (Hardware Lock Elision).
Platforms
atomic-progress is designed to run anywhere Rust runs, but it includes specific optimizations and considerations for high-performance environments.
x86_64 (Linux, macOS, Windows)
On supported x86 hardware, this library leverages Hardware Lock Elision (HLE) via parking_lot. This allows multiple threads to access the metadata lock speculatively; if no conflicts occur, the lock is elided entirely at the CPU level, resulting in near-zero latency for metadata reads.
WebAssembly (wasm32-unknown-unknown)
This library is fully compatible with WASM environments (browsers, Node.js, Cloudflare Workers). It uses web-time to ensure Instant and Duration calculations work correctly in the browser.
For multi-threaded WASM applications (e.g., using Web Workers with SharedArrayBuffer), you should consider compiling with the following target features to ensure atomics map to native WASM instructions rather than software emulation traps:
+atomics: Essential for theArc<AtomicU64>hot path to work across workers.+nontrapping-fptoint: Recommended for safe float-to-int conversions in ETA/throughput calculations without risking runtime traps on some engines.
# .cargo/config.toml
[]
= [
"-C", "target-feature=+atomics,+bulk-memory,+mutable-globals",
"-C", "target-feature=+nontrapping-fptoint",
]
Installation
Add the library to your project via Cargo:
Optional Features
You can enable additional features to support serialization, which is useful for sending progress snapshots over a network (e.g., to a dashboard or frontend) or between processes.
serde: AddsSerializeandDeserializeimplementations from the serde framework toProgressSnapshotandProgressType.rkyv: Adds zero-copy serialization support via rkyv, ideal for high-performance IPC or shared memory implementations.
To enable these, you can use the cargo add command with --features, or you can directly add them to your Cargo.toml.
Usage Examples
1. The "One-Liner" (Iterators)
The easiest way to track a loop. The iterator adapter automatically detects if it should be a Bar (known size) or a Spinner (unknown size).
use ;
use ProgressIteratorExt;
2. Concurrent / Multi-Threaded
Pass a clone of the progress handle to a worker thread.
use thread;
use ;
3. Tracking I/O (Downloads/Uploads)
Wrap any Read or Write implementor to track bytes automatically.
use ;
use ;
4. Managing Multiple Bars (ProgressStack)
Use ProgressStack when you have a dynamic number of tasks running in parallel-- or you want a simple global/static progress lock shared with your whole binary.
use thread;
use ProgressStack;
Architecture: The Hot/Cold Split
atomic-progress is designed to avoid locking contention on the worker thread.
-
Hot Data (Atomics):
- Fields:
position,total,finished. - Mechanism:
std::sync::atomic. - Benefit: Calling
progress.inc(1)is wait-free. It will never block your worker thread, ensuring your progress bar doesn't slow down your actual processing.
- Fields:
-
Cold Data (RwLock):
- Fields:
name,current_item,error,start/stop time. - Mechanism:
parking_lot::RwLock. - Benefit: These fields change rarely. We optimize for read-concurrency so the renderer can snapshot them without blocking writers.
- Fields:
Async Safe?
Yes. atomic-progress is fully Send + Sync and can be moved into tokio::spawn or other async tasks without issues.
- Hot Path (
inc,set_pos): Uses non-blocking CPU instructions (atomics). This is safe to call directly in async code and will not block the executor. - Cold Path (
set_name,snapshot): Usesparking_lot::RwLock, which is a synchronous lock. However, critical sections are designed to be extremely short (nanoseconds) as they only involve memory copies of string data. You do not need to wrap these calls inspawn_blocking; they are fast enough to run on the main async threads without causing starvation. - Deadlocks: The library API never returns a lock guard. All data access returns owned data (e.g.,
ProgressSnapshot). This makes it impossible to accidentally hold a synchronous lock across an.awaitpoint, eliminating the most common cause of async deadlocks.
License
This project is licensed under the MIT License.