Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qreadwritelock.h
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#ifndef QREADWRITELOCK_H
6#define QREADWRITELOCK_H
7
8#include <QtCore/qglobal.h>
9#include <QtCore/qdeadlinetimer.h>
10#include <QtCore/qtsan_impl.h>
11
13
14#if QT_CONFIG(thread)
15
16class QReadWriteLockPrivate;
17
18class QBasicReadWriteLock
19{
20public:
21 constexpr QBasicReadWriteLock() = default;
22
23 void lockForRead()
24 {
25 tryLockForReadInternal(QDeadlineTimer::Forever, 0);
26 }
27 bool tryLockForRead()
28 {
29 return tryLockForReadInternal(QDeadlineTimer(), QtTsan::TryLock);
30 }
31 bool tryLockForRead(QDeadlineTimer timeout)
32 {
33 return tryLockForReadInternal(timeout, QtTsan::TryLock);
34 }
35
36 void lockForWrite()
37 {
38 tryLockForWriteInternal(QDeadlineTimer::Forever, 0);
39 }
40 bool tryLockForWrite()
41 {
42 return tryLockForWriteInternal(QDeadlineTimer(), QtTsan::TryLock);
43 }
44 bool tryLockForWrite(QDeadlineTimer timeout)
45 {
46 return tryLockForWriteInternal(timeout, QtTsan::TryLock);
47 }
48
49 void unlock()
50 {
51 unsigned flags = 0;
52 QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
53 quintptr u = quintptr(d);
54 Q_ASSERT_X(u, "QReadWriteLock::unlock()", "Cannot unlock an unlocked lock");
55 if (u & StateLockedForRead)
56 flags |= QtTsan::ReadLock; // ### will be wrong for past-contention read locks
57
58 QtTsan::mutexPreUnlock(this, flags);
59 if (u > StateMask || !d_ptr.testAndSetRelease(d, nullptr, d))
60 contendedUnlock(d);
61 QtTsan::mutexPostUnlock(this, flags);
62 }
63
64 // std::shared_mutex API:
65 void lock() { lockForWrite(); }
66 void lock_shared() { lockForRead(); }
67 bool try_lock() { return tryLockForWrite(); }
68 bool try_lock_shared() { return tryLockForRead(); }
69 void unlock_shared() { unlock(); }
70
71protected:
72 static constexpr quintptr StateLockedForRead = 0x1;
73 static constexpr quintptr StateLockedForWrite = 0x2;
74 static constexpr quintptr StateMask = StateLockedForRead | StateLockedForWrite;
75 static constexpr quintptr Counter = 0x10;
76
77 Q_ALWAYS_INLINE bool fastTryLockForRead(QReadWriteLockPrivate *&d)
78 {
79 if (d == nullptr) {
80 auto dummyValue = reinterpret_cast<QReadWriteLockPrivate *>(StateLockedForRead);
81 return d_ptr.testAndSetAcquire(nullptr, dummyValue, d);
82 } else if (quintptr u = quintptr(d), v = u + Counter; u & StateLockedForRead) {
83 return d_ptr.testAndSetAcquire(d, reinterpret_cast<QReadWriteLockPrivate *>(v), d);
84 }
85 return false;
86 }
87
88 Q_ALWAYS_INLINE bool tryLockForReadInternal(QDeadlineTimer timeout, unsigned tsanFlags)
89 {
90 tsanFlags |= QtTsan::ReadLock;
91 QtTsan::mutexPreLock(this, tsanFlags);
92
93 QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
94 bool locked = fastTryLockForRead(d);
95 if (!locked)
96 locked = contendedTryLockForRead(timeout, d);
97
98 if (!locked)
99 tsanFlags |= QtTsan::TryLockFailed;
100 QtTsan::mutexPostLock(this, tsanFlags, 0);
101 return locked;
102 }
103
104 Q_ALWAYS_INLINE bool fastTryLockForWrite(QReadWriteLockPrivate *&d)
105 {
106 auto dummyValue = reinterpret_cast<QReadWriteLockPrivate *>(StateLockedForWrite);
107 if (d == nullptr)
108 return d_ptr.testAndSetAcquire(nullptr, dummyValue, d);
109 return false;
110 }
111
112 Q_ALWAYS_INLINE bool tryLockForWriteInternal(QDeadlineTimer timeout, unsigned tsanFlags)
113 {
114 QtTsan::mutexPreLock(this, tsanFlags);
115
116 QReadWriteLockPrivate *d = d_ptr.loadRelaxed();
117 bool locked = fastTryLockForWrite(d);
118 if (!locked)
119 locked = contendedTryLockForWrite(timeout, d);
120
121 if (!locked)
122 tsanFlags |= QtTsan::TryLockFailed;
123 QtTsan::mutexPostLock(this, tsanFlags, 0);
124 return locked;
125 }
126
127 Q_CORE_EXPORT bool contendedTryLockForRead(QDeadlineTimer timeout, void *dd);
128 Q_CORE_EXPORT bool contendedTryLockForWrite(QDeadlineTimer timeout, void *dd);
129 Q_CORE_EXPORT void contendedUnlock(void *dd);
130
131 constexpr QBasicReadWriteLock(QReadWriteLockPrivate *d) noexcept : d_ptr(d)
132 {}
133 Q_DISABLE_COPY(QBasicReadWriteLock)
134 QAtomicPointer<QReadWriteLockPrivate> d_ptr = { nullptr };
135 friend class QReadWriteLockPrivate;
136};
137
138class Q_CORE_EXPORT QReadWriteLock : public QBasicReadWriteLock
139{
140public:
141 enum RecursionMode { NonRecursive, Recursive };
142
143 QT_CORE_INLINE_SINCE(6, 6)
144 explicit QReadWriteLock(RecursionMode recursionMode = NonRecursive);
145 QT_CORE_INLINE_SINCE(6, 6)
146 ~QReadWriteLock();
147
148#if QT_CORE_REMOVED_SINCE(6, 11) || defined(Q_QDOC)
149 // was: QT_CORE_INLINE_SINCE(6, 6)
150 void lockForRead();
151 bool tryLockForRead();
152#endif
153 QT_CORE_INLINE_SINCE(6, 6)
154 bool tryLockForRead(int timeout);
155#if QT_CORE_REMOVED_SINCE(6, 11) || defined(Q_QDOC)
156 bool tryLockForRead(QDeadlineTimer timeout = {});
157#endif
158 using QBasicReadWriteLock::tryLockForRead;
159
160#if QT_CORE_REMOVED_SINCE(6, 11) || defined(Q_QDOC)
161 // was: QT_CORE_INLINE_SINCE(6, 6)
162 void lockForWrite();
163 bool tryLockForWrite();
164#endif
165 QT_CORE_INLINE_SINCE(6, 6)
166 bool tryLockForWrite(int timeout);
167#if QT_CORE_REMOVED_SINCE(6, 11) || defined(Q_QDOC)
168 bool tryLockForWrite(QDeadlineTimer timeout = {});
169#endif
170 using QBasicReadWriteLock::tryLockForWrite;
171
172#if QT_CORE_REMOVED_SINCE(6, 11) || defined(Q_QDOC)
173 void unlock();
174#endif
175
176private:
177 static QReadWriteLockPrivate *initRecursive();
178 static void destroyRecursive(QReadWriteLockPrivate *);
179};
180
181#if QT_CORE_INLINE_IMPL_SINCE(6, 6)
182QReadWriteLock::QReadWriteLock(RecursionMode recursionMode)
183 : QBasicReadWriteLock(recursionMode == Recursive ? initRecursive() : nullptr)
184{
185}
186
187QReadWriteLock::~QReadWriteLock()
188{
189 if (auto d = d_ptr.loadAcquire())
190 destroyRecursive(d);
191}
192
193bool QReadWriteLock::tryLockForRead(int timeout)
194{
195 return tryLockForRead(QDeadlineTimer(timeout));
196}
197
198bool QReadWriteLock::tryLockForWrite(int timeout)
199{
200 return tryLockForWrite(QDeadlineTimer(timeout));
201}
202#endif // inline since 6.6
203
204#if defined(Q_CC_MSVC)
205#pragma warning( push )
206#pragma warning( disable : 4312 ) // ignoring the warning from /Wp64
207#endif
208
209class QT6_ONLY(Q_CORE_EXPORT) QReadLocker
210{
211public:
212 Q_NODISCARD_CTOR
213 inline QReadLocker(QReadWriteLock *readWriteLock);
214
215 inline ~QReadLocker()
216 { unlock(); }
217
218 inline void unlock()
219 {
220 if (q_val) {
221 if ((q_val & quintptr(1u)) == quintptr(1u)) {
222 q_val &= ~quintptr(1u);
223 readWriteLock()->unlock();
224 }
225 }
226 }
227
228 inline void relock()
229 {
230 if (q_val) {
231 if ((q_val & quintptr(1u)) == quintptr(0u)) {
232 readWriteLock()->lockForRead();
233 q_val |= quintptr(1u);
234 }
235 }
236 }
237
238 inline QReadWriteLock *readWriteLock() const
239 { return reinterpret_cast<QReadWriteLock *>(q_val & ~quintptr(1u)); }
240
241private:
242 Q_DISABLE_COPY(QReadLocker)
243 quintptr q_val;
244};
245
246inline QReadLocker::QReadLocker(QReadWriteLock *areadWriteLock)
247 : q_val(reinterpret_cast<quintptr>(areadWriteLock))
248{
249 Q_ASSERT_X((q_val & quintptr(1u)) == quintptr(0),
250 "QReadLocker", "QReadWriteLock pointer is misaligned");
251 relock();
252}
253
254class QT6_ONLY(Q_CORE_EXPORT) QWriteLocker
255{
256public:
257 Q_NODISCARD_CTOR
258 inline QWriteLocker(QReadWriteLock *readWriteLock);
259
260 inline ~QWriteLocker()
261 { unlock(); }
262
263 inline void unlock()
264 {
265 if (q_val) {
266 if ((q_val & quintptr(1u)) == quintptr(1u)) {
267 q_val &= ~quintptr(1u);
268 readWriteLock()->unlock();
269 }
270 }
271 }
272
273 inline void relock()
274 {
275 if (q_val) {
276 if ((q_val & quintptr(1u)) == quintptr(0u)) {
277 readWriteLock()->lockForWrite();
278 q_val |= quintptr(1u);
279 }
280 }
281 }
282
283 inline QReadWriteLock *readWriteLock() const
284 { return reinterpret_cast<QReadWriteLock *>(q_val & ~quintptr(1u)); }
285
286
287private:
288 Q_DISABLE_COPY(QWriteLocker)
289 quintptr q_val;
290};
291
292inline QWriteLocker::QWriteLocker(QReadWriteLock *areadWriteLock)
293 : q_val(reinterpret_cast<quintptr>(areadWriteLock))
294{
295 Q_ASSERT_X((q_val & quintptr(1u)) == quintptr(0),
296 "QWriteLocker", "QReadWriteLock pointer is misaligned");
297 relock();
298}
299
300#if defined(Q_CC_MSVC)
301#pragma warning( pop )
302#endif
303
304#else // QT_CONFIG(thread)
305
306class QT6_ONLY(Q_CORE_EXPORT) QReadWriteLock
307{
308public:
309 enum RecursionMode { NonRecursive, Recursive };
310 inline explicit QReadWriteLock(RecursionMode = NonRecursive) noexcept { }
311 inline ~QReadWriteLock() { }
312
313 void lockForRead() noexcept { }
314 bool tryLockForRead() noexcept { return true; }
315 bool tryLockForRead(QDeadlineTimer) noexcept { return true; }
316 bool tryLockForRead(int timeout) noexcept { Q_UNUSED(timeout); return true; }
317
318 void lockForWrite() noexcept { }
319 bool tryLockForWrite() noexcept { return true; }
320 bool tryLockForWrite(QDeadlineTimer) noexcept { return true; }
321 bool tryLockForWrite(int timeout) noexcept { Q_UNUSED(timeout); return true; }
322
323 void unlock() noexcept { }
324
325private:
326 Q_DISABLE_COPY(QReadWriteLock)
327};
328
329class QT6_ONLY(Q_CORE_EXPORT) QReadLocker
330{
331public:
332 Q_NODISCARD_CTOR
333 inline explicit QReadLocker(QReadWriteLock *) noexcept { }
334 inline ~QReadLocker() noexcept { }
335
336 void unlock() noexcept { }
337 void relock() noexcept { }
338 QReadWriteLock *readWriteLock() noexcept { return nullptr; }
339
340private:
341 Q_DISABLE_COPY(QReadLocker)
342};
343
344class QT6_ONLY(Q_CORE_EXPORT) QWriteLocker
345{
346public:
347 Q_NODISCARD_CTOR
348 inline explicit QWriteLocker(QReadWriteLock *) noexcept { }
349 inline ~QWriteLocker() noexcept { }
350
351 void unlock() noexcept { }
352 void relock() noexcept { }
353 QReadWriteLock *readWriteLock() noexcept { return nullptr; }
354
355private:
356 Q_DISABLE_COPY(QWriteLocker)
357};
358
359#endif // QT_CONFIG(thread)
360
361QT_END_NAMESPACE
362
363#endif // QREADWRITELOCK_H
static QString appendSlashIfNeeded(const QString &path)