Port QWaitCondition to QDeadlineTimer

Since pthread_cond_timedwait takes absolute time instead of relative
time like most POSIX API, there's a small gain in performance here: we
avoid an extra system call to get the current time.

Task-number: QTBUG-64266
Change-Id: I25d85d86649448d5b2b3fffd1451138568091f50
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Thiago Macieira 2016-05-22 20:06:57 -07:00
parent 8c04a5e964
commit fd207c06b8
8 changed files with 60 additions and 25 deletions

View File

@ -39,7 +39,6 @@
#include "qdeadlinetimer.h" #include "qdeadlinetimer.h"
#include "qdeadlinetimer_p.h" #include "qdeadlinetimer_p.h"
#include <qpair.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE

View File

@ -43,6 +43,7 @@
#include <QtCore/qelapsedtimer.h> #include <QtCore/qelapsedtimer.h>
#include <QtCore/qmetatype.h> #include <QtCore/qmetatype.h>
#include <QtCore/qnamespace.h> #include <QtCore/qnamespace.h>
#include <QtCore/qpair.h>
#ifdef max #ifdef max
// un-pollute the namespace. We need std::numeric_limits::max() and std::chrono::duration::max() // un-pollute the namespace. We need std::numeric_limits::max() and std::chrono::duration::max()
@ -186,6 +187,10 @@ private:
unsigned type; unsigned type;
qint64 rawRemainingTimeNSecs() const Q_DECL_NOTHROW; qint64 rawRemainingTimeNSecs() const Q_DECL_NOTHROW;
public:
// This is not a public function, it's here only for Qt's internal convenience...
QPair<qint64, unsigned> _q_data() const { return qMakePair(t1, t2); }
}; };
Q_DECLARE_SHARED(QDeadlineTimer) Q_DECLARE_SHARED(QDeadlineTimer)

View File

