Thanks for mentioning this, it's indeed one of the interesting subtleties of futex().
In my defense, I explicitly say in the post that it's not a replacement for the man page and suggest the readers at least go through the descriptions of FUTEX_WAIT and FUTEX_WAKE. The former explains this behavior in the very first sentence.
Moreover, the post mentions this behavior twice:
1. When explaining spurious wakeups in the wait_on_futex_value
[...] FUTEX_WAIT will immediately return if the
value at the futex address is not equal to val. In our case this can
happen if the child issued a wait before the parent wrote 0xA, for
example.
2. In a comment inside Mutex::lock()
// We're here when either:
// (a) the mutex was in fact unlocked (by an intervening thread).
But your point on avoiding an insidious deadlock is spot on.
Indeed it is mentioned. I wrote the comment because repeating the check in the kernel on a user provided value is what distinguishes futexes from a condvar. Of course muted+condvar+int is not a replacement if you're implementing pthreads, but it's also much less efficient than futexes if you're implementing a synchronization primitive not provided by pthreads.
(The most common use of futexes that I have seen outside glibc is to implement binary semaphores. Binary semaphores can be a lighter-weight alternative to condition variables when only a single thread can ever wait on an event).
In my experience futexes are primarily used to confound valgrind. Damn you libgomp (yeah I know you can recompile gcc to use pthreads in the openmp implementation).
In my defense, I explicitly say in the post that it's not a replacement for the man page and suggest the readers at least go through the descriptions of FUTEX_WAIT and FUTEX_WAKE. The former explains this behavior in the very first sentence.
Moreover, the post mentions this behavior twice:
1. When explaining spurious wakeups in the wait_on_futex_value
2. In a comment inside Mutex::lock() But your point on avoiding an insidious deadlock is spot on.