Niall Douglas wrote:
I still don't see how the compiler knows anything about that. The compiler can see a syscall, that is all.
It's the reverse. The compiler has to know that the syscall doesn't change 'result' in order to prove that 'result' is still zero. And the syscall in this case does change 'result' - by executing a function that calls a lambda that assigns to 'result'. So a compiler that "knew" that the syscall doesn't change 'result' would be broken. This is really not much different on a conceptual level from int main() { extern void f( int* ); int result = 0; f( &result ); std::cout << result << std::endl; } The compiler cannot know that f doesn't change 'result'.
Your original point was that the docs should not claim no need for predicate testing during waits. I asserted this because unlike a condvar, permits carry state i.e. their own predicate, so predicate testing - where that predicate is for indicating a thread can continue - is unnecessary.
You assert that: "You will never see spurious wakeups from a permit object -- [...]. This means you don't need to do any wait predicate checking with permits..." That is, you specifically claim that it is the absence of spurious wakeups that frees one from predicate testing after wait returns, not the fact that the permit has state of its own. And the example I provided demonstrates a situation in which a thread sees something that is indistinguishable from a spurious wakeup, from that thread's point of view. The wait call returns, but the state of the program is as if nobody has called notify_one. The predicate P in my example represents program state. Your threads are calling wait because they are waiting for something to occur. P=1 represents a state in which this something has occurred. P=0 represents a state in which it has not. I can rephrase the example with a queue Q, so that P is Q.empty(), producers push items onto Q, and consumers pop all the items and do something with them. In this formulation, a spurious wakeup would mean that a consumer returns from wait() and finds the queue empty, which is exactly what happens in my example.