All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
bigRWMutex.h
1//
2// Copyright 2022 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7#ifndef PXR_BASE_TF_BIG_RW_MUTEX_H
8#define PXR_BASE_TF_BIG_RW_MUTEX_H
9
10#include "pxr/pxr.h"
11#include "pxr/base/tf/api.h"
12
13#include "pxr/base/arch/align.h"
14#include "pxr/base/arch/hints.h"
16#include "pxr/base/tf/hash.h"
17#include "pxr/base/tf/spinRWMutex.h"
18
19#include <atomic>
20#include <memory>
21#include <thread>
22
23PXR_NAMESPACE_OPEN_SCOPE
24
53{
54public:
55 // Number of different cache-line-sized lock states.
56 static constexpr unsigned NumStates = 16;
57
58 // Lock states -- 0 means not locked, -1 means locked for write, other
59 // positive values count the number of readers locking this particular lock
60 // state object.
61 static constexpr int NotLocked = 0;
62 static constexpr int WriteLocked = -1;
63
65 TF_API TfBigRWMutex();
66
69 struct ScopedLock {
70
71 // Acquisition states: -1 means not acquired, -2 means acquired for
72 // write (exclusive lock), >= 0 indicates locked for read, and the value
73 // indicates which lock state index the reader has incremented.
74 static constexpr int NotAcquired = -1;
75 static constexpr int WriteAcquired = -2;
76
79 explicit ScopedLock(TfBigRWMutex &m, bool write=true)
80 : _mutex(&m)
81 , _acqState(NotAcquired) {
82 Acquire(write);
83 }
84
86 ScopedLock() : _mutex(nullptr), _acqState(NotAcquired) {}
87
91 Release();
92 }
93
97 void Acquire(TfBigRWMutex &m, bool write=true) {
98 Release();
99 _mutex = &m;
100 Acquire(write);
101 }
102
108 void Acquire(bool write=true) {
109 if (write) {
110 AcquireWrite();
111 }
112 else {
113 AcquireRead();
114 }
115 }
116
119 void Release() {
120 switch (_acqState) {
121 case NotAcquired:
122 break;
123 case WriteAcquired:
124 _ReleaseWrite();
125 break;
126 default:
127 _ReleaseRead();
128 break;
129 };
130 }
131
134 void AcquireRead() {
135 TF_AXIOM(_acqState == NotAcquired);
136 _acqState = _mutex->_AcquireRead(_GetSeed());
137 }
138
142 TF_AXIOM(_acqState == NotAcquired);
143 _mutex->_AcquireWrite();
144 _acqState = WriteAcquired;
145 }
146
154 TF_AXIOM(_acqState >= 0);
155 Release();
156 AcquireWrite();
157 return false;
158 }
159
160 private:
161
162 void _ReleaseRead() {
163 TF_AXIOM(_acqState >= 0);
164 _mutex->_ReleaseRead(_acqState);
165 _acqState = NotAcquired;
166 }
167
168 void _ReleaseWrite() {
169 TF_AXIOM(_acqState == WriteAcquired);
170 _mutex->_ReleaseWrite();
171 _acqState = NotAcquired;
172 }
173
174 // Helper for returning a seed value associated with this lock object.
175 // This helps determine which lock state a read-lock should use.
176 inline int _GetSeed() const {
177 return static_cast<int>(
178 static_cast<unsigned>(TfHash()(this)) >> 8);
179 }
180
181 TfBigRWMutex *_mutex;
182 int _acqState; // NotAcquired (-1), WriteAcquired (-2), otherwise
183 // acquired for read, and index indicates which lock
184 // state we are associated with.
185 };
186
187private:
188
189 // Optimistic read-lock case inlined.
190 inline int _AcquireRead(int seed) {
191 // Determine a lock state index to use.
192 int stateIndex = seed % NumStates;
193 if (ARCH_UNLIKELY(_writerActive) ||
194 !_states[stateIndex].mutex.TryAcquireRead()) {
195 _AcquireReadContended(stateIndex);
196 }
197 return stateIndex;
198 }
199
200 // Contended read-lock helper.
201 TF_API void _AcquireReadContended(int stateIndex);
202
203 void _ReleaseRead(int stateIndex) {
204 _states[stateIndex].mutex.ReleaseRead();
205 }
206
207 TF_API void _AcquireWrite();
208 TF_API void _ReleaseWrite();
209
210 struct _LockState {
211 TfSpinRWMutex mutex;
212 // This padding ensures that \p state instances sit on different cache
213 // lines.
214 char _unused_padding[
216 };
217
218 std::unique_ptr<_LockState []> _states;
219 std::atomic<bool> _writerActive;
220
221};
222
223PXR_NAMESPACE_CLOSE_SCOPE
224
225#endif // PXR_BASE_TF_BIG_RW_MUTEX_H
Provide architecture-specific memory-alignment information.
This class implements a readers-writer mutex and provides a scoped lock utility.
Definition: bigRWMutex.h:53
TF_API TfBigRWMutex()
Construct a mutex, initially unlocked.
A user-extensible hashing mechanism for use with runtime hash tables.
Definition: hash.h:472
This class implements a readers-writer spin lock that emphasizes throughput when there is light conte...
Definition: spinRWMutex.h:51
Stripped down version of diagnostic.h that doesn't define std::string.
#define ARCH_CACHE_LINE_SIZE
The size of a CPU cache line on the current processor architecture in bytes.
Definition: align.h:67
#define TF_AXIOM(cond)
Aborts if the condition cond is not met.
Definition: diagnostic.h:193
Compiler hints.
Scoped lock utility class.
Definition: bigRWMutex.h:69
bool UpgradeToWriter()
Change this lock's acquisition state from a read lock to a write lock.
Definition: bigRWMutex.h:153
void Acquire(bool write=true)
Acquire either a read or write lock on this lock's associated mutex depending on write.
Definition: bigRWMutex.h:108
ScopedLock()
Construct a scoped lock not associated with a mutex.
Definition: bigRWMutex.h:86
ScopedLock(TfBigRWMutex &m, bool write=true)
Construct a scoped lock for mutex m and acquire either a read or a write lock depending on write.
Definition: bigRWMutex.h:79
void Release()
Release the currently required lock on the associated mutex.
Definition: bigRWMutex.h:119
~ScopedLock()
If this scoped lock is acquired for either read or write, Release() it.
Definition: bigRWMutex.h:90
void Acquire(TfBigRWMutex &m, bool write=true)
If the current scoped lock is acquired, Release() it, then associate this lock with m and acquire eit...
Definition: bigRWMutex.h:97
void AcquireWrite()
Acquire a write lock on this lock's associated mutex.
Definition: bigRWMutex.h:141
void AcquireRead()
Acquire a read lock on this lock's associated mutex.
Definition: bigRWMutex.h:134