Light cleanup in QSemaphore Futex implementation
Gets rid of a goto, fixes overflow detection with wakeAll set, and fixes 64-bit futex mode with futexHasWaiterCount = false. Change-Id: I8bb98118013fc1dc2a8a405845bec0cb3350494f Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
385e242ba9
commit
a7fabe2328
@ -132,8 +132,8 @@ static constexpr bool futexHasWaiterCount = true;
|
|||||||
static constexpr bool futexHasWaiterCount = false;
|
static constexpr bool futexHasWaiterCount = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const quintptr futexNeedsWakeAllBit =
|
static constexpr quintptr futexNeedsWakeAllBit = futexHasWaiterCount ?
|
||||||
Q_UINT64_C(1) << (sizeof(quintptr) * CHAR_BIT - 1);
|
(Q_UINT64_C(1) << (sizeof(quintptr) * CHAR_BIT - 1)) : 0x80000000U;
|
||||||
|
|
||||||
static int futexAvailCounter(quintptr v)
|
static int futexAvailCounter(quintptr v)
|
||||||
{
|
{
|
||||||
@ -169,6 +169,7 @@ static QBasicAtomicInteger<quint32> *futexLow32(QBasicAtomicInteger<quintptr> *p
|
|||||||
|
|
||||||
static QBasicAtomicInteger<quint32> *futexHigh32(QBasicAtomicInteger<quintptr> *ptr)
|
static QBasicAtomicInteger<quint32> *futexHigh32(QBasicAtomicInteger<quintptr> *ptr)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(futexHasWaiterCount);
|
||||||
auto result = reinterpret_cast<QBasicAtomicInteger<quint32> *>(ptr);
|
auto result = reinterpret_cast<QBasicAtomicInteger<quint32> *>(ptr);
|
||||||
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN && QT_POINTER_SIZE > 4
|
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN && QT_POINTER_SIZE > 4
|
||||||
++result;
|
++result;
|
||||||
@ -184,38 +185,16 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
|
|||||||
int n = int(unsigned(nn));
|
int n = int(unsigned(nn));
|
||||||
|
|
||||||
// we're called after one testAndSet, so start by waiting first
|
// we're called after one testAndSet, so start by waiting first
|
||||||
goto start_wait;
|
for (;;) {
|
||||||
|
|
||||||
forever {
|
|
||||||
if (futexAvailCounter(curValue) >= n) {
|
|
||||||
// try to acquire
|
|
||||||
quintptr newValue = curValue - nn;
|
|
||||||
if (u.testAndSetOrdered(curValue, newValue, curValue))
|
|
||||||
return true; // succeeded!
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// not enough tokens available, put us to wait
|
|
||||||
if (remainingTime == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// indicate we're waiting
|
// 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) {
|
if (n > 1 || !futexHasWaiterCount) {
|
||||||
u.fetchAndOrRelaxed(futexNeedsWakeAllBit);
|
u.fetchAndOrRelaxed(futexNeedsWakeAllBit);
|
||||||
curValue |= futexNeedsWakeAllBit;
|
curValue |= futexNeedsWakeAllBit;
|
||||||
if constexpr (futexHasWaiterCount) {
|
if constexpr (futexHasWaiterCount) {
|
||||||
if (n > 1) {
|
Q_ASSERT(n > 1);
|
||||||
ptr = futexHigh32(&u);
|
ptr = futexHigh32(&u);
|
||||||
// curValue >>= 32; // but this is UB in 32-bit, so roundabout:
|
curValue >>= 32;
|
||||||
curValue = quint64(curValue) >> 32;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,6 +209,17 @@ start_wait:
|
|||||||
curValue = u.loadAcquire();
|
curValue = u.loadAcquire();
|
||||||
if (IsTimed)
|
if (IsTimed)
|
||||||
remainingTime = timer.remainingTimeNSecs();
|
remainingTime = timer.remainingTimeNSecs();
|
||||||
|
|
||||||
|
// try to acquire
|
||||||
|
while (futexAvailCounter(curValue) >= n) {
|
||||||
|
quintptr newValue = curValue - nn;
|
||||||
|
if (u.testAndSetOrdered(curValue, newValue, curValue))
|
||||||
|
return true; // succeeded!
|
||||||
|
}
|
||||||
|
|
||||||
|
// not enough tokens available, put us to wait
|
||||||
|
if (remainingTime == 0)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,12 +242,14 @@ template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quintp
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// we need to wait
|
// we need to wait
|
||||||
quintptr oneWaiter = quintptr(Q_UINT64_C(1) << 32); // zero on 32-bit
|
constexpr quintptr oneWaiter = quintptr(Q_UINT64_C(1) << 32); // zero on 32-bit
|
||||||
if (futexHasWaiterCount) {
|
if constexpr (futexHasWaiterCount) {
|
||||||
// We don't use the fetched value from above so futexWait() fails if
|
// We don't use the fetched value from above so futexWait() fails if
|
||||||
// it changed after the testAndSetOrdered above.
|
// it changed after the testAndSetOrdered above.
|
||||||
if ((quint64(curValue) >> 32) == 0x7fffffff)
|
if (((curValue >> 32) & 0x7fffffffU) == 0x7fffffffU) {
|
||||||
return false; // overflow!
|
qCritical() << "Waiter count overflow in QSemaphore";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// increase the waiter count
|
// increase the waiter count
|
||||||
u.fetchAndAddRelaxed(oneWaiter);
|
u.fetchAndAddRelaxed(oneWaiter);
|
||||||
@ -270,6 +262,8 @@ template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quintp
|
|||||||
if (futexSemaphoreTryAcquire_loop<IsTimed>(u, curValue, nn, timeout))
|
if (futexSemaphoreTryAcquire_loop<IsTimed>(u, curValue, nn, timeout))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
Q_ASSERT(IsTimed);
|
||||||
|
|
||||||
if (futexHasWaiterCount) {
|
if (futexHasWaiterCount) {
|
||||||
// decrement the number of threads waiting
|
// decrement the number of threads waiting
|
||||||
Q_ASSERT(futexHigh32(&u)->loadRelaxed() & 0x7fffffffU);
|
Q_ASSERT(futexHigh32(&u)->loadRelaxed() & 0x7fffffffU);
|
||||||
@ -501,8 +495,6 @@ bool QSemaphore::tryAcquire(int n, int timeout)
|
|||||||
return false;
|
return false;
|
||||||
d->avail -= n;
|
d->avail -= n;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
Loading…
Reference in New Issue
Block a user