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