- Notifications
You must be signed in to change notification settings - Fork2
A downward counter (CountDownLatch) which can be used to synchronize threads or coordinate tasks
License
Apache-2.0, MIT licenses found
Licenses found
mirromutth/latches
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
A latch is a downward counter which can be used to synchronize threads or coordinate tasks. The value of the counter is initialized on creation. Threads/tasks may block/suspend on the latch until the counter is decremented to 0.
In contrast tostd::sync::Barrier
, it is a one-shot phenomenon, that mean the counter will not be reset after reaching 0. Instead, it has the useful property that it does not make them wait for the counter to reach 0 by callingcount_down()
orarrive()
. This also means that it can be decremented by a participating thread/task more than once.
See alsostd::latch
in C++ 20,java.util.concurrent.CountDownLatch
in Java,Concurrent::CountDownLatch
inconcurrent-ruby
.
Thesync
implementation withatomic-wait
by default features:
cargo add latches
Thetask
implementation withstd
:
cargo add latches --no-default-features --features task --features std
Thefutex
implementation:
cargo add latches --no-default-features --features futex
See alsoWhich One Should Be Used?
It can be used withno_std
when thestd
feature is not enabled.
use std::{sync::Arc, thread};// Naming rule: `latches::{implementation-name}::Latch`.use latches::sync::Latch;let latch =Arc::new(Latch::new(10));for _in0..10{let latch = latch.clone(); thread::spawn(move || latch.count_down());}// Waits 10 threads complete their works.// Requires `.await` if it is the `task` implementation.latch.wait();
use std::{sync::Arc, thread};// Naming rule: `latches::{implementation-name}::Latch`.use latches::sync::Latch;let gate =Arc::new(Latch::new(1));for _in0..10{let gate = gate.clone(); thread::spawn(move ||{// Waits for the gate signal.// Requires `.await` if it is the `task` implementation. gate.wait();// Do some work after gate.});}// Allows 10 threads start their works.gate.count_down();
Thesync
implementation is the default implementation of threads.
Feature dependencies:
- Add
std
feature will make it usestd::sync::Mutex
andstd::sync::Condvar
as condition variables, it supports timeouts - If
std
is disabled, addatomic-wait
feature will make it useatomic-wait
as condition variables, it does not support timeouts - If both
std
andatomic-wait
are disabled, it will throw a compile error
Bothatomic-wait
andsync
are enabled in default features for easy-to-use. So if you want to usesync
withstd
and don't want to import the unnecessary crateatomic-wait
, please disable default features.
Thefutex
implementation is similar to popular implementations of C++20std::latch
, which provides slightly better performance compared to thesync
implementation.
It does not support timeouts for waiting.
Feature dependencies:
- It depends on feature
atomic-wait
and crateatomic-wait
, and cannot be disabled
Thetask
implementation is typically used to coordinate asynchronous tasks.
It requires extern cratealloc
if inno_std
.
Feature dependencies:
- Add
std
feature will make it usestd::sync::Mutex
as thread mutexes on waker collection - If
std
is disabled, addatomic-wait
feature will make it use [atomic-wait
][atomic-wait] as thread mutexes on waker collection - If both
std
andatomic-wait
are disabled, it will use spinlocks as thread mutexes on waker collection
Similarities:
- All implementations are based on atomic, so will not work on platforms that do not support atomic, i.e.
#[cfg(target_has_atomic)]
- All implementations can be used on
no_std
if thestd
feature does not be enabled - All methods except waits: do not need
async
/await
Differences:
sync | task | futex | |
---|---|---|---|
counter type | usize | usize | u32 |
mutexes | std oratomic-wait | std ,atomic-wait , or spinlock | No mutex* |
waits | Blocking | Futures | Blocking |
timeouts | Requriesstd | Requries an async timer | Not support |
* No mutex doesn't mean it doesn't need to eliminate race conditions, in fact it uses Futex (i.e.atomic-wait) instead
If your project is usingasync
/await
tasks, use thetask
implementation. Add it withstd
oratomic-wait
feature might make it more concurrency-friendly forgate scenarios. Like following:
cargo add latches --no-default-features --features task --feature atomic-wait
If the amount of concurrency in your project is small, use thefutex
implementation. Like following:
cargo add latches --no-default-features --features futex
Otherwise, use thesync
implementation. It has the same counter typeusize
as thetask
implementation andstd::sync::Barrier
. Add it withstd
feature will make it supports timeouts. Note that it should be used with one of thestd
oratomic-wait
features, otherwise a compilation error will be thrown. Like following:
# Both `sync` and `atomic-wait` are enabled in default featurescargo add latches
Or enablestd
feature for timeouts support:
cargo add latches --no-default-features --features sync --features std
Under a large amount of concurrency, there is no obvious performance gap between thefutex
implementation and thesync
implementation.
Additionally, if you are migrating C++ code to Rust, using afutex
implementation may be an approach which makes more conservative, e.g. similar memory usage, ABI calls, etc. Note that thefutex
implementation has no undefined behavior, which is not like thestd::latch
in C++.
Run benchmarks for all implementations withatomic-wait
(thefutex
implementation depends onatomic-wait
):
cargo bench --package benches
Run benchmarks with thesync
implementation withstd
:
cargo bench --package benches --no-default-features --features sync --features std
Run benchmarks with thetask
implementation withatomic-wait
:
cargo bench --package benches --no-default-features --features task --features atomic-wait
Or run benchmarks withstd
and comparison group:
cargo bench --package benches --features std --features comparison
etc.
Overall benchmarks include thread scheduling overhead, andLatch
is much faster than thread scheduling, so there may be timing jitter and large standard deviations. All overall benchmarks will have name postfix-overall
.
The asynchronous comparison groups are also atomic-based and depend on specific async libraries such astokio
andasync-std
.
The synchronous comparison group usesMutex
state instead of atomic.
Latches is released under the terms of either theMIT License or theApache License Version 2.0, at your option.
About
A downward counter (CountDownLatch) which can be used to synchronize threads or coordinate tasks