diff options
Diffstat (limited to 'src/corelib/thread/qmutex.cpp')
-rw-r--r-- | src/corelib/thread/qmutex.cpp | 183 |
1 files changed, 121 insertions, 62 deletions
diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp index ba97ef8a42..ec6c711a4f 100644 --- a/src/corelib/thread/qmutex.cpp +++ b/src/corelib/thread/qmutex.cpp @@ -1,50 +1,13 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Copyright (C) 2012 Olivier Goffart <ogoffart@woboq.com> -** 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$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2016 Intel Corporation. +// Copyright (C) 2012 Olivier Goffart <ogoffart@woboq.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "global/qglobal.h" #include "qplatformdefs.h" #include "qmutex.h" #include <qdebug.h> #include "qatomic.h" -#include "qelapsedtimer.h" #include "qfutex_p.h" #include "qthread.h" #include "qmutex_p.h" @@ -182,6 +145,23 @@ void QBasicMutex::destroyInternal(QMutexPrivate *d) \sa lock(), unlock() */ +/*! \fn bool QMutex::tryLock(QDeadlineTimer timer) + \since 6.6 + + Attempts to lock the mutex. This function returns \c true if the lock + was obtained; otherwise it returns \c false. If another thread has + locked the mutex, this function will wait until \a timer expires + for the mutex to become available. + + If the lock was obtained, the mutex must be unlocked with unlock() + before another thread can successfully lock it. + + Calling this function multiple times on the same mutex from the + same thread will cause a \e dead-lock. + + \sa lock(), unlock() +*/ + /*! \fn bool QMutex::tryLock() \overload @@ -315,6 +295,8 @@ QRecursiveMutex::~QRecursiveMutex() */ /*! + \fn QRecursiveMutex::tryLock(int timeout) + Attempts to lock the mutex. This function returns \c true if the lock was obtained; otherwise it returns \c false. If another thread has locked the mutex, this function will wait for at most \a timeout @@ -332,16 +314,37 @@ QRecursiveMutex::~QRecursiveMutex() \sa lock(), unlock() */ -bool QRecursiveMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT + +/*! + \since 6.6 + + Attempts to lock the mutex. This function returns \c true if the lock + was obtained; otherwise it returns \c false. If another thread has + locked the mutex, this function will wait until \a timeout expires + for the mutex to become available. + + If the lock was obtained, the mutex must be unlocked with unlock() + before another thread can successfully lock it. + + Calling this function multiple times on the same mutex from the + same thread is allowed. + + \sa lock(), unlock() +*/ +bool QRecursiveMutex::tryLock(QDeadlineTimer timeout) QT_MUTEX_LOCK_NOEXCEPT { + unsigned tsanFlags = QtTsan::MutexWriteReentrant | QtTsan::TryLock; + QtTsan::mutexPreLock(this, tsanFlags); + Qt::HANDLE self = QThread::currentThreadId(); if (owner.loadRelaxed() == self) { ++count; Q_ASSERT_X(count != 0, "QMutex::lock", "Overflow in recursion counter"); + QtTsan::mutexPostLock(this, tsanFlags, 0); return true; } bool success = true; - if (timeout == -1) { + if (timeout.isForever()) { mutex.lock(); } else { success = mutex.tryLock(timeout); @@ -349,6 +352,11 @@ bool QRecursiveMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT if (success) owner.storeRelaxed(self); + else + tsanFlags |= QtTsan::TryLockFailed; + + QtTsan::mutexPostLock(this, tsanFlags, 0); + return success; } @@ -412,6 +420,7 @@ bool QRecursiveMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT void QRecursiveMutex::unlock() noexcept { Q_ASSERT(owner.loadRelaxed() == QThread::currentThreadId()); + QtTsan::mutexPreUnlock(this, 0u); if (count > 0) { count--; @@ -419,6 +428,8 @@ void QRecursiveMutex::unlock() noexcept owner.storeRelaxed(nullptr); mutex.unlock(); } + + QtTsan::mutexPostUnlock(this, 0u); } @@ -486,6 +497,40 @@ void QRecursiveMutex::unlock() noexcept */ /*! + \fn template <typename Mutex> QMutexLocker<Mutex>::QMutexLocker(QMutexLocker &&other) noexcept + \since 6.4 + + Move-constructs a QMutexLocker from \a other. The mutex and the + state of \a other is transferred to the newly constructed instance. + After the move, \a other will no longer be managing any mutex. + + \sa QMutex::lock() +*/ + +/*! + \fn template <typename Mutex> QMutexLocker<Mutex> &QMutexLocker<Mutex>::operator=(QMutexLocker &&other) noexcept + \since 6.4 + + Move-assigns \a other onto this QMutexLocker. If this QMutexLocker + was holding a locked mutex before the assignment, the mutex will be + unlocked. The mutex and the state of \a other is then transferred + to this QMutexLocker. After the move, \a other will no longer be + managing any mutex. + + \sa QMutex::lock() +*/ + +/*! + \fn template <typename Mutex> void QMutexLocker<Mutex>::swap(QMutexLocker &other) noexcept + \since 6.4 + + Swaps the mutex and the state of this QMutexLocker with \a other. + This operation is very fast and never fails. + + \sa QMutex::lock() +*/ + +/*! \fn template <typename Mutex> QMutexLocker<Mutex>::~QMutexLocker() noexcept Destroys the QMutexLocker and unlocks the mutex that was locked @@ -495,6 +540,14 @@ void QRecursiveMutex::unlock() noexcept */ /*! + \fn template <typename Mutex> bool QMutexLocker<Mutex>::isLocked() const noexcept + \since 6.4 + + Returns true if this QMutexLocker is currently locking its associated + mutex, or false otherwise. +*/ + +/*! \fn template <typename Mutex> void QMutexLocker<Mutex>::unlock() noexcept Unlocks this mutex locker. You can use \c relock() to lock @@ -604,27 +657,38 @@ void QBasicMutex::lockInternal() QT_MUTEX_LOCK_NOEXCEPT /*! \internal helper for lock(int) */ +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT { if (timeout == 0) return false; + return lockInternal(QDeadlineTimer(timeout)); +} +#endif + +/*! + \internal helper for tryLock(QDeadlineTimer) + */ +bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXCEPT +{ + if (deadlineTimer.hasExpired()) + return false; + if (futexAvailable()) { - if (Q_UNLIKELY(timeout < 0)) { + if (Q_UNLIKELY(deadlineTimer.isForever())) { lockInternal(); return true; } - QDeadlineTimer deadlineTimer(timeout); // The mutex is already locked, set a bit indicating we're waiting. // Note we must set to dummyFutexValue because there could be other threads // also waiting. if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr) return true; - qint64 remainingTime = deadlineTimer.remainingTimeNSecs(); - Q_FOREVER { - if (!futexWait(d_ptr, dummyFutexValue(), remainingTime)) + for (;;) { + if (!futexWait(d_ptr, dummyFutexValue(), deadlineTimer)) return false; // We got woken up, so must try to acquire the mutex. We must set @@ -633,9 +697,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr) return true; - // calculate the remaining time - remainingTime = deadlineTimer.remainingTimeNSecs(); - if (remainingTime <= 0) + if (deadlineTimer.hasExpired()) return false; } } @@ -647,7 +709,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT continue; if (copy == dummyLocked()) { - if (timeout == 0) + if (deadlineTimer.hasExpired()) return false; // The mutex is locked but does not have a QMutexPrivate yet. // we need to allocate a QMutexPrivate @@ -662,7 +724,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT } QMutexPrivate *d = static_cast<QMutexPrivate *>(copy); - if (timeout == 0 && !d->possiblyUnlocked.loadRelaxed()) + if (deadlineTimer.hasExpired() && !d->possiblyUnlocked.loadRelaxed()) return false; // At this point we have a pointer to a QMutexPrivate. But the other thread @@ -715,7 +777,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT continue; } - if (d->wait(timeout)) { + if (d->wait(deadlineTimer)) { // reset the possiblyUnlocked flag if needed (and deref its corresponding reference) if (d->possiblyUnlocked.loadRelaxed() && d->possiblyUnlocked.testAndSetRelaxed(true, false)) d->deref(); @@ -724,8 +786,7 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT Q_ASSERT(d == d_ptr.loadRelaxed()); return true; } else { - Q_ASSERT(timeout >= 0); - //timeout + // timed out d->derefWaiters(1); //There may be a race in which the mutex is unlocked right after we timed out, // and before we deref the waiters, so maybe the mutex is actually unlocked. @@ -793,7 +854,7 @@ struct FreeListConstants : QFreeListDefaultConstants { enum { BlockCount = 4, MaxIndex=0xffff }; static const int Sizes[BlockCount]; }; -const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = { +Q_CONSTINIT const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = { 16, 128, 1024, @@ -802,7 +863,7 @@ const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = { typedef QFreeList<QMutexPrivate, FreeListConstants> FreeList; // We cannot use Q_GLOBAL_STATIC because it uses QMutex -static FreeList freeList_; +Q_CONSTINIT static FreeList freeList_; FreeList *freelist() { return &freeList_; @@ -847,12 +908,10 @@ void QMutexPrivate::derefWaiters(int value) noexcept QT_END_NAMESPACE -#if defined(Q_OS_LINUX) && defined(QT_ALWAYS_USE_FUTEX) +#if defined(QT_ALWAYS_USE_FUTEX) // nothing -#elif defined(Q_OS_MAC) +#elif defined(Q_OS_DARWIN) # include "qmutex_mac.cpp" -#elif defined(Q_OS_WIN) -# include "qmutex_win.cpp" #else # include "qmutex_unix.cpp" #endif |