Switch futex support to QDeadlineTimer

This allows us to use absolute times on Linux (today) and FreeBSD
(soon), plus simplifies both QMutex and QSemaphore.

Change-Id: I63b988479db546dabffcfffd17675a182aa528fa
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Thiago Macieira 2023-06-10 09:47:25 -07:00
parent 585db639a4
commit 96c76839f9
6 changed files with 19 additions and 29 deletions

View File

@ -35,6 +35,7 @@ QT_BEGIN_NAMESPACE
* - FUTEX_PRIVATE_FLAG 2.6.22 * - FUTEX_PRIVATE_FLAG 2.6.22
* - O_CLOEXEC 2.6.23 * - O_CLOEXEC 2.6.23
* - eventfd 2.6.23 * - eventfd 2.6.23
* - FUTEX_WAIT_BITSET 2.6.25
* - pipe2 & dup3 2.6.27 * - pipe2 & dup3 2.6.27
* - accept4 2.6.28 * - accept4 2.6.28
* - renameat2 3.16 QT_CONFIG(renameat2) * - renameat2 3.16 QT_CONFIG(renameat2)

View File

@ -16,10 +16,9 @@
// //
#include <private/qcore_unix_p.h> #include <private/qcore_unix_p.h>
#include <qdeadlinetimer.h>
#include <qtsan_impl.h> #include <qtsan_impl.h>
#include <chrono>
#include <asm/unistd.h> #include <asm/unistd.h>
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
@ -68,10 +67,12 @@ inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
_q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue)); _q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue));
} }
template <typename Atomic> template <typename Atomic>
inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, std::chrono::nanoseconds timeout) inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, QDeadlineTimer deadline)
{ {
auto timeout = deadline.deadline<std::chrono::steady_clock>().time_since_epoch();
struct timespec ts = durationToTimespec(timeout); struct timespec ts = durationToTimespec(timeout);
int r = _q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue), quintptr(&ts)); int r = _q_futex(addr(&futex), FUTEX_WAIT_BITSET, qintptr(expectedValue), quintptr(&ts),
nullptr, FUTEX_BITSET_MATCH_ANY);
return r == 0 || errno != ETIMEDOUT; return r == 0 || errno != ETIMEDOUT;
} }
template <typename Atomic> inline void futexWakeOne(Atomic &futex) template <typename Atomic> inline void futexWakeOne(Atomic &futex)

View File

@ -15,16 +15,15 @@
// We mean it. // We mean it.
// //
#include <qdeadlinetimer.h>
#include <private/qglobal_p.h> #include <private/qglobal_p.h>
#include <chrono>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
namespace QtDummyFutex { namespace QtDummyFutex {
constexpr inline bool futexAvailable() { return false; } constexpr inline bool futexAvailable() { return false; }
template <typename Atomic> template <typename Atomic>
inline bool futexWait(Atomic &, typename Atomic::Type, std::chrono::nanoseconds = {}) inline bool futexWait(Atomic &, typename Atomic::Type, QDeadlineTimer = {})
{ Q_UNREACHABLE_RETURN(false); } { Q_UNREACHABLE_RETURN(false); }
template <typename Atomic> inline void futexWakeOne(Atomic &) template <typename Atomic> inline void futexWakeOne(Atomic &)
{ Q_UNREACHABLE(); } { Q_UNREACHABLE(); }

View File

@ -16,10 +16,9 @@
// //
#include <private/qglobal_p.h> #include <private/qglobal_p.h>
#include <qdeadlinetimer.h>
#include <qtsan_impl.h> #include <qtsan_impl.h>
#include <chrono>
#include <qt_windows.h> #include <qt_windows.h>
#define QT_ALWAYS_USE_FUTEX #define QT_ALWAYS_USE_FUTEX
@ -37,12 +36,10 @@ inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
QtTsan::futexAcquire(&futex); QtTsan::futexAcquire(&futex);
} }
template <typename Atomic> template <typename Atomic>
inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, std::chrono::nanoseconds timeout) inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, QDeadlineTimer deadline)
{ {
using namespace std::chrono; using namespace std::chrono;
// Using ceil so that any non-zero timeout doesn't get trunated to 0ms BOOL r = WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), DWORD(deadline.remainingTime()));
auto msecs = ceil<milliseconds>(timeout);
BOOL r = WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), DWORD(msecs.count()));
return r || GetLastError() != ERROR_TIMEOUT; return r || GetLastError() != ERROR_TIMEOUT;
} }
template <typename Atomic> inline void futexWakeAll(Atomic &futex) template <typename Atomic> inline void futexWakeAll(Atomic &futex)

