diff options
Diffstat (limited to 'src/corelib/thread')
-rw-r--r-- | src/corelib/thread/qatomic.h | 6 | ||||
-rw-r--r-- | src/corelib/thread/qbasicatomic.h | 1 | ||||
-rw-r--r-- | src/corelib/thread/qexception.cpp | 4 | ||||
-rw-r--r-- | src/corelib/thread/qexception.h | 6 | ||||
-rw-r--r-- | src/corelib/thread/qfutex_p.h | 142 | ||||
-rw-r--r-- | src/corelib/thread/qfutureinterface.h | 2 | ||||
-rw-r--r-- | src/corelib/thread/qfuturewatcher.h | 18 | ||||
-rw-r--r-- | src/corelib/thread/qfuturewatcher_p.h | 4 | ||||
-rw-r--r-- | src/corelib/thread/qmutex.h | 10 | ||||
-rw-r--r-- | src/corelib/thread/qmutex_linux.cpp | 83 | ||||
-rw-r--r-- | src/corelib/thread/qreadwritelock.h | 4 | ||||
-rw-r--r-- | src/corelib/thread/qresultstore.h | 4 | ||||
-rw-r--r-- | src/corelib/thread/qsemaphore.cpp | 131 | ||||
-rw-r--r-- | src/corelib/thread/qsemaphore.h | 5 | ||||
-rw-r--r-- | src/corelib/thread/qthread.h | 6 | ||||
-rw-r--r-- | src/corelib/thread/qthread_p.h | 2 | ||||
-rw-r--r-- | src/corelib/thread/qthreadpool.cpp | 2 | ||||
-rw-r--r-- | src/corelib/thread/qthreadpool.h | 2 | ||||
-rw-r--r-- | src/corelib/thread/thread.pri | 1 |
19 files changed, 342 insertions, 91 deletions
diff --git a/src/corelib/thread/qatomic.h b/src/corelib/thread/qatomic.h index f9eacbf6f0..f17c38e2fe 100644 --- a/src/corelib/thread/qatomic.h +++ b/src/corelib/thread/qatomic.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -168,9 +168,9 @@ class QAtomicPointer : public QBasicAtomicPointer<T> { public: #ifdef QT_BASIC_ATOMIC_HAS_CONSTRUCTORS - constexpr QAtomicPointer(T *value = 0) Q_DECL_NOTHROW : QBasicAtomicPointer<T>(value) {} + constexpr QAtomicPointer(T *value = nullptr) Q_DECL_NOTHROW : QBasicAtomicPointer<T>(value) {} #else - inline QAtomicPointer(T *value = 0) Q_DECL_NOTHROW + inline QAtomicPointer(T *value = nullptr) Q_DECL_NOTHROW { this->store(value); } diff --git a/src/corelib/thread/qbasicatomic.h b/src/corelib/thread/qbasicatomic.h index 24218e833a..92db7a6228 100644 --- a/src/corelib/thread/qbasicatomic.h +++ b/src/corelib/thread/qbasicatomic.h @@ -90,6 +90,7 @@ template <typename T> class QBasicAtomicInteger { public: + typedef T Type; typedef QAtomicOps<T> Ops; // static check that this is a valid integer Q_STATIC_ASSERT_X(QTypeInfo<T>::isIntegral, "template parameter is not an integral type"); diff --git a/src/corelib/thread/qexception.cpp b/src/corelib/thread/qexception.cpp index 62c2b608d8..e00181e64b 100644 --- a/src/corelib/thread/qexception.cpp +++ b/src/corelib/thread/qexception.cpp @@ -171,7 +171,7 @@ public: }; ExceptionHolder::ExceptionHolder(QException *exception) -: base(exception ? new Base(exception) : Q_NULLPTR) {} +: base(exception ? new Base(exception) : nullptr) {} ExceptionHolder::ExceptionHolder(const ExceptionHolder &other) : base(other.base) @@ -188,7 +188,7 @@ ExceptionHolder::~ExceptionHolder() QException *ExceptionHolder::exception() const { if (!base) - return Q_NULLPTR; + return nullptr; return base->exception; } diff --git a/src/corelib/thread/qexception.h b/src/corelib/thread/qexception.h index b14d386c69..5c43cedffd 100644 --- a/src/corelib/thread/qexception.h +++ b/src/corelib/thread/qexception.h @@ -80,8 +80,8 @@ public: throw() #endif ; - void raise() const Q_DECL_OVERRIDE; - QUnhandledException *clone() const Q_DECL_OVERRIDE; + void raise() const override; + QUnhandledException *clone() const override; }; namespace QtPrivate { @@ -90,7 +90,7 @@ class Base; class Q_CORE_EXPORT ExceptionHolder { public: - ExceptionHolder(QException *exception = Q_NULLPTR); + ExceptionHolder(QException *exception = nullptr); ExceptionHolder(const ExceptionHolder &other); void operator=(const ExceptionHolder &other); // ### Qt6: copy-assign operator shouldn't return void. Remove this method and the copy-ctor, they are unneeded. ~ExceptionHolder(); diff --git a/src/corelib/thread/qfutex_p.h b/src/corelib/thread/qfutex_p.h new file mode 100644 index 0000000000..483664c783 --- /dev/null +++ b/src/corelib/thread/qfutex_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFUTEX_P_H +#define QFUTEX_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <qglobal.h> + +QT_BEGIN_NAMESPACE + +namespace QtDummyFutex { + Q_DECL_CONSTEXPR inline bool futexAvailable() { return false; } + template <typename Atomic> + inline bool futexWait(Atomic &, typename Atomic::Type, int = 0) + { Q_UNREACHABLE(); return false; } + template <typename Atomic> inline void futexWakeOne(Atomic &) + { Q_UNREACHABLE(); } + template <typename Atomic> inline void futexWakeAll(Atomic &) + { Q_UNREACHABLE(); } +} + +QT_END_NAMESPACE + +#if defined(Q_OS_LINUX) && !defined(QT_LINUXBASE) +// use Linux mutexes everywhere except for LSB builds +# include <sys/syscall.h> +# include <errno.h> +# include <limits.h> +# include <unistd.h> +# include <asm/unistd.h> +# include <linux/futex.h> +# define QT_ALWAYS_USE_FUTEX + +// if not defined in linux/futex.h +# define FUTEX_PRIVATE_FLAG 128 // added in v2.6.22 + +QT_BEGIN_NAMESPACE +namespace QtLinuxFutex { + constexpr inline bool futexAvailable() { return true; } + inline int _q_futex(int *addr, int op, int val, quintptr val2 = 0, + int *addr2 = nullptr, int val3 = 0) Q_DECL_NOTHROW + { + // we use __NR_futex because some libcs (like Android's bionic) don't + // provide SYS_futex etc. + return syscall(__NR_futex, addr, op | FUTEX_PRIVATE_FLAG, val, val2, addr2, val3); + } + template <typename T> int *addr(T *ptr) + { + int *int_addr = reinterpret_cast<int *>(ptr); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + if (sizeof(T) > sizeof(int)) + int_addr++; //We want a pointer to the least significant half +#endif + return int_addr; + } + + template <typename Atomic> + inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue) + { + _q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue)); + } + template <typename Atomic> + inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, qint64 nstimeout) + { + struct timespec ts; + ts.tv_sec = nstimeout / 1000 / 1000 / 1000; + ts.tv_nsec = nstimeout % (1000 * 1000 * 1000); + int r = _q_futex(addr(&futex), FUTEX_WAIT, qintptr(expectedValue), quintptr(&ts)); + return r == 0 || errno != ETIMEDOUT; + } + template <typename Atomic> inline void futexWakeOne(Atomic &futex) + { + _q_futex(addr(&futex), FUTEX_WAKE, 1); + } + template <typename Atomic> inline void futexWakeAll(Atomic &futex) + { + _q_futex(addr(&futex), FUTEX_WAKE, INT_MAX); + } + template <typename Atomic> inline + void futexWakeOp(Atomic &futex1, int wake1, int wake2, Atomic &futex2, quint32 op) + { + _q_futex(addr(&futex1), FUTEX_WAKE_OP, wake1, wake2, addr(&futex2), op); + } +} +namespace QtFutex = QtLinuxFutex; +QT_END_NAMESPACE + +#else + +QT_BEGIN_NAMESPACE +namespace QtFutex = QtDummyFutex; +QT_END_NAMESPACE +#endif + +#endif // QFUTEX_P_H diff --git a/src/corelib/thread/qfutureinterface.h b/src/corelib/thread/qfutureinterface.h index 7b12f51e3e..320c2d7b33 100644 --- a/src/corelib/thread/qfutureinterface.h +++ b/src/corelib/thread/qfutureinterface.h @@ -291,7 +291,7 @@ public: void reportResult(const void *, int) { } void reportResults(const QVector<void> &, int) { } - void reportFinished(const void * = Q_NULLPTR) { QFutureInterfaceBase::reportFinished(); } + void reportFinished(const void * = nullptr) { QFutureInterfaceBase::reportFinished(); } }; QT_END_NAMESPACE diff --git a/src/corelib/thread/qfuturewatcher.h b/src/corelib/thread/qfuturewatcher.h index 3357e27037..5143db8d2e 100644 --- a/src/corelib/thread/qfuturewatcher.h +++ b/src/corelib/thread/qfuturewatcher.h @@ -58,7 +58,7 @@ class Q_CORE_EXPORT QFutureWatcherBase : public QObject Q_DECLARE_PRIVATE(QFutureWatcherBase) public: - explicit QFutureWatcherBase(QObject *parent = Q_NULLPTR); + explicit QFutureWatcherBase(QObject *parent = nullptr); // de-inline dtor int progressValue() const; @@ -76,7 +76,7 @@ public: void setPendingResultsLimit(int limit); - bool event(QEvent *event) Q_DECL_OVERRIDE; + bool event(QEvent *event) override; Q_SIGNALS: void started(); @@ -98,8 +98,8 @@ public Q_SLOTS: void togglePaused(); protected: - void connectNotify (const QMetaMethod &signal) Q_DECL_OVERRIDE; - void disconnectNotify (const QMetaMethod &signal) Q_DECL_OVERRIDE; + void connectNotify (const QMetaMethod &signal) override; + void disconnectNotify (const QMetaMethod &signal) override; // called from setFuture() implemented in template sub-classes void connectOutputInterface(); @@ -166,8 +166,8 @@ public Q_SLOTS: private: QFuture<T> m_future; - const QFutureInterfaceBase &futureInterface() const Q_DECL_OVERRIDE { return m_future.d; } - QFutureInterfaceBase &futureInterface() Q_DECL_OVERRIDE { return m_future.d; } + const QFutureInterfaceBase &futureInterface() const override { return m_future.d; } + QFutureInterfaceBase &futureInterface() override { return m_future.d; } }; template <typename T> @@ -185,7 +185,7 @@ template <> class QFutureWatcher<void> : public QFutureWatcherBase { public: - explicit QFutureWatcher(QObject *_parent = Q_NULLPTR) + explicit QFutureWatcher(QObject *_parent = nullptr) : QFutureWatcherBase(_parent) { } ~QFutureWatcher() @@ -197,8 +197,8 @@ public: private: QFuture<void> m_future; - const QFutureInterfaceBase &futureInterface() const Q_DECL_OVERRIDE { return m_future.d; } - QFutureInterfaceBase &futureInterface() Q_DECL_OVERRIDE { return m_future.d; } + const QFutureInterfaceBase &futureInterface() const override { return m_future.d; } + QFutureInterfaceBase &futureInterface() override { return m_future.d; } }; Q_INLINE_TEMPLATE void QFutureWatcher<void>::setFuture(const QFuture<void> &_future) diff --git a/src/corelib/thread/qfuturewatcher_p.h b/src/corelib/thread/qfuturewatcher_p.h index e83bdaf45a..25d1ef8d25 100644 --- a/src/corelib/thread/qfuturewatcher_p.h +++ b/src/corelib/thread/qfuturewatcher_p.h @@ -69,8 +69,8 @@ class QFutureWatcherBasePrivate : public QObjectPrivate, public: QFutureWatcherBasePrivate(); - void postCallOutEvent(const QFutureCallOutEvent &callOutEvent) Q_DECL_OVERRIDE; - void callOutInterfaceDisconnected() Q_DECL_OVERRIDE; + void postCallOutEvent(const QFutureCallOutEvent &callOutEvent) override; + void callOutInterfaceDisconnected() override; void sendCallOutEvent(QFutureCallOutEvent *event); diff --git a/src/corelib/thread/qmutex.h b/src/corelib/thread/qmutex.h index 12987f16c3..f40dd3c814 100644 --- a/src/corelib/thread/qmutex.h +++ b/src/corelib/thread/qmutex.h @@ -92,16 +92,16 @@ public: private: inline bool fastTryLock() Q_DECL_NOTHROW { - return d_ptr.testAndSetAcquire(Q_NULLPTR, dummyLocked()); + return d_ptr.testAndSetAcquire(nullptr, dummyLocked()); } inline bool fastTryUnlock() Q_DECL_NOTHROW { - return d_ptr.testAndSetRelease(dummyLocked(), Q_NULLPTR); + return d_ptr.testAndSetRelease(dummyLocked(), nullptr); } inline bool fastTryLock(QMutexData *¤t) Q_DECL_NOTHROW { - return d_ptr.testAndSetAcquire(Q_NULLPTR, dummyLocked(), current); + return d_ptr.testAndSetAcquire(nullptr, dummyLocked(), current); } inline bool fastTryUnlock(QMutexData *¤t) Q_DECL_NOTHROW { - return d_ptr.testAndSetRelease(dummyLocked(), Q_NULLPTR, current); + return d_ptr.testAndSetRelease(dummyLocked(), nullptr, current); } void lockInternal() QT_MUTEX_LOCK_NOEXCEPT; @@ -287,7 +287,7 @@ public: inline void unlock() Q_DECL_NOTHROW {} void relock() Q_DECL_NOTHROW {} - inline QMutex *mutex() const Q_DECL_NOTHROW { return Q_NULLPTR; } + inline QMutex *mutex() const Q_DECL_NOTHROW { return nullptr; } private: Q_DISABLE_COPY(QMutexLocker) diff --git a/src/corelib/thread/qmutex_linux.cpp b/src/corelib/thread/qmutex_linux.cpp index 5f6e74ac6f..d3d97ea108 100644 --- a/src/corelib/thread/qmutex_linux.cpp +++ b/src/corelib/thread/qmutex_linux.cpp @@ -44,15 +44,9 @@ #ifndef QT_NO_THREAD #include "qatomic.h" #include "qmutex_p.h" -#include "qelapsedtimer.h" +#include "qfutex_p.h" -#include <linux/futex.h> -#include <sys/syscall.h> -#include <unistd.h> -#include <errno.h> -#include <asm/unistd.h> - -#ifndef QT_LINUX_FUTEX +#ifndef QT_ALWAYS_USE_FUTEX # error "Qt build is broken: qmutex_linux.cpp is being built but futex support is not wanted" #endif @@ -63,6 +57,8 @@ QT_BEGIN_NAMESPACE +using namespace QtFutex; + /* * QBasicMutex implementation on Linux with futexes * @@ -107,20 +103,6 @@ QT_BEGIN_NAMESPACE * waiting in the past. We then set the mutex to 0x0 and perform a FUTEX_WAKE. */ -static inline int _q_futex(void *addr, int op, int val, const struct timespec *timeout) Q_DECL_NOTHROW -{ - volatile int *int_addr = reinterpret_cast<volatile int *>(addr); -#if Q_BYTE_ORDER == Q_BIG_ENDIAN && QT_POINTER_SIZE == 8 - int_addr++; //We want a pointer to the 32 least significant bit of QMutex::d -#endif - int *addr2 = 0; - int val2 = 0; - - // we use __NR_futex because some libcs (like Android's bionic) don't - // provide SYS_futex etc. - return syscall(__NR_futex, int_addr, op | FUTEX_PRIVATE_FLAG, val, timeout, addr2, val2); -} - static inline QMutexData *dummyFutexValue() { return reinterpret_cast<QMutexData *>(quintptr(3)); @@ -136,36 +118,38 @@ bool lockInternal_helper(QBasicAtomicPointer<QMutexData> &d_ptr, int timeout = - if (timeout == 0) return false; - struct timespec ts, *pts = 0; - if (IsTimed && timeout > 0) { - ts.tv_sec = timeout / 1000; - ts.tv_nsec = (timeout % 1000) * 1000 * 1000; - } - // the mutex is locked already, set a bit indicating we're waiting - while (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) != 0) { - if (IsTimed && pts == &ts) { - // recalculate the timeout - qint64 xtimeout = qint64(timeout) * 1000 * 1000; - xtimeout -= elapsedTimer->nsecsElapsed(); - if (xtimeout <= 0) { - // timer expired after we returned - return false; - } - ts.tv_sec = xtimeout / Q_INT64_C(1000) / 1000 / 1000; - ts.tv_nsec = xtimeout % (Q_INT64_C(1000) * 1000 * 1000); - } - if (IsTimed && timeout > 0) - pts = &ts; + if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr) + return true; + qint64 nstimeout = timeout * Q_INT64_C(1000) * 1000; + qint64 remainingTime = nstimeout; + forever { // successfully set the waiting bit, now sleep - int r = _q_futex(&d_ptr, FUTEX_WAIT, quintptr(dummyFutexValue()), pts); - if (IsTimed && r != 0 && errno == ETIMEDOUT) - return false; + if (IsTimed && nstimeout >= 0) { + bool r = futexWait(d_ptr, dummyFutexValue(), remainingTime); + if (!r) + return false; + + // we got woken up, so try to acquire the mutex + // note we must set to dummyFutexValue because there could be other threads + // also waiting + if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr) + return true; - // we got woken up, so try to acquire the mutex - // note we must set to dummyFutexValue because there could be other threads - // also waiting + // recalculate the timeout + remainingTime = nstimeout - elapsedTimer->nsecsElapsed(); + if (remainingTime <= 0) + return false; + } else { + futexWait(d_ptr, dummyFutexValue()); + + // we got woken up, so try to acquire the mutex + // note we must set to dummyFutexValue because there could be other threads + // also waiting + if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr) + return true; + } } Q_ASSERT(d_ptr.load()); @@ -195,10 +179,9 @@ void QBasicMutex::unlockInternal() Q_DECL_NOTHROW Q_ASSERT(!isRecursive()); d_ptr.storeRelease(0); - _q_futex(&d_ptr, FUTEX_WAKE, 1, 0); + futexWakeOne(d_ptr); } - QT_END_NAMESPACE #endif // QT_NO_THREAD diff --git a/src/corelib/thread/qreadwritelock.h b/src/corelib/thread/qreadwritelock.h index 777efdb3bf..ecdb98f2f5 100644 --- a/src/corelib/thread/qreadwritelock.h +++ b/src/corelib/thread/qreadwritelock.h @@ -205,7 +205,7 @@ public: static inline void unlock() Q_DECL_NOTHROW { } static inline void relock() Q_DECL_NOTHROW { } - static inline QReadWriteLock *readWriteLock() Q_DECL_NOTHROW { return Q_NULLPTR; } + static inline QReadWriteLock *readWriteLock() Q_DECL_NOTHROW { return nullptr; } private: Q_DISABLE_COPY(QReadLocker) @@ -219,7 +219,7 @@ public: static inline void unlock() Q_DECL_NOTHROW { } static inline void relock() Q_DECL_NOTHROW { } - static inline QReadWriteLock *readWriteLock() Q_DECL_NOTHROW { return Q_NULLPTR; } + static inline QReadWriteLock *readWriteLock() Q_DECL_NOTHROW { return nullptr; } private: Q_DISABLE_COPY(QWriteLocker) diff --git a/src/corelib/thread/qresultstore.h b/src/corelib/thread/qresultstore.h index 6c814ef854..5f0cefa133 100644 --- a/src/corelib/thread/qresultstore.h +++ b/src/corelib/thread/qresultstore.h @@ -67,8 +67,8 @@ class ResultItem public: ResultItem(const void *_result, int _count) : m_count(_count), result(_result) { } // contruct with vector of results ResultItem(const void *_result) : m_count(0), result(_result) { } // construct with result - ResultItem() : m_count(0), result(Q_NULLPTR) { } - bool isValid() const { return result != Q_NULLPTR; } + ResultItem() : m_count(0), result(nullptr) { } + bool isValid() const { return result != nullptr; } bool isVector() const { return m_count != 0; } int count() const { return (m_count == 0) ? 1 : m_count; } int m_count; // result is either a pointer to a result or to a vector of results, diff --git a/src/corelib/thread/qsemaphore.cpp b/src/corelib/thread/qsemaphore.cpp index 96c031eec6..fa63bb858f 100644 --- a/src/corelib/thread/qsemaphore.cpp +++ b/src/corelib/thread/qsemaphore.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2017 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -41,12 +42,15 @@ #ifndef QT_NO_THREAD #include "qmutex.h" +#include "qfutex_p.h" #include "qwaitcondition.h" #include "qdeadlinetimer.h" #include "qdatetime.h" QT_BEGIN_NAMESPACE +using namespace QtFutex; + /*! \class QSemaphore \inmodule QtCore @@ -97,6 +101,68 @@ QT_BEGIN_NAMESPACE \sa QSemaphoreReleaser, QMutex, QWaitCondition, QThread, {Semaphores Example} */ +/* + QSemaphore futex operation + + QSemaphore stores a 32-bit integer with the counter of currently available + tokens (value between 0 and INT_MAX). When a thread attempts to acquire n + tokens and the counter is larger than that, we perform a compare-and-swap + with the new count. If that succeeds, the acquisition worked; if not, we + loop again because the counter changed. If there were not enough tokens, + we'll perform a futex-wait. + + Before we do, we set the high bit in the futex to indicate that semaphore + is contended: that is, there's a thread waiting for more tokens. On + release() for n tokens, we perform a fetch-and-add of n and then check if + that high bit was set. If it was, then we clear that bit and perform a + futex-wake on the semaphore to indicate the waiting threads can wake up and + acquire tokens. Which ones get woken up is unspecified. + */ +static const quint32 futexContendedBit = 1U << 31; + +static int futexAvailCounter(quint32 v) +{ + // the low 31 bits + return int(v) & (futexContendedBit - 1); +} + +template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quint32> &u, int n, int timeout) +{ + QDeadlineTimer timer(IsTimed ? QDeadlineTimer(timeout) : QDeadlineTimer()); + quint32 curValue = u.loadAcquire(); + qint64 remainingTime = timeout * Q_INT64_C(1000) * 1000; + forever { + int available = futexAvailCounter(curValue); + if (available >= n) { + // try to acquire + quint32 newValue = curValue - n; + if (u.testAndSetOrdered(curValue, newValue, curValue)) + return true; // succeeded! + continue; + } + + // not enough tokens available, put us to wait + if (remainingTime == 0) + return false; + + // set the contended bit + u.fetchAndOrRelaxed(futexContendedBit); + curValue |= futexContendedBit; + + if (IsTimed && remainingTime > 0) { + bool timedout = !futexWait(u, curValue, remainingTime); + if (timedout) + return false; + } else { + futexWait(u, curValue); + } + + curValue = u.loadAcquire(); + if (IsTimed) + remainingTime = timer.remainingTimeNSecs(); + } +} + class QSemaphorePrivate { public: inline QSemaphorePrivate(int n) : avail(n) { } @@ -116,7 +182,10 @@ public: QSemaphore::QSemaphore(int n) { Q_ASSERT_X(n >= 0, "QSemaphore", "parameter 'n' must be non-negative"); - d = new QSemaphorePrivate(n); + if (futexAvailable()) + u.store(n); + else + d = new QSemaphorePrivate(n); } /*! @@ -126,7 +195,10 @@ QSemaphore::QSemaphore(int n) undefined behavior. */ QSemaphore::~QSemaphore() -{ delete d; } +{ + if (!futexAvailable()) + delete d; +} /*! Tries to acquire \c n resources guarded by the semaphore. If \a n @@ -138,6 +210,12 @@ QSemaphore::~QSemaphore() void QSemaphore::acquire(int n) { Q_ASSERT_X(n >= 0, "QSemaphore::acquire", "parameter 'n' must be non-negative"); + + if (futexAvailable()) { + futexSemaphoreTryAcquire<false>(u, n, -1); + return; + } + QMutexLocker locker(&d->mutex); while (n > d->avail) d->cond.wait(locker.mutex()); @@ -160,6 +238,42 @@ void QSemaphore::acquire(int n) void QSemaphore::release(int n) { Q_ASSERT_X(n >= 0, "QSemaphore::release", "parameter 'n' must be non-negative"); + + if (futexAvailable()) { + quint32 prevValue = u.fetchAndAddRelease(n); + if (prevValue & futexContendedBit) { +#ifdef FUTEX_OP + /* + We'll ask the kernel to wake up and clear the bit for us. + + atomic { + int oldval = u; + u = oldval & ~(1 << 31); + futexWake(u, INT_MAX); + if (oldval == 0) // impossible condition + futexWake(u, INT_MAX); + } + */ + quint32 op = FUTEX_OP_ANDN | FUTEX_OP_OPARG_SHIFT; + quint32 oparg = 31; + quint32 cmp = FUTEX_OP_CMP_EQ; + quint32 cmparg = 0; + futexWakeOp(u, INT_MAX, INT_MAX, u, FUTEX_OP(op, oparg, cmp, cmparg)); +#else + // Unset the bit and wake everyone. There are two possibibilies + // 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 + // its acquisition anyway, so it has to wait; + // 2) it did not see the new counter value, in which case its + // futexWait will fail. + u.fetchAndAndRelease(futexContendedBit - 1); + futexWakeAll(u); +#endif + } + return; + } + QMutexLocker locker(&d->mutex); d->avail += n; d->cond.wakeAll(); @@ -173,6 +287,9 @@ void QSemaphore::release(int n) */ int QSemaphore::available() const { + if (futexAvailable()) + return futexAvailCounter(u.load()); + QMutexLocker locker(&d->mutex); return d->avail; } @@ -191,6 +308,10 @@ int QSemaphore::available() const bool QSemaphore::tryAcquire(int n) { Q_ASSERT_X(n >= 0, "QSemaphore::tryAcquire", "parameter 'n' must be non-negative"); + + if (futexAvailable()) + return futexSemaphoreTryAcquire<false>(u, n, 0); + QMutexLocker locker(&d->mutex); if (n > d->avail) return false; @@ -217,8 +338,8 @@ bool QSemaphore::tryAcquire(int n) bool QSemaphore::tryAcquire(int n, int timeout) { Q_ASSERT_X(n >= 0, "QSemaphore::tryAcquire", "parameter 'n' must be non-negative"); - if (timeout < 0) - return tryAcquire(n); + if (futexAvailable()) + return futexSemaphoreTryAcquire<true>(u, n, timeout < 0 ? -1 : timeout); QDeadlineTimer timer(timeout); QMutexLocker locker(&d->mutex); diff --git a/src/corelib/thread/qsemaphore.h b/src/corelib/thread/qsemaphore.h index a92740c8ce..03ffa033d8 100644 --- a/src/corelib/thread/qsemaphore.h +++ b/src/corelib/thread/qsemaphore.h @@ -66,7 +66,10 @@ public: private: Q_DISABLE_COPY(QSemaphore) - QSemaphorePrivate *d; + union { + QSemaphorePrivate *d; + QBasicAtomicInteger<quint32> u; + }; }; class QSemaphoreReleaser diff --git a/src/corelib/thread/qthread.h b/src/corelib/thread/qthread.h index 03b5424bb6..1f98cb59af 100644 --- a/src/corelib/thread/qthread.h +++ b/src/corelib/thread/qthread.h @@ -76,7 +76,7 @@ public: static int idealThreadCount() Q_DECL_NOTHROW; static void yieldCurrentThread(); - explicit QThread(QObject *parent = Q_NULLPTR); + explicit QThread(QObject *parent = nullptr); ~QThread(); enum Priority { @@ -110,7 +110,7 @@ public: QAbstractEventDispatcher *eventDispatcher() const; void setEventDispatcher(QAbstractEventDispatcher *eventDispatcher); - bool event(QEvent *event) Q_DECL_OVERRIDE; + bool event(QEvent *event) override; int loopLevel() const; #ifdef Q_QDOC @@ -154,7 +154,7 @@ protected: static void setTerminationEnabled(bool enabled = true); protected: - QThread(QThreadPrivate &dd, QObject *parent = Q_NULLPTR); + QThread(QThreadPrivate &dd, QObject *parent = nullptr); private: Q_DECLARE_PRIVATE(QThread) diff --git a/src/corelib/thread/qthread_p.h b/src/corelib/thread/qthread_p.h index f3d4750177..1d38eb0ebf 100644 --- a/src/corelib/thread/qthread_p.h +++ b/src/corelib/thread/qthread_p.h @@ -317,7 +317,7 @@ public: void init(); private: - void run() Q_DECL_OVERRIDE; + void run() override; }; QT_END_NAMESPACE diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp index ba46d98cf9..fd5a1106a0 100644 --- a/src/corelib/thread/qthreadpool.cpp +++ b/src/corelib/thread/qthreadpool.cpp @@ -56,7 +56,7 @@ class QThreadPoolThread : public QThread { public: QThreadPoolThread(QThreadPoolPrivate *manager); - void run() Q_DECL_OVERRIDE; + void run() override; void registerThreadInactive(); QWaitCondition runnableReady; diff --git a/src/corelib/thread/qthreadpool.h b/src/corelib/thread/qthreadpool.h index a65eacc996..606e192768 100644 --- a/src/corelib/thread/qthreadpool.h +++ b/src/corelib/thread/qthreadpool.h @@ -62,7 +62,7 @@ class Q_CORE_EXPORT QThreadPool : public QObject friend class QFutureInterfaceBase; public: - QThreadPool(QObject *parent = Q_NULLPTR); + QThreadPool(QObject *parent = nullptr); ~QThreadPool(); static QThreadPool *globalInstance(); diff --git a/src/corelib/thread/thread.pri b/src/corelib/thread/thread.pri index 0a989cfcaf..a4cb2478c6 100644 --- a/src/corelib/thread/thread.pri +++ b/src/corelib/thread/thread.pri @@ -22,6 +22,7 @@ HEADERS += thread/qmutex.h \ # private headers HEADERS += thread/qmutex_p.h \ thread/qmutexpool_p.h \ + thread/qfutex_p.h \ thread/qfutureinterface_p.h \ thread/qfuturewatcher_p.h \ thread/qorderedmutexlocker_p.h \ |