| /**************************************************************************** |
| ** |
| ** 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$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qplatformdefs.h" |
| #include "qmutex.h" |
| #include <qdebug.h> |
| #include "qatomic.h" |
| #include "qelapsedtimer.h" |
| #include "qthread.h" |
| #include "qmutex_p.h" |
| |
| #ifndef QT_LINUX_FUTEX |
| #include "private/qfreelist_p.h" |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| static inline bool isRecursive(QMutexData *d) |
| { |
| quintptr u = quintptr(d); |
| if (Q_LIKELY(u <= 0x3)) |
| return false; |
| #ifdef QT_LINUX_FUTEX |
| Q_ASSERT(d->recursive); |
| return true; |
| #else |
| return d->recursive; |
| #endif |
| } |
| |
| class QRecursiveMutexPrivate : public QMutexData |
| { |
| public: |
| QRecursiveMutexPrivate() |
| : QMutexData(QMutex::Recursive), owner(0), count(0) {} |
| |
| // written to by the thread that first owns 'mutex'; |
| // read during attempts to acquire ownership of 'mutex' from any other thread: |
| QAtomicPointer<std::remove_pointer<Qt::HANDLE>::type> owner; |
| |
| // only ever accessed from the thread that owns 'mutex': |
| uint count; |
| |
| QMutex mutex; |
| |
| bool lock(int timeout) QT_MUTEX_LOCK_NOEXCEPT; |
| void unlock() noexcept; |
| }; |
| |
| /* |
| \class QBasicMutex |
| \inmodule QtCore |
| \brief QMutex POD |
| \internal |
| |
| \ingroup thread |
| |
| - Can be used as global static object. |
| - Always non-recursive |
| - Do not use tryLock with timeout > 0, else you can have a leak (see the ~QMutex destructor) |
| */ |
| |
| /*! |
| \class QMutex |
| \inmodule QtCore |
| \brief The QMutex class provides access serialization between threads. |
| |
| \threadsafe |
| |
| \ingroup thread |
| |
| The purpose of a QMutex is to protect an object, data structure or |
| section of code so that only one thread can access it at a time |
| (this is similar to the Java \c synchronized keyword). It is |
| usually best to use a mutex with a QMutexLocker since this makes |
| it easy to ensure that locking and unlocking are performed |
| consistently. |
| |
| For example, say there is a method that prints a message to the |
| user on two lines: |
| |
| \snippet code/src_corelib_thread_qmutex.cpp 0 |
| |
| If these two methods are called in succession, the following happens: |
| |
| \snippet code/src_corelib_thread_qmutex.cpp 1 |
| |
| If these two methods are called simultaneously from two threads then the |
| following sequence could result: |
| |
| \snippet code/src_corelib_thread_qmutex.cpp 2 |
| |
| If we add a mutex, we should get the result we want: |
| |
| \snippet code/src_corelib_thread_qmutex.cpp 3 |
| |
| Then only one thread can modify \c number at any given time and |
| the result is correct. This is a trivial example, of course, but |
| applies to any other case where things need to happen in a |
| particular sequence. |
| |
| When you call lock() in a thread, other threads that try to call |
| lock() in the same place will block until the thread that got the |
| lock calls unlock(). A non-blocking alternative to lock() is |
| tryLock(). |
| |
| QMutex is optimized to be fast in the non-contended case. A non-recursive |
| QMutex will not allocate memory if there is no contention on that mutex. |
| It is constructed and destroyed with almost no overhead, |
| which means it is fine to have many mutexes as part of other classes. |
| |
| \sa QRecursiveMutex, QMutexLocker, QReadWriteLock, QSemaphore, QWaitCondition |
| */ |
| |
| /*! |
| \enum QMutex::RecursionMode |
| |
| \value Recursive In this mode, a thread can lock the same mutex |
| multiple times and the mutex won't be unlocked |
| until a corresponding number of unlock() calls |
| have been made. You should use QRecursiveMutex |
| for this use-case. |
| |
| \value NonRecursive In this mode, a thread may only lock a mutex |
| once. |
| |
| \sa QMutex(), QRecursiveMutex |
| */ |
| |
| /*! |
| \fn QMutex::QMutex() |
| |
| Constructs a new mutex. The mutex is created in an unlocked state. |
| */ |
| |
| /*! |
| Constructs a new mutex. The mutex is created in an unlocked state. |
| |
| If \a mode is QMutex::Recursive, a thread can lock the same mutex |
| multiple times and the mutex won't be unlocked until a |
| corresponding number of unlock() calls have been made. Otherwise |
| a thread may only lock a mutex once. The default is |
| QMutex::NonRecursive. |
| |
| Recursive mutexes are slower and take more memory than non-recursive ones. |
| |
| \sa lock(), unlock() |
| */ |
| QMutex::QMutex(RecursionMode mode) |
| { |
| d_ptr.storeRelaxed(mode == Recursive ? new QRecursiveMutexPrivate : 0); |
| } |
| |
| /*! |
| Destroys the mutex. |
| |
| \warning Destroying a locked mutex may result in undefined behavior. |
| */ |
| QMutex::~QMutex() |
| { |
| QMutexData *d = d_ptr.loadRelaxed(); |
| if (isRecursive()) { |
| delete static_cast<QRecursiveMutexPrivate *>(d); |
| } else if (d) { |
| #ifndef QT_LINUX_FUTEX |
| if (d != dummyLocked() && static_cast<QMutexPrivate *>(d)->possiblyUnlocked.loadRelaxed() |
| && tryLock()) { |
| unlock(); |
| return; |
| } |
| #endif |
| qWarning("QMutex: destroying locked mutex"); |
| } |
| } |
| |
| /*! \fn void QMutex::lock() |
| \fn QRecursiveMutex::lock() |
| |
| Locks the mutex. If another thread has locked the mutex then this |
| call will block until that thread has unlocked it. |
| |
| Calling this function multiple times on the same mutex from the |
| same thread is allowed if this mutex is a |
| \l{QRecursiveMutex}{recursive mutex}. If this mutex is a |
| \l{QMutex}{non-recursive mutex}, this function will |
| \e dead-lock when the mutex is locked recursively. |
| |
| \sa unlock() |
| */ |
| void QMutex::lock() QT_MUTEX_LOCK_NOEXCEPT |
| { |
| QMutexData *current; |
| if (fastTryLock(current)) |
| return; |
| if (QT_PREPEND_NAMESPACE(isRecursive)(current)) |
| static_cast<QRecursiveMutexPrivate *>(current)->lock(-1); |
| else |
| lockInternal(); |
| } |
| |
| /*! \fn bool QMutex::tryLock(int timeout) |
| \fn bool 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 |
| milliseconds for the mutex to become available. |
| |
| Note: Passing a negative number as the \a timeout is equivalent to |
| calling lock(), i.e. this function will wait forever until mutex |
| can be locked if \a timeout is negative. |
| |
| 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 if this mutex is a |
| \l{QRecursiveMutex}{recursive mutex}. If this mutex is a |
| \l{QMutex}{non-recursive mutex}, this function will |
| \e always return false when attempting to lock the mutex |
| recursively. |
| |
| \sa lock(), unlock() |
| */ |
| bool QMutex::tryLock(int timeout) QT_MUTEX_LOCK_NOEXCEPT |
| { |
| QMutexData *current; |
| if (fastTryLock(current)) |
| return true; |
| if (QT_PREPEND_NAMESPACE(isRecursive)(current)) |
| return static_cast<QRecursiveMutexPrivate *>(current)->lock(timeout); |
| else |
| return lockInternal(timeout); |
| } |
| |
| /*! \fn bool QMutex::try_lock() |
| \fn bool QRecursiveMutex::try_lock() |
| \since 5.8 |
| |
| Attempts to lock the mutex. This function returns \c true if the lock |
| was obtained; otherwise it returns \c false. |
| |
| This function is provided for compatibility with the Standard Library |
| concept \c Lockable. It is equivalent to tryLock(). |
| |
| The function returns \c true if the lock was obtained; otherwise it |
| returns \c false |
| */ |
| |
| /*! \fn template <class Rep, class Period> bool QMutex::try_lock_for(std::chrono::duration<Rep, Period> duration) |
| \fn template <class Rep, class Period> bool QRecursiveMutex::try_lock_for(std::chrono::duration<Rep, Period> duration) |
| \since 5.8 |
| |
| 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 least \a duration |
| for the mutex to become available. |
| |
| Note: Passing a negative duration as the \a duration is equivalent to |
| calling try_lock(). This behavior differs from tryLock(). |
| |
| 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 if this mutex is a |
| \l{QRecursiveMutex}{recursive mutex}. If this mutex is a |
| \l{QMutex}{non-recursive mutex}, this function will |
| \e always return false when attempting to lock the mutex |
| recursively. |
| |
| \sa lock(), unlock() |
| */ |
| |
| /*! \fn template<class Clock, class Duration> bool QMutex::try_lock_until(std::chrono::time_point<Clock, Duration> timePoint) |
| \fn template<class Clock, class Duration> bool QRecursiveMutex::try_lock_until(std::chrono::time_point<Clock, Duration> timePoint) |
| \since 5.8 |
| |
| 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 at least until \a timePoint |
| for the mutex to become available. |
| |
| Note: Passing a \a timePoint which has already passed is equivalent |
| to calling try_lock(). This behavior differs from tryLock(). |
| |
| 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 if this mutex is a |
| \l{QRecursiveMutex}{recursive mutex}. If this mutex is a |
| \l{QMutex}{non-recursive mutex}, this function will |
| \e always return false when attempting to lock the mutex |
| recursively. |
| |
| \sa lock(), unlock() |
| */ |
| |
| /*! \fn void QMutex::unlock() |
| \fn void QRecursiveMutex::unlock() |
| |
| Unlocks the mutex. Attempting to unlock a mutex in a different |
| thread to the one that locked it results in an error. Unlocking a |
| mutex that is not locked results in undefined behavior. |
| |
| \sa lock() |
| */ |
| void QMutex::unlock() noexcept |
| { |
| QMutexData *current; |
| if (fastTryUnlock(current)) |
| return; |
| if (QT_PREPEND_NAMESPACE(isRecursive)(current)) |
| static_cast<QRecursiveMutexPrivate *>(current)->unlock(); |
| else |
| unlockInternal(); |
| } |
| |
| |
| /*! |
| \fn bool QMutex::isRecursive() const |
| \since 5.7 |
| |
| Returns \c true if the mutex is recursive. |
| */ |
| |
| bool QBasicMutex::isRecursive() noexcept |
| { |
| return QT_PREPEND_NAMESPACE(isRecursive)(d_ptr.loadAcquire()); |
| } |
| |
| /*! |
| \since 5.7 |
| |
| Returns \c true if the mutex is recursive. |
| */ |
| bool QBasicMutex::isRecursive() const noexcept |
| { |
| return QT_PREPEND_NAMESPACE(isRecursive)(d_ptr.loadAcquire()); |
| } |
| |
| /*! |
| \class QRecursiveMutex |
| \inmodule QtCore |
| \since 5.14 |
| \brief The QRecursiveMutex class provides access serialization between threads. |
| |
| \threadsafe |
| |
| \ingroup thread |
| |
| The QRecursiveMutex class is a mutex, like QMutex, with which it is |
| API-compatible. It differs from QMutex by accepting lock() calls from |
| the same thread any number of times. QMutex would deadlock in this situation. |
| |
| QRecursiveMutex is much more expensive to construct and operate on, so |
| use a plain QMutex whenever you can. Sometimes, one public function, |
| however, calls another public function, and they both need to lock the |
| same mutex. In this case, you have two options: |
| |
| \list |
| \li Factor the code that needs mutex protection into private functions, |
| which assume that the mutex is held when they are called, and lock a |
| plain QMutex in the public functions before you call the private |
| implementation ones. |
| \li Or use a recursive mutex, so it doesn't matter that the first public |
| function has already locked the mutex when the second one wishes to do so. |
| \endlist |
| |
| \sa QMutex, QMutexLocker, QReadWriteLock, QSemaphore, QWaitCondition |
| */ |
| |
| /*! |
| Constructs a new recursive mutex. The mutex is created in an unlocked state. |
| |
| \sa lock(), unlock() |
| */ |
| QRecursiveMutex::QRecursiveMutex() |
| : QMutex() |
| { |
| d_ptr.storeRelaxed(new QRecursiveMutexPrivate); |
| } |
| |
| /*! |
| Destroys the mutex. |
| |
| \warning Destroying a locked mutex may result in undefined behavior. |
| */ |
| QRecursiveMutex::~QRecursiveMutex() |
| { |
| delete static_cast<QRecursiveMutexPrivate*>(d_ptr.fetchAndStoreAcquire(nullptr)); |
| } |
| |
| /*! |
| \class QMutexLocker |
| \inmodule QtCore |
| \brief The QMutexLocker class is a convenience class that simplifies |
| locking and unlocking mutexes. |
| |
| \threadsafe |
| |
| \ingroup thread |
| |
| Locking and unlocking a QMutex in complex functions and |
| statements or in exception handling code is error-prone and |
| difficult to debug. QMutexLocker can be used in such situations |
| to ensure that the state of the mutex is always well-defined. |
| |
| QMutexLocker should be created within a function where a |
| QMutex needs to be locked. The mutex is locked when QMutexLocker |
| is created. You can unlock and relock the mutex with \c unlock() |
| and \c relock(). If locked, the mutex will be unlocked when the |
| QMutexLocker is destroyed. |
| |
| For example, this complex function locks a QMutex upon entering |
| the function and unlocks the mutex at all the exit points: |
| |
| \snippet code/src_corelib_thread_qmutex.cpp 4 |
| |
| This example function will get more complicated as it is |
| developed, which increases the likelihood that errors will occur. |
| |
| Using QMutexLocker greatly simplifies the code, and makes it more |
| readable: |
| |
| \snippet code/src_corelib_thread_qmutex.cpp 5 |
| |
| Now, the mutex will always be unlocked when the QMutexLocker |
| object is destroyed (when the function returns since \c locker is |
| an auto variable). |
| |
| The same principle applies to code that throws and catches |
| exceptions. An exception that is not caught in the function that |
| has locked the mutex has no way of unlocking the mutex before the |
| exception is passed up the stack to the calling function. |
| |
| QMutexLocker also provides a \c mutex() member function that returns |
| the mutex on which the QMutexLocker is operating. This is useful |
| for code that needs access to the mutex, such as |
| QWaitCondition::wait(). For example: |
| |
| \snippet code/src_corelib_thread_qmutex.cpp 6 |
| |
| \sa QReadLocker, QWriteLocker, QMutex |
| */ |
| |
| /*! |
| \fn QMutexLocker::QMutexLocker(QMutex *mutex) |
| |
| Constructs a QMutexLocker and locks \a mutex. The mutex will be |
| unlocked when the QMutexLocker is destroyed. If \a mutex is zero, |
| QMutexLocker does nothing. |
| |
| \sa QMutex::lock() |
| */ |
| |
| /*! |
| \fn QMutexLocker::QMutexLocker(QRecursiveMutex *mutex) |
| \since 5.14 |
| |
| Constructs a QMutexLocker and locks \a mutex. The mutex will be |
| unlocked (unlock() called) when the QMutexLocker is destroyed. |
| If \a mutex is \nullptr, QMutexLocker does nothing. |
| |
| \sa QMutex::lock() |
| */ |
| |
| /*! |
| \fn QMutexLocker::~QMutexLocker() |
| |
| Destroys the QMutexLocker and unlocks the mutex that was locked |
| in the constructor. |
| |
| \sa QMutex::unlock() |
| */ |
| |
| /*! |
| \fn void QMutexLocker::unlock() |
| |
| Unlocks this mutex locker. You can use \c relock() to lock |
| it again. It does not need to be locked when destroyed. |
| |
| \sa relock() |
| */ |
| |
| /*! |
| \fn void QMutexLocker::relock() |
| |
| Relocks an unlocked mutex locker. |
| |
| \sa unlock() |
| */ |
| |
| /*! |
| \fn QMutex *QMutexLocker::mutex() const |
| |
| Returns the mutex on which the QMutexLocker is operating. |
| |
| */ |
| |
| #ifndef QT_LINUX_FUTEX //linux implementation is in qmutex_linux.cpp |
| |
| /* |
| For a rough introduction on how this works, refer to |
| http://woboq.com/blog/internals-of-qmutex-in-qt5.html |
| which explains a slightly simplified version of it. |
| The differences are that here we try to work with timeout (requires the |
| possiblyUnlocked flag) and that we only wake one thread when unlocking |
| (requires maintaining the waiters count) |
| We also support recursive mutexes which always have a valid d_ptr. |
| |
| The waiters flag represents the number of threads that are waiting or about |
| to wait on the mutex. There are two tricks to keep in mind: |
| We don't want to increment waiters after we checked no threads are waiting |
| (waiters == 0). That's why we atomically set the BigNumber flag on waiters when |
| we check waiters. Similarly, if waiters is decremented right after we checked, |
| the mutex would be unlocked (d->wakeUp() has (or will) be called), but there is |
| no thread waiting. This is only happening if there was a timeout in tryLock at the |
| same time as the mutex is unlocked. So when there was a timeout, we set the |
| possiblyUnlocked flag. |
| */ |
| |
| /*! |
| \internal helper for lock() |
| */ |
| void QBasicMutex::lockInternal() QT_MUTEX_LOCK_NOEXCEPT |
| { |
| lockInternal(-1); |
| } |
| |
| /*! |
| \internal helper for lock(int) |
| */ |
| bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT |
| { |
| Q_ASSERT(!isRecursive()); |
| |
| while (!fastTryLock()) { |
| QMutexData *copy = d_ptr.loadAcquire(); |
| if (!copy) // if d is 0, the mutex is unlocked |
| continue; |
| |
| if (copy == dummyLocked()) { |
| if (timeout == 0) |
| return false; |
| // The mutex is locked but does not have a QMutexPrivate yet. |
| // we need to allocate a QMutexPrivate |
| QMutexPrivate *newD = QMutexPrivate::allocate(); |
| if (!d_ptr.testAndSetOrdered(dummyLocked(), newD)) { |
| //Either the mutex is already unlocked, or another thread already set it. |
| newD->deref(); |
| continue; |
| } |
| copy = newD; |
| //the d->refCount is already 1 the deref will occurs when we unlock |
| } |
| |
| QMutexPrivate *d = static_cast<QMutexPrivate *>(copy); |
| if (timeout == 0 && !d->possiblyUnlocked.loadRelaxed()) |
| return false; |
| |
| // At this point we have a pointer to a QMutexPrivate. But the other thread |
| // may unlock the mutex at any moment and release the QMutexPrivate to the pool. |
| // We will try to reference it to avoid unlock to release it to the pool to make |
| // sure it won't be released. But if the refcount is already 0 it has been released. |
| if (!d->ref()) |
| continue; //that QMutexData was already released |
| |
| // We now hold a reference to the QMutexPrivate. It won't be released and re-used. |
| // But it is still possible that it was already re-used by another QMutex right before |
| // we did the ref(). So check if we still hold a pointer to the right mutex. |
| if (d != d_ptr.loadAcquire()) { |
| //Either the mutex is already unlocked, or relocked with another mutex |
| d->deref(); |
| continue; |
| } |
| |
| // In this part, we will try to increment the waiters count. |
| // We just need to take care of the case in which the old_waiters |
| // is set to the BigNumber magic value set in unlockInternal() |
| int old_waiters; |
| do { |
| old_waiters = d->waiters.loadRelaxed(); |
| if (old_waiters == -QMutexPrivate::BigNumber) { |
| // we are unlocking, and the thread that unlocks is about to change d to 0 |
| // we try to acquire the mutex by changing to dummyLocked() |
| if (d_ptr.testAndSetAcquire(d, dummyLocked())) { |
| // Mutex acquired |
| d->deref(); |
| return true; |
| } else { |
| Q_ASSERT(d != d_ptr.loadRelaxed()); //else testAndSetAcquire should have succeeded |
| // Mutex is likely to bo 0, we should continue the outer-loop, |
| // set old_waiters to the magic value of BigNumber |
| old_waiters = QMutexPrivate::BigNumber; |
| break; |
| } |
| } |
| } while (!d->waiters.testAndSetRelaxed(old_waiters, old_waiters + 1)); |
| |
| if (d != d_ptr.loadAcquire()) { |
| // The mutex was unlocked before we incremented waiters. |
| if (old_waiters != QMutexPrivate::BigNumber) { |
| //we did not break the previous loop |
| Q_ASSERT(d->waiters.loadRelaxed() >= 1); |
| d->waiters.deref(); |
| } |
| d->deref(); |
| continue; |
| } |
| |
| if (d->wait(timeout)) { |
| // reset the possiblyUnlocked flag if needed (and deref its corresponding reference) |
| if (d->possiblyUnlocked.loadRelaxed() && d->possiblyUnlocked.testAndSetRelaxed(true, false)) |
| d->deref(); |
| d->derefWaiters(1); |
| //we got the lock. (do not deref) |
| Q_ASSERT(d == d_ptr.loadRelaxed()); |
| return true; |
| } else { |
| Q_ASSERT(timeout >= 0); |
| //timeout |
| 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. |
| // Set the possiblyUnlocked flag to indicate this possibility. |
| if (!d->possiblyUnlocked.testAndSetRelaxed(false, true)) { |
| // We keep a reference when possiblyUnlocked is true. |
| // but if possiblyUnlocked was already true, we don't need to keep the reference. |
| d->deref(); |
| } |
| return false; |
| } |
| } |
| Q_ASSERT(d_ptr.loadRelaxed() != 0); |
| return true; |
| } |
| |
| /*! |
| \internal |
| */ |
| void QBasicMutex::unlockInternal() noexcept |
| { |
| QMutexData *copy = d_ptr.loadAcquire(); |
| Q_ASSERT(copy); //we must be locked |
| Q_ASSERT(copy != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed |
| Q_ASSERT(!isRecursive()); |
| |
| QMutexPrivate *d = reinterpret_cast<QMutexPrivate *>(copy); |
| |
| // If no one is waiting for the lock anymore, we should reset d to 0x0. |
| // Using fetchAndAdd, we atomically check that waiters was equal to 0, and add a flag |
| // to the waiters variable (BigNumber). That way, we avoid the race in which waiters is |
| // incremented right after we checked, because we won't increment waiters if is |
| // equal to -BigNumber |
| if (d->waiters.fetchAndAddRelease(-QMutexPrivate::BigNumber) == 0) { |
| //there is no one waiting on this mutex anymore, set the mutex as unlocked (d = 0) |
| if (d_ptr.testAndSetRelease(d, 0)) { |
| // reset the possiblyUnlocked flag if needed (and deref its corresponding reference) |
| if (d->possiblyUnlocked.loadRelaxed() && d->possiblyUnlocked.testAndSetRelaxed(true, false)) |
| d->deref(); |
| } |
| d->derefWaiters(0); |
| } else { |
| d->derefWaiters(0); |
| //there are thread waiting, transfer the lock. |
| d->wakeUp(); |
| } |
| d->deref(); |
| } |
| |
| //The freelist management |
| namespace { |
| struct FreeListConstants : QFreeListDefaultConstants { |
| enum { BlockCount = 4, MaxIndex=0xffff }; |
| static const int Sizes[BlockCount]; |
| }; |
| const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = { |
| 16, |
| 128, |
| 1024, |
| FreeListConstants::MaxIndex - (16 + 128 + 1024) |
| }; |
| |
| typedef QFreeList<QMutexPrivate, FreeListConstants> FreeList; |
| // We cannot use Q_GLOBAL_STATIC because it uses QMutex |
| static FreeList freeList_; |
| FreeList *freelist() |
| { |
| return &freeList_; |
| } |
| } |
| |
| QMutexPrivate *QMutexPrivate::allocate() |
| { |
| int i = freelist()->next(); |
| QMutexPrivate *d = &(*freelist())[i]; |
| d->id = i; |
| Q_ASSERT(d->refCount.loadRelaxed() == 0); |
| Q_ASSERT(!d->recursive); |
| Q_ASSERT(!d->possiblyUnlocked.loadRelaxed()); |
| Q_ASSERT(d->waiters.loadRelaxed() == 0); |
| d->refCount.storeRelaxed(1); |
| return d; |
| } |
| |
| void QMutexPrivate::release() |
| { |
| Q_ASSERT(!recursive); |
| Q_ASSERT(refCount.loadRelaxed() == 0); |
| Q_ASSERT(!possiblyUnlocked.loadRelaxed()); |
| Q_ASSERT(waiters.loadRelaxed() == 0); |
| freelist()->release(id); |
| } |
| |
| // atomically subtract "value" to the waiters, and remove the QMutexPrivate::BigNumber flag |
| void QMutexPrivate::derefWaiters(int value) noexcept |
| { |
| int old_waiters; |
| int new_waiters; |
| do { |
| old_waiters = waiters.loadRelaxed(); |
| new_waiters = old_waiters; |
| if (new_waiters < 0) { |
| new_waiters += QMutexPrivate::BigNumber; |
| } |
| new_waiters -= value; |
| } while (!waiters.testAndSetRelaxed(old_waiters, new_waiters)); |
| } |
| #endif |
| |
| /*! |
| \internal |
| */ |
| inline bool QRecursiveMutexPrivate::lock(int timeout) QT_MUTEX_LOCK_NOEXCEPT |
| { |
| Qt::HANDLE self = QThread::currentThreadId(); |
| if (owner.loadRelaxed() == self) { |
| ++count; |
| Q_ASSERT_X(count != 0, "QMutex::lock", "Overflow in recursion counter"); |
| return true; |
| } |
| bool success = true; |
| if (timeout == -1) { |
| mutex.QBasicMutex::lock(); |
| } else { |
| success = mutex.tryLock(timeout); |
| } |
| |
| if (success) |
| owner.storeRelaxed(self); |
| return success; |
| } |
| |
| /*! |
| \internal |
| */ |
| inline void QRecursiveMutexPrivate::unlock() noexcept |
| { |
| if (count > 0) { |
| count--; |
| } else { |
| owner.storeRelaxed(0); |
| mutex.QBasicMutex::unlock(); |
| } |
| } |
| |
| QT_END_NAMESPACE |
| |
| #ifdef QT_LINUX_FUTEX |
| # include "qmutex_linux.cpp" |
| #elif defined(Q_OS_MAC) |
| # include "qmutex_mac.cpp" |
| #elif defined(Q_OS_WIN) |
| # include "qmutex_win.cpp" |
| #else |
| # include "qmutex_unix.cpp" |
| #endif |