xref: /aosp_15_r20/external/skia/src/base/SkSharedMutex.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2015 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkSharedMutex.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkSemaphore.h"
12*c8dee2aaSAndroid Build Coastguard Worker 
13*c8dee2aaSAndroid Build Coastguard Worker #include <cinttypes>
14*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
15*c8dee2aaSAndroid Build Coastguard Worker 
16*c8dee2aaSAndroid Build Coastguard Worker #if !defined(__has_feature)
17*c8dee2aaSAndroid Build Coastguard Worker     #define __has_feature(x) 0
18*c8dee2aaSAndroid Build Coastguard Worker #endif
19*c8dee2aaSAndroid Build Coastguard Worker 
20*c8dee2aaSAndroid Build Coastguard Worker #if __has_feature(thread_sanitizer)
21*c8dee2aaSAndroid Build Coastguard Worker 
22*c8dee2aaSAndroid Build Coastguard Worker     /* Report that a lock has been created at address "lock". */
23*c8dee2aaSAndroid Build Coastguard Worker     #define ANNOTATE_RWLOCK_CREATE(lock) \
24*c8dee2aaSAndroid Build Coastguard Worker         AnnotateRWLockCreate(__FILE__, __LINE__, lock)
25*c8dee2aaSAndroid Build Coastguard Worker 
26*c8dee2aaSAndroid Build Coastguard Worker     /* Report that the lock at address "lock" is about to be destroyed. */
27*c8dee2aaSAndroid Build Coastguard Worker     #define ANNOTATE_RWLOCK_DESTROY(lock) \
28*c8dee2aaSAndroid Build Coastguard Worker         AnnotateRWLockDestroy(__FILE__, __LINE__, lock)
29*c8dee2aaSAndroid Build Coastguard Worker 
30*c8dee2aaSAndroid Build Coastguard Worker     /* Report that the lock at address "lock" has been acquired.
31*c8dee2aaSAndroid Build Coastguard Worker        is_w=1 for writer lock, is_w=0 for reader lock. */
32*c8dee2aaSAndroid Build Coastguard Worker     #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \
33*c8dee2aaSAndroid Build Coastguard Worker         AnnotateRWLockAcquired(__FILE__, __LINE__, lock, is_w)
34*c8dee2aaSAndroid Build Coastguard Worker 
35*c8dee2aaSAndroid Build Coastguard Worker     /* Report that the lock at address "lock" is about to be released. */
36*c8dee2aaSAndroid Build Coastguard Worker     #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \
37*c8dee2aaSAndroid Build Coastguard Worker       AnnotateRWLockReleased(__FILE__, __LINE__, lock, is_w)
38*c8dee2aaSAndroid Build Coastguard Worker 
39*c8dee2aaSAndroid Build Coastguard Worker     #if defined(DYNAMIC_ANNOTATIONS_WANT_ATTRIBUTE_WEAK)
40*c8dee2aaSAndroid Build Coastguard Worker         #if defined(__GNUC__)
41*c8dee2aaSAndroid Build Coastguard Worker             #define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK __attribute__((weak))
42*c8dee2aaSAndroid Build Coastguard Worker         #else
43*c8dee2aaSAndroid Build Coastguard Worker             /* TODO(glider): for Windows support we may want to change this macro in order
44*c8dee2aaSAndroid Build Coastguard Worker                to prepend __declspec(selectany) to the annotations' declarations. */
45*c8dee2aaSAndroid Build Coastguard Worker             #error weak annotations are not supported for your compiler
46*c8dee2aaSAndroid Build Coastguard Worker         #endif
47*c8dee2aaSAndroid Build Coastguard Worker     #else
48*c8dee2aaSAndroid Build Coastguard Worker         #define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK
49*c8dee2aaSAndroid Build Coastguard Worker     #endif
50*c8dee2aaSAndroid Build Coastguard Worker 
51*c8dee2aaSAndroid Build Coastguard Worker     extern "C" {
52*c8dee2aaSAndroid Build Coastguard Worker     void AnnotateRWLockCreate(
53*c8dee2aaSAndroid Build Coastguard Worker         const char *file, int line,
54*c8dee2aaSAndroid Build Coastguard Worker         const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
55*c8dee2aaSAndroid Build Coastguard Worker     void AnnotateRWLockDestroy(
56*c8dee2aaSAndroid Build Coastguard Worker         const char *file, int line,
57*c8dee2aaSAndroid Build Coastguard Worker         const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
58*c8dee2aaSAndroid Build Coastguard Worker     void AnnotateRWLockAcquired(
59*c8dee2aaSAndroid Build Coastguard Worker         const char *file, int line,
60*c8dee2aaSAndroid Build Coastguard Worker         const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
61*c8dee2aaSAndroid Build Coastguard Worker     void AnnotateRWLockReleased(
62*c8dee2aaSAndroid Build Coastguard Worker         const char *file, int line,
63*c8dee2aaSAndroid Build Coastguard Worker         const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK;
64*c8dee2aaSAndroid Build Coastguard Worker     }
65*c8dee2aaSAndroid Build Coastguard Worker 
66*c8dee2aaSAndroid Build Coastguard Worker #else
67*c8dee2aaSAndroid Build Coastguard Worker 
68*c8dee2aaSAndroid Build Coastguard Worker     #define ANNOTATE_RWLOCK_CREATE(lock)
69*c8dee2aaSAndroid Build Coastguard Worker     #define ANNOTATE_RWLOCK_DESTROY(lock)
70*c8dee2aaSAndroid Build Coastguard Worker     #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w)
71*c8dee2aaSAndroid Build Coastguard Worker     #define ANNOTATE_RWLOCK_RELEASED(lock, is_w)
72*c8dee2aaSAndroid Build Coastguard Worker 
73*c8dee2aaSAndroid Build Coastguard Worker #endif
74*c8dee2aaSAndroid Build Coastguard Worker 
75*c8dee2aaSAndroid Build Coastguard Worker #ifdef SK_DEBUG
76*c8dee2aaSAndroid Build Coastguard Worker 
77*c8dee2aaSAndroid Build Coastguard Worker     #include "include/private/base/SkTDArray.h"
78*c8dee2aaSAndroid Build Coastguard Worker     #include "include/private/base/SkThreadID.h"
79*c8dee2aaSAndroid Build Coastguard Worker 
80*c8dee2aaSAndroid Build Coastguard Worker     class SkSharedMutex::ThreadIDSet {
81*c8dee2aaSAndroid Build Coastguard Worker     public:
82*c8dee2aaSAndroid Build Coastguard Worker         // Returns true if threadID is in the set.
find(SkThreadID threadID) const83*c8dee2aaSAndroid Build Coastguard Worker         bool find(SkThreadID threadID) const {
84*c8dee2aaSAndroid Build Coastguard Worker             for (auto& t : fThreadIDs) {
85*c8dee2aaSAndroid Build Coastguard Worker                 if (t == threadID) return true;
86*c8dee2aaSAndroid Build Coastguard Worker             }
87*c8dee2aaSAndroid Build Coastguard Worker             return false;
88*c8dee2aaSAndroid Build Coastguard Worker         }
89*c8dee2aaSAndroid Build Coastguard Worker 
90*c8dee2aaSAndroid Build Coastguard Worker         // Returns true if did not already exist.
tryAdd(SkThreadID threadID)91*c8dee2aaSAndroid Build Coastguard Worker         bool tryAdd(SkThreadID threadID) {
92*c8dee2aaSAndroid Build Coastguard Worker             for (auto& t : fThreadIDs) {
93*c8dee2aaSAndroid Build Coastguard Worker                 if (t == threadID) return false;
94*c8dee2aaSAndroid Build Coastguard Worker             }
95*c8dee2aaSAndroid Build Coastguard Worker             fThreadIDs.append(1, &threadID);
96*c8dee2aaSAndroid Build Coastguard Worker             return true;
97*c8dee2aaSAndroid Build Coastguard Worker         }
98*c8dee2aaSAndroid Build Coastguard Worker         // Returns true if already exists in Set.
tryRemove(SkThreadID threadID)99*c8dee2aaSAndroid Build Coastguard Worker         bool tryRemove(SkThreadID threadID) {
100*c8dee2aaSAndroid Build Coastguard Worker             for (int i = 0; i < fThreadIDs.size(); ++i) {
101*c8dee2aaSAndroid Build Coastguard Worker                 if (fThreadIDs[i] == threadID) {
102*c8dee2aaSAndroid Build Coastguard Worker                     fThreadIDs.remove(i);
103*c8dee2aaSAndroid Build Coastguard Worker                     return true;
104*c8dee2aaSAndroid Build Coastguard Worker                 }
105*c8dee2aaSAndroid Build Coastguard Worker             }
106*c8dee2aaSAndroid Build Coastguard Worker             return false;
107*c8dee2aaSAndroid Build Coastguard Worker         }
108*c8dee2aaSAndroid Build Coastguard Worker 
swap(ThreadIDSet & other)109*c8dee2aaSAndroid Build Coastguard Worker         void swap(ThreadIDSet& other) {
110*c8dee2aaSAndroid Build Coastguard Worker             fThreadIDs.swap(other.fThreadIDs);
111*c8dee2aaSAndroid Build Coastguard Worker         }
112*c8dee2aaSAndroid Build Coastguard Worker 
count() const113*c8dee2aaSAndroid Build Coastguard Worker         int count() const {
114*c8dee2aaSAndroid Build Coastguard Worker             return fThreadIDs.size();
115*c8dee2aaSAndroid Build Coastguard Worker         }
116*c8dee2aaSAndroid Build Coastguard Worker 
117*c8dee2aaSAndroid Build Coastguard Worker     private:
118*c8dee2aaSAndroid Build Coastguard Worker         SkTDArray<SkThreadID> fThreadIDs;
119*c8dee2aaSAndroid Build Coastguard Worker     };
120*c8dee2aaSAndroid Build Coastguard Worker 
SkSharedMutex()121*c8dee2aaSAndroid Build Coastguard Worker     SkSharedMutex::SkSharedMutex()
122*c8dee2aaSAndroid Build Coastguard Worker         : fCurrentShared(new ThreadIDSet)
123*c8dee2aaSAndroid Build Coastguard Worker         , fWaitingExclusive(new ThreadIDSet)
124*c8dee2aaSAndroid Build Coastguard Worker         , fWaitingShared(new ThreadIDSet){
125*c8dee2aaSAndroid Build Coastguard Worker         ANNOTATE_RWLOCK_CREATE(this);
126*c8dee2aaSAndroid Build Coastguard Worker     }
127*c8dee2aaSAndroid Build Coastguard Worker 
~SkSharedMutex()128*c8dee2aaSAndroid Build Coastguard Worker     SkSharedMutex::~SkSharedMutex() {  ANNOTATE_RWLOCK_DESTROY(this); }
129*c8dee2aaSAndroid Build Coastguard Worker 
acquire()130*c8dee2aaSAndroid Build Coastguard Worker     void SkSharedMutex::acquire() {
131*c8dee2aaSAndroid Build Coastguard Worker         SkThreadID threadID(SkGetThreadID());
132*c8dee2aaSAndroid Build Coastguard Worker         int currentSharedCount;
133*c8dee2aaSAndroid Build Coastguard Worker         int waitingExclusiveCount;
134*c8dee2aaSAndroid Build Coastguard Worker         {
135*c8dee2aaSAndroid Build Coastguard Worker             SkAutoMutexExclusive l(fMu);
136*c8dee2aaSAndroid Build Coastguard Worker 
137*c8dee2aaSAndroid Build Coastguard Worker             SkASSERTF(!fCurrentShared->find(threadID),
138*c8dee2aaSAndroid Build Coastguard Worker                       "Thread %" PRIx64 " already has an shared lock\n", (uint64_t)threadID);
139*c8dee2aaSAndroid Build Coastguard Worker 
140*c8dee2aaSAndroid Build Coastguard Worker             if (!fWaitingExclusive->tryAdd(threadID)) {
141*c8dee2aaSAndroid Build Coastguard Worker                 SkDEBUGFAILF("Thread %" PRIx64 " already has an exclusive lock\n",
142*c8dee2aaSAndroid Build Coastguard Worker                              (uint64_t)threadID);
143*c8dee2aaSAndroid Build Coastguard Worker             }
144*c8dee2aaSAndroid Build Coastguard Worker 
145*c8dee2aaSAndroid Build Coastguard Worker             currentSharedCount = fCurrentShared->count();
146*c8dee2aaSAndroid Build Coastguard Worker             waitingExclusiveCount = fWaitingExclusive->count();
147*c8dee2aaSAndroid Build Coastguard Worker         }
148*c8dee2aaSAndroid Build Coastguard Worker 
149*c8dee2aaSAndroid Build Coastguard Worker         if (currentSharedCount > 0 || waitingExclusiveCount > 1) {
150*c8dee2aaSAndroid Build Coastguard Worker             fExclusiveQueue.wait();
151*c8dee2aaSAndroid Build Coastguard Worker         }
152*c8dee2aaSAndroid Build Coastguard Worker 
153*c8dee2aaSAndroid Build Coastguard Worker         ANNOTATE_RWLOCK_ACQUIRED(this, 1);
154*c8dee2aaSAndroid Build Coastguard Worker     }
155*c8dee2aaSAndroid Build Coastguard Worker 
156*c8dee2aaSAndroid Build Coastguard Worker     // Implementation Detail:
157*c8dee2aaSAndroid Build Coastguard Worker     // The shared threads need two separate queues to keep the threads that were added after the
158*c8dee2aaSAndroid Build Coastguard Worker     // exclusive lock separate from the threads added before.
release()159*c8dee2aaSAndroid Build Coastguard Worker     void SkSharedMutex::release() {
160*c8dee2aaSAndroid Build Coastguard Worker         ANNOTATE_RWLOCK_RELEASED(this, 1);
161*c8dee2aaSAndroid Build Coastguard Worker         SkThreadID threadID(SkGetThreadID());
162*c8dee2aaSAndroid Build Coastguard Worker         int sharedWaitingCount;
163*c8dee2aaSAndroid Build Coastguard Worker         int exclusiveWaitingCount;
164*c8dee2aaSAndroid Build Coastguard Worker         int sharedQueueSelect;
165*c8dee2aaSAndroid Build Coastguard Worker         {
166*c8dee2aaSAndroid Build Coastguard Worker             SkAutoMutexExclusive l(fMu);
167*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(0 == fCurrentShared->count());
168*c8dee2aaSAndroid Build Coastguard Worker             if (!fWaitingExclusive->tryRemove(threadID)) {
169*c8dee2aaSAndroid Build Coastguard Worker                 SkDEBUGFAILF("Thread %" PRIx64 " did not have the lock held.\n",
170*c8dee2aaSAndroid Build Coastguard Worker                              (uint64_t)threadID);
171*c8dee2aaSAndroid Build Coastguard Worker             }
172*c8dee2aaSAndroid Build Coastguard Worker             exclusiveWaitingCount = fWaitingExclusive->count();
173*c8dee2aaSAndroid Build Coastguard Worker             sharedWaitingCount = fWaitingShared->count();
174*c8dee2aaSAndroid Build Coastguard Worker             fWaitingShared.swap(fCurrentShared);
175*c8dee2aaSAndroid Build Coastguard Worker             sharedQueueSelect = fSharedQueueSelect;
176*c8dee2aaSAndroid Build Coastguard Worker             if (sharedWaitingCount > 0) {
177*c8dee2aaSAndroid Build Coastguard Worker                 fSharedQueueSelect = 1 - fSharedQueueSelect;
178*c8dee2aaSAndroid Build Coastguard Worker             }
179*c8dee2aaSAndroid Build Coastguard Worker         }
180*c8dee2aaSAndroid Build Coastguard Worker 
181*c8dee2aaSAndroid Build Coastguard Worker         if (sharedWaitingCount > 0) {
182*c8dee2aaSAndroid Build Coastguard Worker             fSharedQueue[sharedQueueSelect].signal(sharedWaitingCount);
183*c8dee2aaSAndroid Build Coastguard Worker         } else if (exclusiveWaitingCount > 0) {
184*c8dee2aaSAndroid Build Coastguard Worker             fExclusiveQueue.signal();
185*c8dee2aaSAndroid Build Coastguard Worker         }
186*c8dee2aaSAndroid Build Coastguard Worker     }
187*c8dee2aaSAndroid Build Coastguard Worker 
assertHeld() const188*c8dee2aaSAndroid Build Coastguard Worker     void SkSharedMutex::assertHeld() const {
189*c8dee2aaSAndroid Build Coastguard Worker         SkThreadID threadID(SkGetThreadID());
190*c8dee2aaSAndroid Build Coastguard Worker         SkAutoMutexExclusive l(fMu);
191*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(0 == fCurrentShared->count());
192*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fWaitingExclusive->find(threadID));
193*c8dee2aaSAndroid Build Coastguard Worker     }
194*c8dee2aaSAndroid Build Coastguard Worker 
acquireShared()195*c8dee2aaSAndroid Build Coastguard Worker     void SkSharedMutex::acquireShared() {
196*c8dee2aaSAndroid Build Coastguard Worker         SkThreadID threadID(SkGetThreadID());
197*c8dee2aaSAndroid Build Coastguard Worker         int exclusiveWaitingCount;
198*c8dee2aaSAndroid Build Coastguard Worker         int sharedQueueSelect;
199*c8dee2aaSAndroid Build Coastguard Worker         {
200*c8dee2aaSAndroid Build Coastguard Worker             SkAutoMutexExclusive l(fMu);
201*c8dee2aaSAndroid Build Coastguard Worker             exclusiveWaitingCount = fWaitingExclusive->count();
202*c8dee2aaSAndroid Build Coastguard Worker             if (exclusiveWaitingCount > 0) {
203*c8dee2aaSAndroid Build Coastguard Worker                 if (!fWaitingShared->tryAdd(threadID)) {
204*c8dee2aaSAndroid Build Coastguard Worker                     SkDEBUGFAILF("Thread %" PRIx64 " was already waiting!\n", (uint64_t)threadID);
205*c8dee2aaSAndroid Build Coastguard Worker                 }
206*c8dee2aaSAndroid Build Coastguard Worker             } else {
207*c8dee2aaSAndroid Build Coastguard Worker                 if (!fCurrentShared->tryAdd(threadID)) {
208*c8dee2aaSAndroid Build Coastguard Worker                     SkDEBUGFAILF("Thread %" PRIx64 " already holds a shared lock!\n",
209*c8dee2aaSAndroid Build Coastguard Worker                                  (uint64_t)threadID);
210*c8dee2aaSAndroid Build Coastguard Worker                 }
211*c8dee2aaSAndroid Build Coastguard Worker             }
212*c8dee2aaSAndroid Build Coastguard Worker             sharedQueueSelect = fSharedQueueSelect;
213*c8dee2aaSAndroid Build Coastguard Worker         }
214*c8dee2aaSAndroid Build Coastguard Worker 
215*c8dee2aaSAndroid Build Coastguard Worker         if (exclusiveWaitingCount > 0) {
216*c8dee2aaSAndroid Build Coastguard Worker             fSharedQueue[sharedQueueSelect].wait();
217*c8dee2aaSAndroid Build Coastguard Worker         }
218*c8dee2aaSAndroid Build Coastguard Worker 
219*c8dee2aaSAndroid Build Coastguard Worker         ANNOTATE_RWLOCK_ACQUIRED(this, 0);
220*c8dee2aaSAndroid Build Coastguard Worker     }
221*c8dee2aaSAndroid Build Coastguard Worker 
releaseShared()222*c8dee2aaSAndroid Build Coastguard Worker     void SkSharedMutex::releaseShared() {
223*c8dee2aaSAndroid Build Coastguard Worker         ANNOTATE_RWLOCK_RELEASED(this, 0);
224*c8dee2aaSAndroid Build Coastguard Worker         SkThreadID threadID(SkGetThreadID());
225*c8dee2aaSAndroid Build Coastguard Worker 
226*c8dee2aaSAndroid Build Coastguard Worker         int currentSharedCount;
227*c8dee2aaSAndroid Build Coastguard Worker         int waitingExclusiveCount;
228*c8dee2aaSAndroid Build Coastguard Worker         {
229*c8dee2aaSAndroid Build Coastguard Worker             SkAutoMutexExclusive l(fMu);
230*c8dee2aaSAndroid Build Coastguard Worker             if (!fCurrentShared->tryRemove(threadID)) {
231*c8dee2aaSAndroid Build Coastguard Worker                 SkDEBUGFAILF("Thread %" PRIx64 " does not hold a shared lock.\n",
232*c8dee2aaSAndroid Build Coastguard Worker                              (uint64_t)threadID);
233*c8dee2aaSAndroid Build Coastguard Worker             }
234*c8dee2aaSAndroid Build Coastguard Worker             currentSharedCount = fCurrentShared->count();
235*c8dee2aaSAndroid Build Coastguard Worker             waitingExclusiveCount = fWaitingExclusive->count();
236*c8dee2aaSAndroid Build Coastguard Worker         }
237*c8dee2aaSAndroid Build Coastguard Worker 
238*c8dee2aaSAndroid Build Coastguard Worker         if (0 == currentSharedCount && waitingExclusiveCount > 0) {
239*c8dee2aaSAndroid Build Coastguard Worker             fExclusiveQueue.signal();
240*c8dee2aaSAndroid Build Coastguard Worker         }
241*c8dee2aaSAndroid Build Coastguard Worker     }
242*c8dee2aaSAndroid Build Coastguard Worker 
assertHeldShared() const243*c8dee2aaSAndroid Build Coastguard Worker     void SkSharedMutex::assertHeldShared() const {
244*c8dee2aaSAndroid Build Coastguard Worker         SkThreadID threadID(SkGetThreadID());
245*c8dee2aaSAndroid Build Coastguard Worker         SkAutoMutexExclusive l(fMu);
246*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fCurrentShared->find(threadID));
247*c8dee2aaSAndroid Build Coastguard Worker     }
248*c8dee2aaSAndroid Build Coastguard Worker 
249*c8dee2aaSAndroid Build Coastguard Worker #else
250*c8dee2aaSAndroid Build Coastguard Worker 
251*c8dee2aaSAndroid Build Coastguard Worker     // The fQueueCounts fields holds many counts in an int32_t in order to make managing them atomic.
252*c8dee2aaSAndroid Build Coastguard Worker     // These three counts must be the same size, so each gets 10 bits. The 10 bits represent
253*c8dee2aaSAndroid Build Coastguard Worker     // the log of the count which is 1024.
254*c8dee2aaSAndroid Build Coastguard Worker     //
255*c8dee2aaSAndroid Build Coastguard Worker     // The three counts held in fQueueCounts are:
256*c8dee2aaSAndroid Build Coastguard Worker     // * Shared - the number of shared lock holders currently running.
257*c8dee2aaSAndroid Build Coastguard Worker     // * WaitingExclusive - the number of threads waiting for an exclusive lock.
258*c8dee2aaSAndroid Build Coastguard Worker     // * WaitingShared - the number of threads waiting to run while waiting for an exclusive thread
259*c8dee2aaSAndroid Build Coastguard Worker     //   to finish.
260*c8dee2aaSAndroid Build Coastguard Worker     static const int kLogThreadCount = 10;
261*c8dee2aaSAndroid Build Coastguard Worker 
262*c8dee2aaSAndroid Build Coastguard Worker     enum {
263*c8dee2aaSAndroid Build Coastguard Worker         kSharedOffset          = (0 * kLogThreadCount),
264*c8dee2aaSAndroid Build Coastguard Worker         kWaitingExlusiveOffset = (1 * kLogThreadCount),
265*c8dee2aaSAndroid Build Coastguard Worker         kWaitingSharedOffset   = (2 * kLogThreadCount),
266*c8dee2aaSAndroid Build Coastguard Worker         kSharedMask            = ((1 << kLogThreadCount) - 1) << kSharedOffset,
267*c8dee2aaSAndroid Build Coastguard Worker         kWaitingExclusiveMask  = ((1 << kLogThreadCount) - 1) << kWaitingExlusiveOffset,
268*c8dee2aaSAndroid Build Coastguard Worker         kWaitingSharedMask     = ((1 << kLogThreadCount) - 1) << kWaitingSharedOffset,
269*c8dee2aaSAndroid Build Coastguard Worker     };
270*c8dee2aaSAndroid Build Coastguard Worker 
SkSharedMutex()271*c8dee2aaSAndroid Build Coastguard Worker     SkSharedMutex::SkSharedMutex() : fQueueCounts(0) { ANNOTATE_RWLOCK_CREATE(this); }
~SkSharedMutex()272*c8dee2aaSAndroid Build Coastguard Worker     SkSharedMutex::~SkSharedMutex() {  ANNOTATE_RWLOCK_DESTROY(this); }
acquire()273*c8dee2aaSAndroid Build Coastguard Worker     void SkSharedMutex::acquire() {
274*c8dee2aaSAndroid Build Coastguard Worker         // Increment the count of exclusive queue waiters.
275*c8dee2aaSAndroid Build Coastguard Worker         int32_t oldQueueCounts = fQueueCounts.fetch_add(1 << kWaitingExlusiveOffset,
276*c8dee2aaSAndroid Build Coastguard Worker                                                         std::memory_order_acquire);
277*c8dee2aaSAndroid Build Coastguard Worker 
278*c8dee2aaSAndroid Build Coastguard Worker         // If there are no other exclusive waiters and no shared threads are running then run
279*c8dee2aaSAndroid Build Coastguard Worker         // else wait.
280*c8dee2aaSAndroid Build Coastguard Worker         if ((oldQueueCounts & kWaitingExclusiveMask) > 0 || (oldQueueCounts & kSharedMask) > 0) {
281*c8dee2aaSAndroid Build Coastguard Worker             fExclusiveQueue.wait();
282*c8dee2aaSAndroid Build Coastguard Worker         }
283*c8dee2aaSAndroid Build Coastguard Worker         ANNOTATE_RWLOCK_ACQUIRED(this, 1);
284*c8dee2aaSAndroid Build Coastguard Worker     }
285*c8dee2aaSAndroid Build Coastguard Worker 
release()286*c8dee2aaSAndroid Build Coastguard Worker     void SkSharedMutex::release() {
287*c8dee2aaSAndroid Build Coastguard Worker         ANNOTATE_RWLOCK_RELEASED(this, 1);
288*c8dee2aaSAndroid Build Coastguard Worker 
289*c8dee2aaSAndroid Build Coastguard Worker         int32_t oldQueueCounts = fQueueCounts.load(std::memory_order_relaxed);
290*c8dee2aaSAndroid Build Coastguard Worker         int32_t waitingShared;
291*c8dee2aaSAndroid Build Coastguard Worker         int32_t newQueueCounts;
292*c8dee2aaSAndroid Build Coastguard Worker         do {
293*c8dee2aaSAndroid Build Coastguard Worker             newQueueCounts = oldQueueCounts;
294*c8dee2aaSAndroid Build Coastguard Worker 
295*c8dee2aaSAndroid Build Coastguard Worker             // Decrement exclusive waiters.
296*c8dee2aaSAndroid Build Coastguard Worker             newQueueCounts -= 1 << kWaitingExlusiveOffset;
297*c8dee2aaSAndroid Build Coastguard Worker 
298*c8dee2aaSAndroid Build Coastguard Worker             // The number of threads waiting to acquire a shared lock.
299*c8dee2aaSAndroid Build Coastguard Worker             waitingShared = (oldQueueCounts & kWaitingSharedMask) >> kWaitingSharedOffset;
300*c8dee2aaSAndroid Build Coastguard Worker 
301*c8dee2aaSAndroid Build Coastguard Worker             // If there are any move the counts of all the shared waiters to actual shared. They are
302*c8dee2aaSAndroid Build Coastguard Worker             // going to run next.
303*c8dee2aaSAndroid Build Coastguard Worker             if (waitingShared > 0) {
304*c8dee2aaSAndroid Build Coastguard Worker 
305*c8dee2aaSAndroid Build Coastguard Worker                 // Set waiting shared to zero.
306*c8dee2aaSAndroid Build Coastguard Worker                 newQueueCounts &= ~kWaitingSharedMask;
307*c8dee2aaSAndroid Build Coastguard Worker 
308*c8dee2aaSAndroid Build Coastguard Worker                 // Because this is the exclusive release, then there are zero readers. So, the bits
309*c8dee2aaSAndroid Build Coastguard Worker                 // for shared locks should be zero. Since those bits are zero, we can just |= in the
310*c8dee2aaSAndroid Build Coastguard Worker                 // waitingShared count instead of clearing with an &= and then |= the count.
311*c8dee2aaSAndroid Build Coastguard Worker                 newQueueCounts |= waitingShared << kSharedOffset;
312*c8dee2aaSAndroid Build Coastguard Worker             }
313*c8dee2aaSAndroid Build Coastguard Worker 
314*c8dee2aaSAndroid Build Coastguard Worker         } while (!fQueueCounts.compare_exchange_strong(oldQueueCounts, newQueueCounts,
315*c8dee2aaSAndroid Build Coastguard Worker                                                        std::memory_order_release,
316*c8dee2aaSAndroid Build Coastguard Worker                                                        std::memory_order_relaxed));
317*c8dee2aaSAndroid Build Coastguard Worker 
318*c8dee2aaSAndroid Build Coastguard Worker         if (waitingShared > 0) {
319*c8dee2aaSAndroid Build Coastguard Worker             // Run all the shared.
320*c8dee2aaSAndroid Build Coastguard Worker             fSharedQueue.signal(waitingShared);
321*c8dee2aaSAndroid Build Coastguard Worker         } else if ((newQueueCounts & kWaitingExclusiveMask) > 0) {
322*c8dee2aaSAndroid Build Coastguard Worker             // Run a single exclusive waiter.
323*c8dee2aaSAndroid Build Coastguard Worker             fExclusiveQueue.signal();
324*c8dee2aaSAndroid Build Coastguard Worker         }
325*c8dee2aaSAndroid Build Coastguard Worker     }
326*c8dee2aaSAndroid Build Coastguard Worker 
acquireShared()327*c8dee2aaSAndroid Build Coastguard Worker     void SkSharedMutex::acquireShared() {
328*c8dee2aaSAndroid Build Coastguard Worker         int32_t oldQueueCounts = fQueueCounts.load(std::memory_order_relaxed);
329*c8dee2aaSAndroid Build Coastguard Worker         int32_t newQueueCounts;
330*c8dee2aaSAndroid Build Coastguard Worker         do {
331*c8dee2aaSAndroid Build Coastguard Worker             newQueueCounts = oldQueueCounts;
332*c8dee2aaSAndroid Build Coastguard Worker             // If there are waiting exclusives then this shared lock waits else it runs.
333*c8dee2aaSAndroid Build Coastguard Worker             if ((newQueueCounts & kWaitingExclusiveMask) > 0) {
334*c8dee2aaSAndroid Build Coastguard Worker                 newQueueCounts += 1 << kWaitingSharedOffset;
335*c8dee2aaSAndroid Build Coastguard Worker             } else {
336*c8dee2aaSAndroid Build Coastguard Worker                 newQueueCounts += 1 << kSharedOffset;
337*c8dee2aaSAndroid Build Coastguard Worker             }
338*c8dee2aaSAndroid Build Coastguard Worker         } while (!fQueueCounts.compare_exchange_strong(oldQueueCounts, newQueueCounts,
339*c8dee2aaSAndroid Build Coastguard Worker                                                        std::memory_order_acquire,
340*c8dee2aaSAndroid Build Coastguard Worker                                                        std::memory_order_relaxed));
341*c8dee2aaSAndroid Build Coastguard Worker 
342*c8dee2aaSAndroid Build Coastguard Worker         // If there are waiting exclusives, then this shared waits until after it runs.
343*c8dee2aaSAndroid Build Coastguard Worker         if ((newQueueCounts & kWaitingExclusiveMask) > 0) {
344*c8dee2aaSAndroid Build Coastguard Worker             fSharedQueue.wait();
345*c8dee2aaSAndroid Build Coastguard Worker         }
346*c8dee2aaSAndroid Build Coastguard Worker         ANNOTATE_RWLOCK_ACQUIRED(this, 0);
347*c8dee2aaSAndroid Build Coastguard Worker 
348*c8dee2aaSAndroid Build Coastguard Worker     }
349*c8dee2aaSAndroid Build Coastguard Worker 
releaseShared()350*c8dee2aaSAndroid Build Coastguard Worker     void SkSharedMutex::releaseShared() {
351*c8dee2aaSAndroid Build Coastguard Worker         ANNOTATE_RWLOCK_RELEASED(this, 0);
352*c8dee2aaSAndroid Build Coastguard Worker 
353*c8dee2aaSAndroid Build Coastguard Worker         // Decrement the shared count.
354*c8dee2aaSAndroid Build Coastguard Worker         int32_t oldQueueCounts = fQueueCounts.fetch_sub(1 << kSharedOffset,
355*c8dee2aaSAndroid Build Coastguard Worker                                                         std::memory_order_release);
356*c8dee2aaSAndroid Build Coastguard Worker 
357*c8dee2aaSAndroid Build Coastguard Worker         // If shared count is going to zero (because the old count == 1) and there are exclusive
358*c8dee2aaSAndroid Build Coastguard Worker         // waiters, then run a single exclusive waiter.
359*c8dee2aaSAndroid Build Coastguard Worker         if (((oldQueueCounts & kSharedMask) >> kSharedOffset) == 1
360*c8dee2aaSAndroid Build Coastguard Worker             && (oldQueueCounts & kWaitingExclusiveMask) > 0) {
361*c8dee2aaSAndroid Build Coastguard Worker             fExclusiveQueue.signal();
362*c8dee2aaSAndroid Build Coastguard Worker         }
363*c8dee2aaSAndroid Build Coastguard Worker     }
364*c8dee2aaSAndroid Build Coastguard Worker 
365*c8dee2aaSAndroid Build Coastguard Worker #endif
366