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