1 /**************************************************************************** 2 ** 3 ** Copyright (C) 2016 The Qt Company Ltd. 4 ** Contact: https://www.qt.io/licensing/ 5 ** 6 ** This file is part of the QtCore module of the Qt Toolkit. 7 ** 8 ** $QT_BEGIN_LICENSE:LGPL$ 9 ** Commercial License Usage 10 ** Licensees holding valid commercial Qt licenses may use this file in 11 ** accordance with the commercial license agreement provided with the 12 ** Software or, alternatively, in accordance with the terms contained in 13 ** a written agreement between you and The Qt Company. For licensing terms 14 ** and conditions see https://www.qt.io/terms-conditions. For further 15 ** information use the contact form at https://www.qt.io/contact-us. 16 ** 17 ** GNU Lesser General Public License Usage 18 ** Alternatively, this file may be used under the terms of the GNU Lesser 19 ** General Public License version 3 as published by the Free Software 20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the 21 ** packaging of this file. Please review the following information to 22 ** ensure the GNU Lesser General Public License version 3 requirements 23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24 ** 25 ** GNU General Public License Usage 26 ** Alternatively, this file may be used under the terms of the GNU 27 ** General Public License version 2.0 or (at your option) the GNU General 28 ** Public license version 3 or any later version approved by the KDE Free 29 ** Qt Foundation. The licenses are as published by the Free Software 30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31 ** included in the packaging of this file. Please review the following 32 ** information to ensure the GNU General Public License requirements will 33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and 34 ** https://www.gnu.org/licenses/gpl-3.0.html. 35 ** 36 ** $QT_END_LICENSE$ 37 ** 38 ****************************************************************************/ 39 40 #ifndef QMUTEX_H 41 #define QMUTEX_H 42 43 #include <QtCore/qglobal.h> 44 #include <QtCore/qatomic.h> 45 #include <new> 46 47 #if __has_include(<chrono>) 48 # include <chrono> 49 # include <limits> 50 #endif 51 52 class tst_QMutex; 53 54 QT_BEGIN_NAMESPACE 55 56 57 #if QT_CONFIG(thread) || defined(Q_CLANG_QDOC) 58 59 #ifdef Q_OS_LINUX 60 # define QT_MUTEX_LOCK_NOEXCEPT noexcept 61 #else 62 # define QT_MUTEX_LOCK_NOEXCEPT 63 #endif 64 65 class QMutex; 66 class QRecursiveMutex; 67 class QMutexData; 68 69 class Q_CORE_EXPORT QBasicMutex 70 { 71 public: 72 #ifdef Q_COMPILER_CONSTEXPR QBasicMutex()73 constexpr QBasicMutex() 74 : d_ptr(nullptr) 75 {} 76 #endif 77 78 // BasicLockable concept lock()79 inline void lock() QT_MUTEX_LOCK_NOEXCEPT { 80 if (!fastTryLock()) 81 lockInternal(); 82 } 83 84 // BasicLockable concept unlock()85 inline void unlock() noexcept { 86 Q_ASSERT(d_ptr.loadRelaxed()); //mutex must be locked 87 if (!fastTryUnlock()) 88 unlockInternal(); 89 } 90 tryLock()91 bool tryLock() noexcept { 92 return fastTryLock(); 93 } 94 95 // Lockable concept try_lock()96 bool try_lock() noexcept { return tryLock(); } 97 98 bool isRecursive() noexcept; //### Qt6: remove me 99 bool isRecursive() const noexcept; 100 101 private: fastTryLock()102 inline bool fastTryLock() noexcept { 103 return d_ptr.testAndSetAcquire(nullptr, dummyLocked()); 104 } fastTryUnlock()105 inline bool fastTryUnlock() noexcept { 106 return d_ptr.testAndSetRelease(dummyLocked(), nullptr); 107 } fastTryLock(QMutexData * & current)108 inline bool fastTryLock(QMutexData *¤t) noexcept { 109 return d_ptr.testAndSetAcquire(nullptr, dummyLocked(), current); 110 } fastTryUnlock(QMutexData * & current)111 inline bool fastTryUnlock(QMutexData *¤t) noexcept { 112 return d_ptr.testAndSetRelease(dummyLocked(), nullptr, current); 113 } 114 115 void lockInternal() QT_MUTEX_LOCK_NOEXCEPT; 116 bool lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT; 117 void unlockInternal() noexcept; 118 119 QBasicAtomicPointer<QMutexData> d_ptr; dummyLocked()120 static inline QMutexData *dummyLocked() { 121 return reinterpret_cast<QMutexData *>(quintptr(1)); 122 } 123 124 friend class QMutex; 125 friend class QRecursiveMutex; 126 friend class QMutexData; 127 }; 128 129 class Q_CORE_EXPORT QMutex : public QBasicMutex 130 { 131 public: 132 #if defined(Q_COMPILER_CONSTEXPR) && !defined(Q_CC_INTEL) 133 constexpr QMutex() = default; 134 #else 135 QMutex() { d_ptr.storeRelaxed(nullptr); } 136 #endif 137 #if QT_DEPRECATED_SINCE(5,15) 138 enum RecursionMode { NonRecursive, Recursive }; 139 QT_DEPRECATED_VERSION_X(5, 15, "Use QRecursiveMutex instead of a recursive QMutex") 140 explicit QMutex(RecursionMode mode); 141 142 QT_DEPRECATED_VERSION_X(5, 15, "Use QRecursiveMutex instead of a recursive QMutex") isRecursive()143 bool isRecursive() const noexcept 144 { return QBasicMutex::isRecursive(); } 145 #endif 146 147 ~QMutex(); 148 149 // BasicLockable concept 150 void lock() QT_MUTEX_LOCK_NOEXCEPT; 151 bool tryLock(int timeout = 0) QT_MUTEX_LOCK_NOEXCEPT; 152 // BasicLockable concept 153 void unlock() noexcept; 154 155 // Lockable concept try_lock()156 bool try_lock() QT_MUTEX_LOCK_NOEXCEPT { return tryLock(); } 157 158 #if __has_include(<chrono>) || defined(Q_CLANG_QDOC) 159 // TimedLockable concept 160 template <class Rep, class Period> try_lock_for(std::chrono::duration<Rep,Period> duration)161 bool try_lock_for(std::chrono::duration<Rep, Period> duration) 162 { 163 return tryLock(convertToMilliseconds(duration)); 164 } 165 166 // TimedLockable concept 167 template<class Clock, class Duration> try_lock_until(std::chrono::time_point<Clock,Duration> timePoint)168 bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint) 169 { 170 // Implemented in terms of try_lock_for to honor the similar 171 // requirement in N4606 § 30.4.1.3 [thread.timedmutex.requirements]/12. 172 173 return try_lock_for(timePoint - Clock::now()); 174 } 175 #endif 176 177 private: 178 Q_DISABLE_COPY(QMutex) 179 friend class QMutexLocker; 180 friend class QRecursiveMutex; 181 friend class ::tst_QMutex; 182 183 #if __has_include(<chrono>) 184 template<class Rep, class Period> convertToMilliseconds(std::chrono::duration<Rep,Period> duration)185 static int convertToMilliseconds(std::chrono::duration<Rep, Period> duration) 186 { 187 // N4606 § 30.4.1.3.5 [thread.timedmutex.requirements] specifies that a 188 // duration less than or equal to duration.zero() shall result in a 189 // try_lock, unlike QMutex's tryLock with a negative duration which 190 // results in a lock. 191 192 if (duration <= duration.zero()) 193 return 0; 194 195 // when converting from 'duration' to milliseconds, make sure that 196 // the result is not shorter than 'duration': 197 std::chrono::milliseconds wait = std::chrono::duration_cast<std::chrono::milliseconds>(duration); 198 if (wait < duration) 199 wait += std::chrono::milliseconds(1); 200 Q_ASSERT(wait >= duration); 201 const auto ms = wait.count(); 202 const auto maxInt = (std::numeric_limits<int>::max)(); 203 204 return ms < maxInt ? int(ms) : maxInt; 205 } 206 #endif 207 }; 208 209 class QRecursiveMutex : private QMutex 210 { 211 // ### Qt 6: make it independent of QMutex 212 friend class QMutexLocker; 213 public: 214 Q_CORE_EXPORT QRecursiveMutex(); 215 Q_CORE_EXPORT ~QRecursiveMutex(); 216 217 using QMutex::lock; 218 using QMutex::tryLock; 219 using QMutex::unlock; 220 using QMutex::try_lock; 221 #if __has_include(<chrono>) 222 using QMutex::try_lock_for; 223 using QMutex::try_lock_until; 224 #endif 225 }; 226 227 class Q_CORE_EXPORT QMutexLocker 228 { 229 public: 230 #ifndef Q_CLANG_QDOC QMutexLocker(QBasicMutex * m)231 inline explicit QMutexLocker(QBasicMutex *m) QT_MUTEX_LOCK_NOEXCEPT 232 { 233 Q_ASSERT_X((reinterpret_cast<quintptr>(m) & quintptr(1u)) == quintptr(0), 234 "QMutexLocker", "QMutex pointer is misaligned"); 235 val = quintptr(m); 236 if (Q_LIKELY(m)) { 237 // call QMutex::lock() instead of QBasicMutex::lock() 238 static_cast<QMutex *>(m)->lock(); 239 val |= 1; 240 } 241 } QMutexLocker(QRecursiveMutex * m)242 explicit QMutexLocker(QRecursiveMutex *m) QT_MUTEX_LOCK_NOEXCEPT 243 : QMutexLocker{static_cast<QBasicMutex*>(m)} {} 244 #else 245 QMutexLocker(QMutex *) { } 246 QMutexLocker(QRecursiveMutex *) {} 247 #endif ~QMutexLocker()248 inline ~QMutexLocker() { unlock(); } 249 unlock()250 inline void unlock() noexcept 251 { 252 if ((val & quintptr(1u)) == quintptr(1u)) { 253 val &= ~quintptr(1u); 254 mutex()->unlock(); 255 } 256 } 257 relock()258 inline void relock() QT_MUTEX_LOCK_NOEXCEPT 259 { 260 if (val) { 261 if ((val & quintptr(1u)) == quintptr(0u)) { 262 mutex()->lock(); 263 val |= quintptr(1u); 264 } 265 } 266 } 267 268 #if defined(Q_CC_MSVC) 269 #pragma warning( push ) 270 #pragma warning( disable : 4312 ) // ignoring the warning from /Wp64 271 #endif 272 mutex()273 inline QMutex *mutex() const 274 { 275 return reinterpret_cast<QMutex *>(val & ~quintptr(1u)); 276 } 277 278 #if defined(Q_CC_MSVC) 279 #pragma warning( pop ) 280 #endif 281 282 private: 283 Q_DISABLE_COPY(QMutexLocker) 284 285 quintptr val; 286 }; 287 288 #else // !QT_CONFIG(thread) && !Q_CLANG_QDOC 289 290 class Q_CORE_EXPORT QMutex 291 { 292 public: 293 enum RecursionMode { NonRecursive, Recursive }; 294 295 inline Q_DECL_CONSTEXPR explicit QMutex(RecursionMode = NonRecursive) noexcept { } 296 297 inline void lock() noexcept {} 298 inline bool tryLock(int timeout = 0) noexcept { Q_UNUSED(timeout); return true; } 299 inline bool try_lock() noexcept { return true; } 300 inline void unlock() noexcept {} 301 inline bool isRecursive() const noexcept { return true; } 302 303 #if __has_include(<chrono>) 304 template <class Rep, class Period> 305 inline bool try_lock_for(std::chrono::duration<Rep, Period> duration) noexcept 306 { 307 Q_UNUSED(duration); 308 return true; 309 } 310 311 template<class Clock, class Duration> 312 inline bool try_lock_until(std::chrono::time_point<Clock, Duration> timePoint) noexcept 313 { 314 Q_UNUSED(timePoint); 315 return true; 316 } 317 #endif 318 319 private: 320 Q_DISABLE_COPY(QMutex) 321 }; 322 323 class QRecursiveMutex : public QMutex {}; 324 325 class Q_CORE_EXPORT QMutexLocker 326 { 327 public: 328 inline explicit QMutexLocker(QMutex *) noexcept {} 329 inline ~QMutexLocker() noexcept {} 330 331 inline void unlock() noexcept {} 332 void relock() noexcept {} 333 inline QMutex *mutex() const noexcept { return nullptr; } 334 335 private: 336 Q_DISABLE_COPY(QMutexLocker) 337 }; 338 339 typedef QMutex QBasicMutex; 340 341 #endif // !QT_CONFIG(thread) && !Q_CLANG_QDOC 342 343 QT_END_NAMESPACE 344 345 #endif // QMUTEX_H 346