Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qmutex.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// Copyright (C) 2012 Olivier Goffart <ogoffart@woboq.com>
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6#include "global/qglobal.h"
7#include "qplatformdefs.h"
8#include "qmutex.h"
9#include <qdebug.h>
10#include "qatomic.h"
11#include "qfutex_p.h"
12#include "qthread.h"
13#include "qmutex_p.h"
14
15#ifndef QT_ALWAYS_USE_FUTEX
16#include "private/qfreelist_p.h"
17#endif
18
20
21using namespace QtFutex;
23{
24 return reinterpret_cast<QMutexPrivate *>(quintptr(3));
25}
26
27/*
28 \class QBasicMutex
29 \inmodule QtCore
30 \brief QMutex POD
31 \internal
32
33 \ingroup thread
34
35 - Can be used as global static object.
36 - Always non-recursive
37 - Do not use tryLock with timeout > 0, else you can have a leak (see the ~QMutex destructor)
38*/
39
104void QBasicMutex::destroyInternal(QMutexPrivate *d)
105{
106 if (!d)
107 return;
108 if (!futexAvailable()) {
109 if (d != dummyLocked() && d->possiblyUnlocked.loadRelaxed() && tryLock()) {
110 unlock();
111 return;
112 }
113 }
114 qWarning("QMutex: destroying locked mutex");
115}
116
282QRecursiveMutex::~QRecursiveMutex()
283{
284}
285
334bool QRecursiveMutex::tryLock(QDeadlineTimer timeout) QT_MUTEX_LOCK_NOEXCEPT
335{
336 unsigned tsanFlags = QtTsan::MutexWriteReentrant | QtTsan::TryLock;
337 QtTsan::mutexPreLock(this, tsanFlags);
338
340 if (owner.loadRelaxed() == self) {
341 ++count;
342 Q_ASSERT_X(count != 0, "QMutex::lock", "Overflow in recursion counter");
343 QtTsan::mutexPostLock(this, tsanFlags, 0);
344 return true;
345 }
346 bool success = true;
347 if (timeout.isForever()) {
348 mutex.lock();
349 } else {
350 success = mutex.tryLock(timeout);
351 }
352
353 if (success)
354 owner.storeRelaxed(self);
355 else
356 tsanFlags |= QtTsan::TryLockFailed;
357
358 QtTsan::mutexPostLock(this, tsanFlags, 0);
359
360 return success;
361}
362
420void QRecursiveMutex::unlock() noexcept
421{
422 Q_ASSERT(owner.loadRelaxed() == QThread::currentThreadId());
423 QtTsan::mutexPreUnlock(this, 0u);
424
425 if (count > 0) {
426 count--;
427 } else {
428 owner.storeRelaxed(nullptr);
429 mutex.unlock();
430 }
431
432 QtTsan::mutexPostUnlock(this, 0u);
433}
434
435
574/*
575 For a rough introduction on how this works, refer to
576 http://woboq.com/blog/internals-of-qmutex-in-qt5.html
577 which explains a slightly simplified version of it.
578 The differences are that here we try to work with timeout (requires the
579 possiblyUnlocked flag) and that we only wake one thread when unlocking
580 (requires maintaining the waiters count)
581 We also support recursive mutexes which always have a valid d_ptr.
582
583 The waiters flag represents the number of threads that are waiting or about
584 to wait on the mutex. There are two tricks to keep in mind:
585 We don't want to increment waiters after we checked no threads are waiting
586 (waiters == 0). That's why we atomically set the BigNumber flag on waiters when
587 we check waiters. Similarly, if waiters is decremented right after we checked,
588 the mutex would be unlocked (d->wakeUp() has (or will) be called), but there is
589 no thread waiting. This is only happening if there was a timeout in tryLock at the
590 same time as the mutex is unlocked. So when there was a timeout, we set the
591 possiblyUnlocked flag.
592*/
593
594/*
595 * QBasicMutex implementation with futexes (Linux, Windows 10)
596 *
597 * QBasicMutex contains one pointer value, which can contain one of four
598 * different values:
599 * 0x0 unlocked
600 * 0x1 locked, no waiters
601 * 0x3 locked, at least one waiter
602 *
603 * LOCKING:
604 *
605 * A starts in the 0x0 state, indicating that it's unlocked. When the first
606 * thread attempts to lock it, it will perform a testAndSetAcquire
607 * from 0x0 to 0x1. If that succeeds, the caller concludes that it
608 * successfully locked the mutex. That happens in fastTryLock().
609 *
610 * If that testAndSetAcquire fails, QBasicMutex::lockInternal is called.
611 *
612 * lockInternal will examine the value of the pointer. Otherwise, it will use
613 * futexes to sleep and wait for another thread to unlock. To do that, it needs
614 * to set a pointer value of 0x3, which indicates that thread is waiting. It
615 * does that by a simple fetchAndStoreAcquire operation.
616 *
617 * If the pointer value was 0x0, it means we succeeded in acquiring the mutex.
618 * For other values, it will then call FUTEX_WAIT and with an expected value of
619 * 0x3.
620 *
621 * If the pointer value changed before futex(2) managed to sleep, it will
622 * return -1 / EWOULDBLOCK, in which case we have to start over. And even if we
623 * are woken up directly by a FUTEX_WAKE, we need to acquire the mutex, so we
624 * start over again.
625 *
626 * UNLOCKING:
627 *
628 * To unlock, we need to set a value of 0x0 to indicate it's unlocked. The
629 * first attempt is a testAndSetRelease operation from 0x1 to 0x0. If that
630 * succeeds, we're done.
631 *
632 * If it fails, unlockInternal() is called. The only possibility is that the
633 * mutex value was 0x3, which indicates some other thread is waiting or was
634 * waiting in the past. We then set the mutex to 0x0 and perform a FUTEX_WAKE.
635 */
636
640void QBasicMutex::lockInternal() QT_MUTEX_LOCK_NOEXCEPT
641{
642 if (futexAvailable()) {
643 // note we must set to dummyFutexValue because there could be other threads
644 // also waiting
645 while (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) != nullptr) {
646 // successfully set the waiting bit, now sleep
647 futexWait(d_ptr, dummyFutexValue());
648
649 // we got woken up, so try to acquire the mutex
650 }
651 Q_ASSERT(d_ptr.loadRelaxed());
652 } else {
653 lockInternal(-1);
654 }
655}
656
660#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
661bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
662{
663 if (timeout == 0)
664 return false;
665
666 return lockInternal(QDeadlineTimer(timeout));
667}
668#endif
669
673bool QBasicMutex::lockInternal(QDeadlineTimer deadlineTimer) QT_MUTEX_LOCK_NOEXCEPT
674{
675 if (deadlineTimer.hasExpired())
676 return false;
677
678 if (futexAvailable()) {
679 if (Q_UNLIKELY(deadlineTimer.isForever())) {
680 lockInternal();
681 return true;
682 }
683
684 // The mutex is already locked, set a bit indicating we're waiting.
685 // Note we must set to dummyFutexValue because there could be other threads
686 // also waiting.
687 if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
688 return true;
689
690 for (;;) {
691 if (!futexWait(d_ptr, dummyFutexValue(), deadlineTimer))
692 return false;
693
694 // We got woken up, so must try to acquire the mutex. We must set
695 // to dummyFutexValue() again because there could be other threads
696 // waiting.
697 if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
698 return true;
699
700 if (deadlineTimer.hasExpired())
701 return false;
702 }
703 }
704
705#if !defined(QT_ALWAYS_USE_FUTEX)
706 while (!fastTryLock()) {
707 QMutexPrivate *copy = d_ptr.loadAcquire();
708 if (!copy) // if d is 0, the mutex is unlocked
709 continue;
710
711 if (copy == dummyLocked()) {
712 if (deadlineTimer.hasExpired())
713 return false;
714 // The mutex is locked but does not have a QMutexPrivate yet.
715 // we need to allocate a QMutexPrivate
717 if (!d_ptr.testAndSetOrdered(dummyLocked(), newD)) {
718 //Either the mutex is already unlocked, or another thread already set it.
719 newD->deref();
720 continue;
721 }
722 copy = newD;
723 //the d->refCount is already 1 the deref will occurs when we unlock
724 }
725
726 QMutexPrivate *d = static_cast<QMutexPrivate *>(copy);
727 if (deadlineTimer.hasExpired() && !d->possiblyUnlocked.loadRelaxed())
728 return false;
729
730 // At this point we have a pointer to a QMutexPrivate. But the other thread
731 // may unlock the mutex at any moment and release the QMutexPrivate to the pool.
732 // We will try to reference it to avoid unlock to release it to the pool to make
733 // sure it won't be released. But if the refcount is already 0 it has been released.
734 if (!d->ref())
735 continue; //that QMutexPrivate was already released
736
737 // We now hold a reference to the QMutexPrivate. It won't be released and re-used.
738 // But it is still possible that it was already re-used by another QMutex right before
739 // we did the ref(). So check if we still hold a pointer to the right mutex.
740 if (d != d_ptr.loadAcquire()) {
741 //Either the mutex is already unlocked, or relocked with another mutex
742 d->deref();
743 continue;
744 }
745
746 // In this part, we will try to increment the waiters count.
747 // We just need to take care of the case in which the old_waiters
748 // is set to the BigNumber magic value set in unlockInternal()
749 int old_waiters;
750 do {
751 old_waiters = d->waiters.loadAcquire();
752 if (old_waiters == -QMutexPrivate::BigNumber) {
753 // we are unlocking, and the thread that unlocks is about to change d to 0
754 // we try to acquire the mutex by changing to dummyLocked()
755 if (d_ptr.testAndSetAcquire(d, dummyLocked())) {
756 // Mutex acquired
757 d->deref();
758 return true;
759 } else {
760 Q_ASSERT(d != d_ptr.loadRelaxed()); //else testAndSetAcquire should have succeeded
761 // Mutex is likely to bo 0, we should continue the outer-loop,
762 // set old_waiters to the magic value of BigNumber
763 old_waiters = QMutexPrivate::BigNumber;
764 break;
765 }
766 }
767 } while (!d->waiters.testAndSetRelaxed(old_waiters, old_waiters + 1));
768
769 if (d != d_ptr.loadAcquire()) {
770 // The mutex was unlocked before we incremented waiters.
771 if (old_waiters != QMutexPrivate::BigNumber) {
772 //we did not break the previous loop
773 Q_ASSERT(d->waiters.loadRelaxed() >= 1);
774 d->waiters.deref();
775 }
776 d->deref();
777 continue;
778 }
779
780 if (d->wait(deadlineTimer)) {
781 // reset the possiblyUnlocked flag if needed (and deref its corresponding reference)
782 if (d->possiblyUnlocked.loadRelaxed() && d->possiblyUnlocked.testAndSetRelaxed(true, false))
783 d->deref();
784 d->derefWaiters(1);
785 //we got the lock. (do not deref)
786 Q_ASSERT(d == d_ptr.loadRelaxed());
787 return true;
788 } else {
789 // timed out
790 d->derefWaiters(1);
791 //There may be a race in which the mutex is unlocked right after we timed out,
792 // and before we deref the waiters, so maybe the mutex is actually unlocked.
793 // Set the possiblyUnlocked flag to indicate this possibility.
794 if (!d->possiblyUnlocked.testAndSetRelaxed(false, true)) {
795 // We keep a reference when possiblyUnlocked is true.
796 // but if possiblyUnlocked was already true, we don't need to keep the reference.
797 d->deref();
798 }
799 return false;
800 }
801 }
802 Q_ASSERT(d_ptr.loadRelaxed() != 0);
803 return true;
804#else
805 Q_UNREACHABLE();
806#endif
807}
808
812void QBasicMutex::unlockInternal() noexcept
813{
814 QMutexPrivate *copy = d_ptr.loadAcquire();
815 Q_ASSERT(copy); //we must be locked
816 Q_ASSERT(copy != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed
817
818 if (futexAvailable()) {
819 d_ptr.storeRelease(nullptr);
820 return futexWakeOne(d_ptr);
821 }
822
823#if !defined(QT_ALWAYS_USE_FUTEX)
824 QMutexPrivate *d = reinterpret_cast<QMutexPrivate *>(copy);
825
826 // If no one is waiting for the lock anymore, we should reset d to 0x0.
827 // Using fetchAndAdd, we atomically check that waiters was equal to 0, and add a flag
828 // to the waiters variable (BigNumber). That way, we avoid the race in which waiters is
829 // incremented right after we checked, because we won't increment waiters if is
830 // equal to -BigNumber
831 if (d->waiters.fetchAndAddRelease(-QMutexPrivate::BigNumber) == 0) {
832 //there is no one waiting on this mutex anymore, set the mutex as unlocked (d = 0)
833 if (d_ptr.testAndSetRelease(d, 0)) {
834 // reset the possiblyUnlocked flag if needed (and deref its corresponding reference)
835 if (d->possiblyUnlocked.loadRelaxed() && d->possiblyUnlocked.testAndSetRelaxed(true, false))
836 d->deref();
837 }
838 d->derefWaiters(0);
839 } else {
840 d->derefWaiters(0);
841 //there are thread waiting, transfer the lock.
842 d->wakeUp();
843 }
844 d->deref();
845#else
846 Q_UNUSED(copy);
847#endif
848}
849
850#if !defined(QT_ALWAYS_USE_FUTEX)
851//The freelist management
852namespace {
853struct FreeListConstants : QFreeListDefaultConstants {
854 enum { BlockCount = 4, MaxIndex=0xffff };
855 static const int Sizes[BlockCount];
856};
857Q_CONSTINIT const int FreeListConstants::Sizes[FreeListConstants::BlockCount] = {
858 16,
859 128,
860 1024,
861 FreeListConstants::MaxIndex - (16 + 128 + 1024)
862};
863
864typedef QFreeList<QMutexPrivate, FreeListConstants> FreeList;
865// We cannot use Q_GLOBAL_STATIC because it uses QMutex
866Q_CONSTINIT static FreeList freeList_;
867FreeList *freelist()
868{
869 return &freeList_;
870}
871}
872
874{
875 int i = freelist()->next();
876 QMutexPrivate *d = &(*freelist())[i];
877 d->id = i;
878 Q_ASSERT(d->refCount.loadRelaxed() == 0);
879 Q_ASSERT(!d->possiblyUnlocked.loadRelaxed());
880 Q_ASSERT(d->waiters.loadRelaxed() == 0);
881 d->refCount.storeRelaxed(1);
882 return d;
883}
884
886{
890 freelist()->release(id);
891}
892
893// atomically subtract "value" to the waiters, and remove the QMutexPrivate::BigNumber flag
895{
896 int old_waiters;
897 int new_waiters;
898 do {
899 old_waiters = waiters.loadRelaxed();
900 new_waiters = old_waiters;
901 if (new_waiters < 0) {
902 new_waiters += QMutexPrivate::BigNumber;
903 }
904 new_waiters -= value;
905 } while (!waiters.testAndSetRelaxed(old_waiters, new_waiters));
906}
907#endif
908
910
911#if defined(QT_ALWAYS_USE_FUTEX)
912// nothing
913#elif defined(Q_OS_DARWIN)
914# include "qmutex_mac.cpp"
915#else
916# include "qmutex_unix.cpp"
917#endif
T loadRelaxed() const noexcept
\inmodule QtCore
void deref()
Definition qmutex_p.h:65
QAtomicInt waiters
Definition qmutex_p.h:75
void derefWaiters(int value) noexcept
Definition qmutex.cpp:894
static QMutexPrivate * allocate()
Definition qmutex.cpp:873
QAtomicInt possiblyUnlocked
Definition qmutex_p.h:76
QAtomicInt refCount
Definition qmutex_p.h:50
void release()
Definition qmutex.cpp:885
bool tryLock(int timeout=0) noexcept
Attempts to lock the mutex.
Definition qmutex.h:287
void unlock() noexcept
Unlocks the mutex.
Definition qmutex.h:289
void lock() noexcept
Locks the mutex.
Definition qmutex.h:286
static Qt::HANDLE currentThreadId() noexcept Q_DECL_PURE_FUNCTION
Definition qthread.h:149
Combined button and popup list for selecting options.
void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
void futexWakeOne(Atomic &futex)
constexpr bool futexAvailable()
@ MutexWriteReentrant
Definition qtsan_impl.h:66
@ TryLockFailed
Definition qtsan_impl.h:68
@ TryLock
Definition qtsan_impl.h:67
void mutexPostUnlock(void *, unsigned)
Definition qtsan_impl.h:73
void mutexPreUnlock(void *, unsigned)
Definition qtsan_impl.h:72
void mutexPreLock(void *, unsigned)
Definition qtsan_impl.h:70
void mutexPostLock(void *, unsigned, int)
Definition qtsan_impl.h:71
void * HANDLE
QString self
Definition language.cpp:58
static jboolean copy(JNIEnv *, jobject)
#define Q_UNLIKELY(x)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:166
static QMutexPrivate * dummyFutexValue()
Definition qmutex.cpp:22
GLenum GLenum GLsizei count
GLbitfield GLuint64 timeout
[4]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
#define Q_UNUSED(x)
size_t quintptr
Definition qtypes.h:167
QMutex mutex
[2]