Home | History | Annotate | Download | only in core
      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.
     80         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.
     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.
     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 
    106         void swap(ThreadIDSet& other) {
    107             fThreadIDs.swap(other.fThreadIDs);
    108         }
    109 
    110         int count() const {
    111             return fThreadIDs.count();
    112         }
    113 
    114     private:
    115         SkTDArray<SkThreadID> fThreadIDs;
    116     };
    117 
    118     SkSharedMutex::SkSharedMutex()
    119         : fCurrentShared(new ThreadIDSet)
    120         , fWaitingExclusive(new ThreadIDSet)
    121         , fWaitingShared(new ThreadIDSet){
    122         ANNOTATE_RWLOCK_CREATE(this);
    123     }
    124 
    125     SkSharedMutex::~SkSharedMutex() {  ANNOTATE_RWLOCK_DESTROY(this); }
    126 
    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.
    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 
    183     void SkSharedMutex::assertHeld() const {
    184         SkThreadID threadID(SkGetThreadID());
    185         SkAutoMutexAcquire l(&fMu);
    186         SkASSERT(0 == fCurrentShared->count());
    187         SkASSERT(fWaitingExclusive->find(threadID));
    188     }
    189 
    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 
    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 
    236     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 
    264     SkSharedMutex::SkSharedMutex() : fQueueCounts(0) { ANNOTATE_RWLOCK_CREATE(this); }
    265     SkSharedMutex::~SkSharedMutex() {  ANNOTATE_RWLOCK_DESTROY(this); }
    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 
    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 
    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 
    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