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 #ifndef SkSharedLock_DEFINED 9 #define SkSharedLock_DEFINED 10 11 #include "include/private/base/SkDebug.h" 12 #include "include/private/base/SkSemaphore.h" 13 #include "include/private/base/SkThreadAnnotations.h" 14 15 #ifdef SK_DEBUG 16 #include "include/private/base/SkMutex.h" 17 #include <memory> 18 #endif // SK_DEBUG 19 20 // There are two shared lock implementations one debug the other is high performance. They implement 21 // an interface similar to pthread's rwlocks. 22 // This is a shared lock implementation similar to pthreads rwlocks. The high performance 23 // implementation is cribbed from Preshing's article: 24 // http://preshing.com/20150316/semaphores-are-surprisingly-versatile/ 25 // 26 // This lock does not obey strict queue ordering. It will always alternate between readers and 27 // a single writer. 28 // 29 // This allows us to have a mutex without needing the one in 30 // the C++ std library which does not work with all clients. 31 // go/cstyle#Disallowed_Stdlib 32 class SK_CAPABILITY("mutex") SkSharedMutex { 33 public: 34 SkSharedMutex(); 35 ~SkSharedMutex(); 36 // Acquire lock for exclusive use. 37 void acquire() SK_ACQUIRE(); 38 39 // Release lock for exclusive use. 40 void release() SK_RELEASE_CAPABILITY(); 41 42 // Fail if exclusive is not held. 43 void assertHeld() const SK_ASSERT_CAPABILITY(this); 44 45 // Acquire lock for shared use. 46 void acquireShared() SK_ACQUIRE_SHARED(); 47 48 // Release lock for shared use. 49 void releaseShared() SK_RELEASE_SHARED_CAPABILITY(); 50 51 // Fail if shared lock not held. 52 void assertHeldShared() const SK_ASSERT_SHARED_CAPABILITY(this); 53 54 private: 55 #ifdef SK_DEBUG 56 class ThreadIDSet; 57 std::unique_ptr<ThreadIDSet> fCurrentShared; 58 std::unique_ptr<ThreadIDSet> fWaitingExclusive; 59 std::unique_ptr<ThreadIDSet> fWaitingShared; 60 int fSharedQueueSelect{0}; 61 mutable SkMutex fMu; 62 SkSemaphore fSharedQueue[2]; 63 SkSemaphore fExclusiveQueue; 64 #else 65 std::atomic<int32_t> fQueueCounts; 66 SkSemaphore fSharedQueue; 67 SkSemaphore fExclusiveQueue; 68 #endif // SK_DEBUG 69 }; 70 71 #ifndef SK_DEBUG assertHeld()72inline void SkSharedMutex::assertHeld() const {} assertHeldShared()73inline void SkSharedMutex::assertHeldShared() const {} 74 #endif // SK_DEBUG 75 76 class SK_SCOPED_CAPABILITY SkAutoSharedMutexExclusive { 77 public: SkAutoSharedMutexExclusive(SkSharedMutex & lock)78 explicit SkAutoSharedMutexExclusive(SkSharedMutex& lock) SK_ACQUIRE(lock) 79 : fLock(lock) { 80 lock.acquire(); 81 } SK_RELEASE_CAPABILITY()82 ~SkAutoSharedMutexExclusive() SK_RELEASE_CAPABILITY() { fLock.release(); } 83 84 private: 85 SkSharedMutex& fLock; 86 }; 87 88 class SK_SCOPED_CAPABILITY SkAutoSharedMutexShared { 89 public: SkAutoSharedMutexShared(SkSharedMutex & lock)90 explicit SkAutoSharedMutexShared(SkSharedMutex& lock) SK_ACQUIRE_SHARED(lock) 91 : fLock(lock) { 92 lock.acquireShared(); 93 } 94 95 // You would think this should be SK_RELEASE_SHARED_CAPABILITY, but SK_SCOPED_CAPABILITY 96 // doesn't fully understand the difference between shared and exclusive. 97 // Please review https://reviews.llvm.org/D52578 for more information. SK_RELEASE_CAPABILITY()98 ~SkAutoSharedMutexShared() SK_RELEASE_CAPABILITY() { fLock.releaseShared(); } 99 100 private: 101 SkSharedMutex& fLock; 102 }; 103 104 #endif // SkSharedLock_DEFINED 105