3 releases (stable)

Uses old Rust 2015

1.0.1 Sep 27, 2018
1.0.0 Sep 26, 2018
0.1.0 Sep 8, 2017

#549 in Concurrency

Download history 18184/week @ 2024-07-20 17475/week @ 2024-07-27 17369/week @ 2024-08-03 15477/week @ 2024-08-10 17204/week @ 2024-08-17 15151/week @ 2024-08-24 18364/week @ 2024-08-31 16165/week @ 2024-09-07 14715/week @ 2024-09-14 18062/week @ 2024-09-21 16538/week @ 2024-09-28 19625/week @ 2024-10-05 19864/week @ 2024-10-12 22232/week @ 2024-10-19 18917/week @ 2024-10-26 14906/week @ 2024-11-02

78,807 downloads per month
Used in 20 crates (17 directly)

MIT license

18KB
204 lines

AtomicCounter

Atomic (thread-safe) counters for Rust.

This crate contains an AtomicCounter trait that can safely be shared across threads.

This crate provides two implementations:

Both implementations are lock-free. Both are a very thin layer over AtomicUsize which is more powerful but might be harder to use correctly.

Which counter to use

  • 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.

No updates are lost - It's just about the ordering!

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.

Example:

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).
  • Additionally, if at least one counter is a 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.

No runtime deps