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