QSemaphore: fix missed wakes with futex on Windows

We do this by making the 'futexNeedsWakeAllBit' to be part of both the
expectedValue and the pointer we're waiting on.
This makes sense since having the 'futexNeedsWakeAllBit' set is a
requirement for starting a sleep. Thus we should condition
sleeping on whether or not another thread unset it.

Since the futexNeedsWakeAllBit is in the "topmost" bit of the pointer we
wait on we'll need to use the full 64-bits on 64-bit platforms.

This isn't enabled (nor was it an issue) for configurations with
'futexHasWaiterCount' since that works differently, and waits for the
high 32 bits.

Fixes: QTBUG-92148
Change-Id: I424c605f0120ea5e647c5bb19b00ff35eaf1608a
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Mårten Nordheim 2021-03-25 17:26:37 +01:00
parent b8802071ed
commit 587e3bb0ba

View File

@ -200,16 +200,23 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
// indicate we're waiting
start_wait:
auto ptr = futexLow32(&u);
auto ptr = [&u]() {
if constexpr (futexHasWaiterCount)
return futexLow32(&u);
else
return &u;
}();
if (n > 1 || !futexHasWaiterCount) {
u.fetchAndOrRelaxed(futexNeedsWakeAllBit);
curValue |= futexNeedsWakeAllBit;
if (n > 1 && futexHasWaiterCount) {
if constexpr (futexHasWaiterCount) {
if (n > 1) {
ptr = futexHigh32(&u);
//curValue >>= 32; // but this is UB in 32-bit, so roundabout:
// curValue >>= 32; // but this is UB in 32-bit, so roundabout:
curValue = quint64(curValue) >> 32;
}
}
}
if (IsTimed && remainingTime > 0) {
bool timedout = !futexWait(*ptr, curValue, remainingTime);
@ -397,7 +404,7 @@ void QSemaphore::release(int n)
futexWakeOp(*futexLow32(&u), n, INT_MAX, *futexHigh32(&u), FUTEX_OP(op, oparg, cmp, cmparg));
}
#else
// Unset the bit and wake everyone. There are two possibibilies
// Unset the bit and wake everyone. There are two possibilities
// under which a thread can set the bit between the AND and the
// futexWake:
// 1) it did see the new counter value, but it wasn't enough for