View File

@ -673,9 +673,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
*/ */
bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXCEPT bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXCEPT
{ {
using namespace std::chrono; if (deadlineTimer.hasExpired())
nanoseconds remainingTime = deadlineTimer.remainingTimeAsDuration();
if (remainingTime == 0ns)
return false; return false;
if (futexAvailable()) { if (futexAvailable()) {
@ -691,7 +689,7 @@ bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXC
return true; return true;
for (;;) { for (;;) {
if (!futexWait(d_ptr, dummyFutexValue(), remainingTime)) if (!futexWait(d_ptr, dummyFutexValue(), deadlineTimer))
return false; return false;
// We got woken up, so must try to acquire the mutex. We must set // We got woken up, so must try to acquire the mutex. We must set
@ -700,9 +698,7 @@ bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXC
if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr) if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
return true; return true;
// calculate the remaining time if (deadlineTimer.hasExpired())
remainingTime = deadlineTimer.remainingTimeAsDuration();
if (remainingTime == 0ns)
return false; return false;
} }
} }
@ -714,7 +710,7 @@ bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXC
continue; continue;
if (copy == dummyLocked()) { if (copy == dummyLocked()) {
if (remainingTime == 0ns) if (deadlineTimer.hasExpired())
return false; return false;
// The mutex is locked but does not have a QMutexPrivate yet. // The mutex is locked but does not have a QMutexPrivate yet.
// we need to allocate a QMutexPrivate // we need to allocate a QMutexPrivate
@ -729,7 +725,7 @@ bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXC
} }
QMutexPrivate *d = static_cast<QMutexPrivate *>(copy); QMutexPrivate *d = static_cast<QMutexPrivate *>(copy);
if (remainingTime == 0ns && !d->possiblyUnlocked.loadRelaxed()) if (deadlineTimer.hasExpired() && !d->possiblyUnlocked.loadRelaxed())
return false; return false;
// At this point we have a pointer to a QMutexPrivate. But the other thread // At this point we have a pointer to a QMutexPrivate. But the other thread
@ -791,7 +787,6 @@ bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXC
Q_ASSERT(d == d_ptr.loadRelaxed()); Q_ASSERT(d == d_ptr.loadRelaxed());
return true; return true;
} else { } else {
Q_ASSERT(remainingTime >= 0ns);
// timed out // timed out
d->derefWaiters(1); d->derefWaiters(1);
//There may be a race in which the mutex is unlocked right after we timed out, //There may be a race in which the mutex is unlocked right after we timed out,

View File

@ -149,7 +149,6 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
QDeadlineTimer timer) QDeadlineTimer timer)
{ {
using namespace std::chrono; using namespace std::chrono;
nanoseconds remainingTime = IsTimed ? timer.remainingTimeAsDuration() : -1ns;
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
@ -166,8 +165,8 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
} }
} }
if (IsTimed && remainingTime > 0ns) { if (IsTimed) {
bool timedout = !futexWait(*ptr, curValue, remainingTime); bool timedout = !futexWait(*ptr, curValue, timer);
if (timedout) if (timedout)
return false; return false;
} else { } else {
@ -175,8 +174,6 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
} }
curValue = u.loadAcquire(); curValue = u.loadAcquire();
if (IsTimed)
remainingTime = timer.remainingTimeAsDuration();
// try to acquire // try to acquire
while (futexAvailCounter(curValue) >= n) { while (futexAvailCounter(curValue) >= n) {
@ -186,7 +183,7 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
} }
// not enough tokens available, put us to wait // not enough tokens available, put us to wait
if (IsTimed && remainingTime == 0ns) if (IsTimed && timer.hasExpired())
return false; return false;
} }
} }