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:
parent
585db639a4
commit
96c76839f9
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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(); }
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user