2 ms·
I might be missing something, but I think that the handling of the sleeper state is wrong. mutex is unlocked (state: unlocked) A locks mutex (state: lock
by gpderetta 4y ago
I might be missing something, but I think that the handling of the sleeper state is wrong.
mutex is unlocked (state: unlocked)
A locks mutex (state: locked -> locked)
B attempts to lock mutex (state: locked -> sleeper)
C attempts to lock mutex (no changes)
A unlock mutex (state: sleeper -> unlocked), state was sleeper so notify_one B
B locks the mutex (state: unlocked ->locked)
B unlocks mutex (state: locked -> unlocked), state was locked, so do not notify
C hangs forever. To use notify_one you need to either guarantee at most one waiter (making notify_one pointless) or you need a wait count. If you use only one bit (or when your count saturates, which is the same thing), you either need to notify_all (and deal with the thundering herd) or unconditionally call notify_one (and do away with the fast path). IIRC the original futex paper had a discussion of this.
Incidentally, I love the atomic::wait in c++20, although it is not without issues https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2616r2.html https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p26...).
- molyss 4y agoWhen B wakes up the 1st thing it does is state.exchange(sleeper,…), which returns unlocked, and set the state to sleeper. I think it works. I had some concerns about the second one, but I think it works too…
- gpderetta 4y agoOh, now I see it, very nice. I had missed that there is at least one additional exchange after wait returns.