1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qplatformdefs.h"
42#include "qmutex.h"
43#include "qatomic.h"
44#include "qmutex_p.h"
45#include "qfutex_p.h"
46
47#ifndef QT_ALWAYS_USE_FUTEX
48# error "Qt build is broken: qmutex_linux.cpp is being built but futex support is not wanted"
49#endif
50
51#ifndef FUTEX_PRIVATE_FLAG
52# define FUTEX_PRIVATE_FLAG 0
53#endif
54
55QT_BEGIN_NAMESPACE
56
57using namespace QtFutex;
58
59/*
60 * QBasicMutex implementation on Linux with futexes
61 *
62 * QBasicMutex contains one pointer value, which can contain one of four
63 * different values:
64 * 0x0 unlocked, non-recursive mutex
65 * 0x1 locked non-recursive mutex, no waiters
66 * 0x3 locked non-recursive mutex, at least one waiter
67 * > 0x3 recursive mutex, points to a QMutexPrivate object
68 *
69 * LOCKING (non-recursive):
70 *
71 * A non-recursive mutex starts in the 0x0 state, indicating that it's
72 * unlocked. When the first thread attempts to lock it, it will perform a
73 * testAndSetAcquire from 0x0 to 0x1. If that succeeds, the caller concludes
74 * that it successfully locked the mutex. That happens in fastTryLock().
75 *
76 * If that testAndSetAcquire fails, QBasicMutex::lockInternal is called.
77 *
78 * lockInternal will examine the value of the pointer. Otherwise, it will use
79 * futexes to sleep and wait for another thread to unlock. To do that, it needs
80 * to set a pointer value of 0x3, which indicates that thread is waiting. It
81 * does that by a simple fetchAndStoreAcquire operation.
82 *
83 * If the pointer value was 0x0, it means we succeeded in acquiring the mutex.
84 * For other values, it will then call FUTEX_WAIT and with an expected value of
85 * 0x3.
86 *
87 * If the pointer value changed before futex(2) managed to sleep, it will
88 * return -1 / EWOULDBLOCK, in which case we have to start over. And even if we
89 * are woken up directly by a FUTEX_WAKE, we need to acquire the mutex, so we
90 * start over again.
91 *
92 * UNLOCKING (non-recursive):
93 *
94 * To unlock, we need to set a value of 0x0 to indicate it's unlocked. The
95 * first attempt is a testAndSetRelease operation from 0x1 to 0x0. If that
96 * succeeds, we're done.
97 *
98 * If it fails, unlockInternal() is called. The only possibility is that the
99 * mutex value was 0x3, which indicates some other thread is waiting or was
100 * waiting in the past. We then set the mutex to 0x0 and perform a FUTEX_WAKE.
101 */
102
103static inline QMutexData *dummyFutexValue()
104{
105 return reinterpret_cast<QMutexData *>(quintptr(3));
106}
107
108template <bool IsTimed> static inline
109bool lockInternal_helper(QBasicAtomicPointer<QMutexData> &d_ptr, int timeout = -1, QElapsedTimer *elapsedTimer = nullptr) noexcept
110{
111 if (!IsTimed)
112 timeout = -1;
113
114 // we're here because fastTryLock() has just failed
115 if (timeout == 0)
116 return false;
117
118 // the mutex is locked already, set a bit indicating we're waiting
119 if (d_ptr.fetchAndStoreAcquire(newValue: dummyFutexValue()) == nullptr)
120 return true;
121
122 qint64 nstimeout = timeout * Q_INT64_C(1000) * 1000;
123 qint64 remainingTime = nstimeout;
124 forever {
125 // successfully set the waiting bit, now sleep
126 if (IsTimed && nstimeout >= 0) {
127 bool r = futexWait(futex&: d_ptr, expectedValue: dummyFutexValue(), nstimeout: remainingTime);
128 if (!r)
129 return false;
130
131 // we got woken up, so try to acquire the mutex
132 // note we must set to dummyFutexValue because there could be other threads
133 // also waiting
134 if (d_ptr.fetchAndStoreAcquire(newValue: dummyFutexValue()) == nullptr)
135 return true;
136
137 // recalculate the timeout
138 remainingTime = nstimeout - elapsedTimer->nsecsElapsed();
139 if (remainingTime <= 0)
140 return false;
141 } else {
142 futexWait(futex&: d_ptr, expectedValue: dummyFutexValue());
143
144 // we got woken up, so try to acquire the mutex
145 // note we must set to dummyFutexValue because there could be other threads
146 // also waiting
147 if (d_ptr.fetchAndStoreAcquire(newValue: dummyFutexValue()) == nullptr)
148 return true;
149 }
150 }
151
152 Q_ASSERT(d_ptr.loadRelaxed());
153 return true;
154}
155
156void QBasicMutex::lockInternal() noexcept
157{
158 Q_ASSERT(!isRecursive());
159 lockInternal_helper<false>(d_ptr);
160}
161
162bool QBasicMutex::lockInternal(int timeout) noexcept
163{
164 Q_ASSERT(!isRecursive());
165 QElapsedTimer elapsedTimer;
166 elapsedTimer.start();
167 return lockInternal_helper<true>(d_ptr, timeout, elapsedTimer: &elapsedTimer);
168}
169
170void QBasicMutex::unlockInternal() noexcept
171{
172 QMutexData *d = d_ptr.loadRelaxed();
173 Q_ASSERT(d); //we must be locked
174 Q_ASSERT(d != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed
175 Q_UNUSED(d);
176 Q_ASSERT(!isRecursive());
177
178 d_ptr.storeRelease(newValue: nullptr);
179 futexWakeOne(futex&: d_ptr);
180}
181
182QT_END_NAMESPACE
183

source code of qtbase/src/corelib/thread/qmutex_linux.cpp