Skip to content

An atomic counter in rust: Very thin layer above std::sync::atomic integers

License

Notifications You must be signed in to change notification settings

kosta/atomic-counter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AtomicCounter docs.rs Build Status

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.

About

An atomic counter in rust: Very thin layer above std::sync::atomic integers

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published