I was looking for fairness guarantees of the Semaphore implementation and didn't find anything mentioned in the docs. After some digging I found that event_listener::Event is used internally and documented as being fair.
However looking at the implementation of Semaphore::acquire I can see that it is implemented as a loop calling try_acquire and creating a EventListener in order to wait for a permit.
|
loop { |
|
if let Some(guard) = self.try_acquire() { |
|
return guard; |
|
} |
|
|
|
match listener.take() { |
|
None => listener = Some(self.event.listen()), |
|
Some(l) => l.await, |
|
} |
|
} |
If I'm not mistaken it's possible that this code listens for an event and right after being woken another tasks snatches that permit away. This could lead to starvation where the tasks never manages to get a permit.
I think I've seen that exact behavior in some of my benchmarks where some workers hung forever causing timeouts and outliers.
I was wondering if there is a "easy" and correct way to make this implementation fair.
If that's out of scope for async-lock a small text in the documentation would be nice that it is not designed to be fair.
I was looking for fairness guarantees of the
Semaphoreimplementation and didn't find anything mentioned in the docs. After some digging I found thatevent_listener::Eventis used internally and documented as being fair.However looking at the implementation of
Semaphore::acquireI can see that it is implemented as a loop callingtry_acquireand creating aEventListenerin order to wait for a permit.async-lock/src/semaphore.rs
Lines 86 to 95 in 48a5b62
If I'm not mistaken it's possible that this code listens for an event and right after being woken another tasks snatches that permit away. This could lead to starvation where the tasks never manages to get a permit.
I think I've seen that exact behavior in some of my benchmarks where some workers hung forever causing timeouts and outliers.
I was wondering if there is a "easy" and correct way to make this implementation fair.
If that's out of scope for
async-locka small text in the documentation would be nice that it is not designed to be fair.