If I have two functions doing
async with mylock.acquire():....
Once the lock is released, is it guaranteed that the first to await will win, or is the order selected differently? (e.g. randomly, arbitrarily, latest, etc.)
The reason I'm asking, if it is not first-come-first-served, there might easily be a case of starvation where the first function attempting to acquire the lock never gets wins it.
When we talk about how something works it's important to distinguish guarantee expressed in specification and side-effect of implementation. First one shouldn't be changed (at least, within major version), second one can be changed any time in the future.
Martijn's answer clearly shows that current implementation preserves order. What about guarantee for future?
Official documentation for Python 3.6 provides guarantee:
only one coroutine proceeds when a release() call resets the state to unlocked; first coroutine which is blocked in acquire() is being processed.
Interesting thing is that neither documentation for Python 3.7 nor documentation for Python 3.8 dev have this line, not sure if it's intentional though. However class's docstring on github has guarantee.
It's also worth mentioning that threading.Lock
(prototype for asyncio's lock) explicitly says that order is undefined:
only one thread proceeds when a release() call resets the state to unlocked; which one of the waiting threads proceeds is not defined, and may vary across implementations.
Long story short, right now only class's docstring promises to maintain order. It's also fair to note that implementation of lock is unlikely to being changed in the nearest future.
Yet imagine however someone will change it (to increase performance, for example). Will docstring be enough to prevent from implementing lock with undefined order? It's up to you to decide.
If your code critically depends on preserving order and expected to have long life cycle nothing bad if you create your own lock (sub)class which will explicitly guarantee order (OrderedLock
or something). You may just vendorize current implementation.
If situation is simpler you may choose not to bother with it and use current implementation.