@ -58,6 +58,7 @@
#include <QtCore/qnamespace.h> #include <QtCore/qnamespace.h>
#include <QtCore/qmutex.h> #include <QtCore/qmutex.h>
#include <QtCore/qatomic.h> #include <QtCore/qatomic.h>
#include <QtCore/qdeadlinetimer.h>
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
# include <mach/semaphore.h> # include <mach/semaphore.h>
@ -146,7 +147,7 @@ public:
// helper functions for qmutex_unix.cpp and qwaitcondition_unix.cpp // helper functions for qmutex_unix.cpp and qwaitcondition_unix.cpp
// they are in qwaitcondition_unix.cpp actually // they are in qwaitcondition_unix.cpp actually
void qt_initialize_pthread_cond(pthread_cond_t *cond, const char *where); void qt_initialize_pthread_cond(pthread_cond_t *cond, const char *where);
void qt_abstime_for_timeout(struct timespec *ts, int timeout); void qt_abstime_for_timeout(struct timespec *ts, QDeadlineTimer deadline);
#endif #endif
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -130,7 +130,7 @@ bool QMutexPrivate::wait(int timeout)
errorCode = pthread_cond_wait(&cond, &mutex); errorCode = pthread_cond_wait(&cond, &mutex);
} else { } else {
timespec ti; timespec ti;
qt_abstime_for_timeout(&ti, timeout); qt_abstime_for_timeout(&ti, QDeadlineTimer(timeout));
errorCode = pthread_cond_timedwait(&cond, &mutex, &ti); errorCode = pthread_cond_timedwait(&cond, &mutex, &ti);
} }
if (errorCode) { if (errorCode) {

View File

@ -424,11 +424,9 @@ bool QSemaphore::tryAcquire(int n, int timeout)
QDeadlineTimer timer(timeout); QDeadlineTimer timer(timeout);
QMutexLocker locker(&d->mutex); QMutexLocker locker(&d->mutex);
qint64 remainingTime = timer.remainingTime(); while (n > d->avail && !timer.hasExpired()) {
while (n > d->avail && remainingTime != 0) { if (!d->cond.wait(locker.mutex(), timer))
if (!d->cond.wait(locker.mutex(), remainingTime))
return false; return false;
remainingTime = timer.remainingTime();
} }
if (n > d->avail) if (n > d->avail)
return false; return false;

View File

@ -49,6 +49,7 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_THREAD #ifndef QT_NO_THREAD
class QDeadlineTimer;
class QWaitConditionPrivate; class QWaitConditionPrivate;
class QMutex; class QMutex;
class QReadWriteLock; class QReadWriteLock;
@ -59,8 +60,11 @@ public:
QWaitCondition(); QWaitCondition();
~QWaitCondition(); ~QWaitCondition();
// ### Qt 6: remove unsigned long overloads
bool wait(QMutex *lockedMutex, unsigned long time = ULONG_MAX); bool wait(QMutex *lockedMutex, unsigned long time = ULONG_MAX);
bool wait(QMutex *lockedMutex, QDeadlineTimer deadline);
bool wait(QReadWriteLock *lockedReadWriteLock, unsigned long time = ULONG_MAX); bool wait(QReadWriteLock *lockedReadWriteLock, unsigned long time = ULONG_MAX);
bool wait(QReadWriteLock *lockedReadWriteLock, QDeadlineTimer deadline);
void wakeOne(); void wakeOne();
void wakeAll(); void wakeAll();

View File

@ -44,6 +44,8 @@
#include "qreadwritelock.h" #include "qreadwritelock.h"
#include "qatomic.h" #include "qatomic.h"
#include "qstring.h" #include "qstring.h"
#include "qdeadlinetimer.h"
#include "private/qdeadlinetimer_p.h"
#include "qelapsedtimer.h" #include "qelapsedtimer.h"
#include "private/qcore_unix_p.h" #include "private/qcore_unix_p.h"
@ -93,23 +95,25 @@ void qt_initialize_pthread_cond(pthread_cond_t *cond, const char *where)
pthread_condattr_destroy(&condattr); pthread_condattr_destroy(&condattr);
} }
void qt_abstime_for_timeout(timespec *ts, int timeout) void qt_abstime_for_timeout(timespec *ts, QDeadlineTimer deadline)
{ {
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
// on Mac, qt_gettime() (on qelapsedtimer_mac.cpp) returns ticks related to the Mach absolute time // on Mac, qt_gettime() (on qelapsedtimer_mac.cpp) returns ticks related to the Mach absolute time
// that doesn't work with pthread // that doesn't work with pthread
// Mac also doesn't have clock_gettime // Mac also doesn't have clock_gettime
struct timeval tv; struct timeval tv;
qint64 nsec = deadline.remainingTimeNSecs();
gettimeofday(&tv, 0); gettimeofday(&tv, 0);
ts->tv_sec = tv.tv_sec; ts->tv_sec = tv.tv_sec + nsec / (1000 * 1000 * 1000);
ts->tv_nsec = tv.tv_usec * 1000; ts->tv_nsec = tv.tv_usec * 1000 + nsec % (1000 * 1000 * 1000);
#else
*ts = qt_gettime();
#endif
ts->tv_sec += timeout / 1000;
ts->tv_nsec += timeout % 1000 * Q_UINT64_C(1000) * 1000;
normalizedTimespec(*ts); normalizedTimespec(*ts);
#else
// depends on QDeadlineTimer's internals!!
Q_STATIC_ASSERT(QDeadlineTimerNanosecondsInT2);
ts->tv_sec = deadline._q_data().first;
ts->tv_nsec = deadline._q_data().second;
#endif
} }
class QWaitConditionPrivate { class QWaitConditionPrivate {
@ -119,26 +123,27 @@ public:
int waiters; int waiters;
int wakeups; int wakeups;
int wait_relative(unsigned long time) int wait_relative(QDeadlineTimer deadline)
{ {
timespec ti; timespec ti;
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
if (local_cond_timedwait_relative) { if (!local_condattr_setclock && local_cond_timedwait_relative) {
ti.tv_sec = time / 1000; qint64 nsec = deadline.remainingTimeNSecs();
ti.tv_nsec = time % 1000 * Q_UINT64_C(1000) * 1000; ti.tv_sec = nsec / (1000 * 1000 * 1000);
ti.tv_nsec = nsec - ti.tv_sec * 1000 * 1000 * 1000;
return local_cond_timedwait_relative(&cond, &mutex, &ti); return local_cond_timedwait_relative(&cond, &mutex, &ti);
} }
#endif #endif
qt_abstime_for_timeout(&ti, time); qt_abstime_for_timeout(&ti, deadline);
return pthread_cond_timedwait(&cond, &mutex, &ti); return pthread_cond_timedwait(&cond, &mutex, &ti);
} }
bool wait(unsigned long time) bool wait(QDeadlineTimer deadline)
{ {
int code; int code;
forever { forever {
if (time != ULONG_MAX) { if (!deadline.isForever()) {
code = wait_relative(time); code = wait_relative(deadline);
} else { } else {
code = pthread_cond_wait(&cond, &mutex); code = pthread_cond_wait(&cond, &mutex);
} }
@ -200,6 +205,13 @@ void QWaitCondition::wakeAll()
} }
bool QWaitCondition::wait(QMutex *mutex, unsigned long time) bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
{
if (time > std::numeric_limits<qint64>::max())
return wait(mutex, QDeadlineTimer(QDeadlineTimer::Forever));
return wait(mutex, QDeadlineTimer(time));
}
bool QWaitCondition::wait(QMutex *mutex, QDeadlineTimer deadline)
{ {
if (! mutex) if (! mutex)
return false; return false;
@ -212,7 +224,7 @@ bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
++d->waiters; ++d->waiters;
mutex->unlock(); mutex->unlock();
bool returnValue = d->wait(time); bool returnValue = d->wait(deadline);
mutex->lock(); mutex->lock();
@ -220,6 +232,11 @@ bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
} }
bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time) bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
{
return wait(readWriteLock, QDeadlineTimer(time));
}
bool QWaitCondition::wait(QReadWriteLock *readWriteLock, QDeadlineTimer deadline)
{ {
if (!readWriteLock) if (!readWriteLock)
return false; return false;
@ -236,7 +253,7 @@ bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
readWriteLock->unlock(); readWriteLock->unlock();
bool returnValue = d->wait(time); bool returnValue = d->wait(deadline);
if (previousState == QReadWriteLock::LockedForWrite) if (previousState == QReadWriteLock::LockedForWrite)
readWriteLock->lockForWrite(); readWriteLock->lockForWrite();

View File

@ -38,6 +38,7 @@
****************************************************************************/ ****************************************************************************/
#include "qwaitcondition.h" #include "qwaitcondition.h"
#include "qdeadlinetimer.h"
#include "qnamespace.h" #include "qnamespace.h"
#include "qmutex.h" #include "qmutex.h"
#include "qreadwritelock.h" #include "qreadwritelock.h"
@ -184,6 +185,11 @@ bool QWaitCondition::wait(QMutex *mutex, unsigned long time)
return returnValue; return returnValue;
} }
bool QWaitCondition::wait(QMutex *mutex, QDeadlineTimer deadline)
{
return wait(mutex, deadline.remainingTime());
}
bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time) bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
{ {
if (!readWriteLock) if (!readWriteLock)
@ -210,6 +216,11 @@ bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time)
return returnValue; return returnValue;
} }
bool QWaitCondition::wait(QReadWriteLock *readWriteLock, QDeadlineTimer deadline)
{
return wait(readWriteLock, deadline.remainingTime());
}
void QWaitCondition::wakeOne() void QWaitCondition::wakeOne()
{ {
// wake up the first waiting thread in the queue // wake up the first waiting thread in the queue