Crates.io | atomic-counter |
lib.rs | atomic-counter |
version | 1.0.1 |
source | src |
created_at | 2017-09-08 09:32:12.58571 |
updated_at | 2018-09-27 07:19:27.760007 |
description | Atomic (thread-safe) counters for Rust |
homepage | |
repository | https://github.com/kosta/atomic-counter |
max_upload_size | |
id | 31040 |
size | 18,256 |
Atomic (thread-safe) counters for Rust.
This crate contains an AtomicCounter
trait
that can safely be shared across threads.
This crate provides two implementations:
RelaxedCounter
which is suitable for
e.g. collecting metrics or generate IDs, but which does not provide
"Sequential Consistency".
RelaxedCounter
uses Relaxed
memory ordering.
ConsistentCounter
which provides the
same interface but is sequentially consistent. Use this counter if the
order of update from multiple threads is important.
ConsistentCounter
uses Sequentially Consistent
memory ordering.
Both implementations are lock-free. Both are a very thin layer over
AtomicUsize
which is more powerful but might be harder to use correctly.
If you are just collecting metrics, the RelaxedCounter
is probably right choice.
If you are generating IDs, but don't make strong assumptions (like allocating
memory based on the ID count), RelaxedCounter
is probably the right choice.
If you are generating multiple IDs where you maintain an ordering
invariant (e.g. ID a
is always greater than ID b
), you need "Sequential
Consistency" and thus need to use ConsistentCounter
. The same is true
for all use cases where the ordering of incrementing the counter is
important.
Note that in both implementations, no count is lost and all operations are atomic. The difference is only in how the order of operations are observed by different threads.
Assume a
is 5 and b
is 4. You always want to maintain a > b
.
Thread 1 executes this code:
a.inc();
b.inc();
Thread 2 gets counts:
let a_local = a.get();
let b_local = b.get();
What are the values for a_local
and b_local
? That depends on the order
in which thread 1 and 2 have run:
a_local
could still be 5 and b_local
is still be 4 (e.g. if thread 2 ran before thread 1)a_local
could be increment to 6 while b_local
is still at 4 (e.g. if thread 1 and 2 ran in parallel)a_local
could be increment to 6 and b_local
be incremented to 5 (e.g. if thread 2 ran after thread 1).RelaxedCounter
, we cannot make
assumption on the order of a.inc()
and b.inc()
. Thus, in this case
thread 2 can also observe a_local
to be 5 (not incremented yet) but
b_local
to be incremented to 5, breaking the invariant a > b
.
Note that if thread 2 (or any other thread) get()
the counts
again, at some point they will observe both values to be incremented.
No operations will be lost. It is only the ordering of the operations
that cannot be assumed if Ordering
is Relaxed
.So in order to maintain invariants such as a > b
across multiple threads,
use ConsistentCounter
.