Bug 1709347 - Add CanvasRenderingContext2D.reset(). r=lsalzman,webidl,smaug
[gecko.git] / xpcom / threads / RWLock.h
blobe03d008631f258ea1e25366795bd2fc772a75ce1
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // An interface for read-write locks.
9 #ifndef mozilla_RWLock_h
10 #define mozilla_RWLock_h
12 #include "mozilla/Assertions.h"
13 #include "mozilla/Atomics.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/BlockingResourceBase.h"
16 #include "mozilla/PlatformRWLock.h"
17 #include "mozilla/ThreadSafety.h"
19 namespace mozilla {
21 // A RWLock is similar to a Mutex, but whereas a Mutex permits only a single
22 // reader thread or a single writer thread to access a piece of data, a
23 // RWLock distinguishes between readers and writers: you may have multiple
24 // reader threads concurrently accessing a piece of data or a single writer
25 // thread. This difference should guide your usage of RWLock: if you are not
26 // reading the data from multiple threads simultaneously or you are writing
27 // to the data roughly as often as read from it, then Mutex will suit your
28 // purposes just fine.
30 // You should be using the AutoReadLock and AutoWriteLock classes, below,
31 // for RAII read locking and write locking, respectively. If you really must
32 // take a read lock manually, call the ReadLock method; to relinquish that
33 // read lock, call the ReadUnlock method. Similarly, WriteLock and WriteUnlock
34 // perform the same operations, but for write locks.
36 // It is unspecified what happens when a given thread attempts to acquire the
37 // same lock in multiple ways; some underlying implementations of RWLock do
38 // support acquiring a read lock multiple times on a given thread, but you
39 // should not rely on this behavior.
41 // It is unspecified whether RWLock gives priority to waiting readers or
42 // a waiting writer when unlocking.
43 class MOZ_CAPABILITY("rwlock") RWLock : public detail::RWLockImpl,
44 public BlockingResourceBase {
45 public:
46 explicit RWLock(const char* aName);
48 #ifdef DEBUG
49 bool LockedForWritingByCurrentThread();
50 [[nodiscard]] bool TryReadLock() MOZ_SHARED_TRYLOCK_FUNCTION(true);
51 void ReadLock() MOZ_ACQUIRE_SHARED();
52 void ReadUnlock() MOZ_RELEASE_SHARED();
53 [[nodiscard]] bool TryWriteLock() MOZ_TRY_ACQUIRE(true);
54 void WriteLock() MOZ_CAPABILITY_ACQUIRE();
55 void WriteUnlock() MOZ_EXCLUSIVE_RELEASE();
56 #else
57 [[nodiscard]] bool TryReadLock() MOZ_SHARED_TRYLOCK_FUNCTION(true) {
58 return detail::RWLockImpl::tryReadLock();
60 void ReadLock() MOZ_ACQUIRE_SHARED() { detail::RWLockImpl::readLock(); }
61 void ReadUnlock() MOZ_RELEASE_SHARED() { detail::RWLockImpl::readUnlock(); }
62 [[nodiscard]] bool TryWriteLock() MOZ_TRY_ACQUIRE(true) {
63 return detail::RWLockImpl::tryWriteLock();
65 void WriteLock() MOZ_CAPABILITY_ACQUIRE() { detail::RWLockImpl::writeLock(); }
66 void WriteUnlock() MOZ_EXCLUSIVE_RELEASE() {
67 detail::RWLockImpl::writeUnlock();
69 #endif
71 private:
72 RWLock() = delete;
73 RWLock(const RWLock&) = delete;
74 RWLock& operator=(const RWLock&) = delete;
76 #ifdef DEBUG
77 // We record the owning thread for write locks only.
78 PRThread* mOwningThread;
79 #endif
82 // We only use this once; not sure we can add thread safety attributions here
83 template <typename T>
84 class MOZ_RAII BaseAutoTryReadLock {
85 public:
86 explicit BaseAutoTryReadLock(T& aLock)
87 : mLock(aLock.TryReadLock() ? &aLock : nullptr) {}
89 ~BaseAutoTryReadLock() {
90 if (mLock) {
91 mLock->ReadUnlock();
95 explicit operator bool() const { return mLock; }
97 private:
98 BaseAutoTryReadLock() = delete;
99 BaseAutoTryReadLock(const BaseAutoTryReadLock&) = delete;
100 BaseAutoTryReadLock& operator=(const BaseAutoTryReadLock&) = delete;
102 T* mLock;
105 template <typename T>
106 class MOZ_SCOPED_CAPABILITY MOZ_RAII BaseAutoReadLock {
107 public:
108 explicit BaseAutoReadLock(T& aLock) MOZ_ACQUIRE_SHARED(aLock)
109 : mLock(&aLock) {
110 MOZ_ASSERT(mLock, "null lock");
111 mLock->ReadLock();
114 // Not MOZ_RELEASE_SHARED(), which would make sense - apparently this trips
115 // over a bug in clang's static analyzer and it says it expected an
116 // exclusive unlock.
117 ~BaseAutoReadLock() MOZ_RELEASE_GENERIC() { mLock->ReadUnlock(); }
119 private:
120 BaseAutoReadLock() = delete;
121 BaseAutoReadLock(const BaseAutoReadLock&) = delete;
122 BaseAutoReadLock& operator=(const BaseAutoReadLock&) = delete;
124 T* mLock;
127 // XXX Mutex attributions?
128 template <typename T>
129 class MOZ_RAII BaseAutoTryWriteLock {
130 public:
131 explicit BaseAutoTryWriteLock(T& aLock)
132 : mLock(aLock.TryWriteLock() ? &aLock : nullptr) {}
134 ~BaseAutoTryWriteLock() {
135 if (mLock) {
136 mLock->WriteUnlock();
140 explicit operator bool() const { return mLock; }
142 private:
143 BaseAutoTryWriteLock() = delete;
144 BaseAutoTryWriteLock(const BaseAutoTryWriteLock&) = delete;
145 BaseAutoTryWriteLock& operator=(const BaseAutoTryWriteLock&) = delete;
147 T* mLock;
150 template <typename T>
151 class MOZ_SCOPED_CAPABILITY MOZ_RAII BaseAutoWriteLock final {
152 public:
153 explicit BaseAutoWriteLock(T& aLock) MOZ_CAPABILITY_ACQUIRE(aLock)
154 : mLock(&aLock) {
155 MOZ_ASSERT(mLock, "null lock");
156 mLock->WriteLock();
159 ~BaseAutoWriteLock() MOZ_CAPABILITY_RELEASE() { mLock->WriteUnlock(); }
161 private:
162 BaseAutoWriteLock() = delete;
163 BaseAutoWriteLock(const BaseAutoWriteLock&) = delete;
164 BaseAutoWriteLock& operator=(const BaseAutoWriteLock&) = delete;
166 T* mLock;
169 // Read try-lock and unlock a RWLock with RAII semantics. Much preferred to
170 // bare calls to TryReadLock() and ReadUnlock().
171 typedef BaseAutoTryReadLock<RWLock> AutoTryReadLock;
173 // Read lock and unlock a RWLock with RAII semantics. Much preferred to bare
174 // calls to ReadLock() and ReadUnlock().
175 typedef BaseAutoReadLock<RWLock> AutoReadLock;
177 // Write try-lock and unlock a RWLock with RAII semantics. Much preferred to
178 // bare calls to TryWriteLock() and WriteUnlock().
179 typedef BaseAutoTryWriteLock<RWLock> AutoTryWriteLock;
181 // Write lock and unlock a RWLock with RAII semantics. Much preferred to bare
182 // calls to WriteLock() and WriteUnlock().
183 typedef BaseAutoWriteLock<RWLock> AutoWriteLock;
185 class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS MOZ_CAPABILITY("rwlock")
186 StaticRWLock {
187 public:
188 // In debug builds, check that mLock is initialized for us as we expect by
189 // the compiler. In non-debug builds, don't declare a constructor so that
190 // the compiler can see that the constructor is trivial.
191 #ifdef DEBUG
192 StaticRWLock() { MOZ_ASSERT(!mLock); }
193 #endif
195 [[nodiscard]] bool TryReadLock() MOZ_SHARED_TRYLOCK_FUNCTION(true) {
196 return Lock()->TryReadLock();
198 void ReadLock() MOZ_ACQUIRE_SHARED() { Lock()->ReadLock(); }
199 void ReadUnlock() MOZ_RELEASE_SHARED() { Lock()->ReadUnlock(); }
200 [[nodiscard]] bool TryWriteLock() MOZ_TRY_ACQUIRE(true) {
201 return Lock()->TryWriteLock();
203 void WriteLock() MOZ_CAPABILITY_ACQUIRE() { Lock()->WriteLock(); }
204 void WriteUnlock() MOZ_EXCLUSIVE_RELEASE() { Lock()->WriteUnlock(); }
206 private:
207 [[nodiscard]] RWLock* Lock() MOZ_RETURN_CAPABILITY(*mLock) {
208 if (mLock) {
209 return mLock;
212 RWLock* lock = new RWLock("StaticRWLock");
213 if (!mLock.compareExchange(nullptr, lock)) {
214 delete lock;
217 return mLock;
220 Atomic<RWLock*> mLock;
222 // Disallow copy constructor, but only in debug mode. We only define
223 // a default constructor in debug mode (see above); if we declared
224 // this constructor always, the compiler wouldn't generate a trivial
225 // default constructor for us in non-debug mode.
226 #ifdef DEBUG
227 StaticRWLock(const StaticRWLock& aOther);
228 #endif
230 // Disallow these operators.
231 StaticRWLock& operator=(StaticRWLock* aRhs) = delete;
232 static void* operator new(size_t) noexcept(true) = delete;
233 static void operator delete(void*) = delete;
236 typedef BaseAutoTryReadLock<StaticRWLock> StaticAutoTryReadLock;
237 typedef BaseAutoReadLock<StaticRWLock> StaticAutoReadLock;
238 typedef BaseAutoTryWriteLock<StaticRWLock> StaticAutoTryWriteLock;
239 typedef BaseAutoWriteLock<StaticRWLock> StaticAutoWriteLock;
241 } // namespace mozilla
243 #endif // mozilla_RWLock_h