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 "SkMacros.h"
12 #include "SkSemaphore.h"
13 #include "SkTypes.h"
14 #include <atomic>
15 
16 #ifdef SK_DEBUG
17     #include "SkMutex.h"
18     #include <memory>
19 #endif  // SK_DEBUG
20 
21 // There are two shared lock implementations one debug the other is high performance. They implement
22 // an interface similar to pthread's rwlocks.
23 // This is a shared lock implementation similar to pthreads rwlocks. The high performance
24 // implementation is cribbed from Preshing's article:
25 // http://preshing.com/20150316/semaphores-are-surprisingly-versatile/
26 //
27 // This lock does not obey strict queue ordering. It will always alternate between readers and
28 // a single writer.
29 class SkSharedMutex {
30 public:
31     SkSharedMutex();
32     ~SkSharedMutex();
33     // Acquire lock for exclusive use.
34     void acquire();
35 
36     // Release lock for exclusive use.
37     void release();
38 
39     // Fail if exclusive is not held.
40     void assertHeld() const;
41 
42     // Acquire lock for shared use.
43     void acquireShared();
44 
45     // Release lock for shared use.
46     void releaseShared();
47 
48     // Fail if shared lock not held.
49     void assertHeldShared() const;
50 
51 private:
52 #ifdef SK_DEBUG
53     class ThreadIDSet;
54     std::unique_ptr<ThreadIDSet> fCurrentShared;
55     std::unique_ptr<ThreadIDSet> fWaitingExclusive;
56     std::unique_ptr<ThreadIDSet> fWaitingShared;
57     int fSharedQueueSelect{0};
58     mutable SkMutex fMu;
59     SkSemaphore fSharedQueue[2];
60     SkSemaphore fExclusiveQueue;
61 #else
62     std::atomic<int32_t> fQueueCounts;
63     SkSemaphore          fSharedQueue;
64     SkSemaphore          fExclusiveQueue;
65 #endif  // SK_DEBUG
66 };
67 
68 #ifndef SK_DEBUG
assertHeld()69 inline void SkSharedMutex::assertHeld() const {};
assertHeldShared()70 inline void SkSharedMutex::assertHeldShared() const {};
71 #endif  // SK_DEBUG
72 
73 class SkAutoSharedMutexShared {
74 public:
SkAutoSharedMutexShared(SkSharedMutex & lock)75     SkAutoSharedMutexShared(SkSharedMutex& lock) : fLock(lock) { lock.acquireShared(); }
~SkAutoSharedMutexShared()76     ~SkAutoSharedMutexShared() { fLock.releaseShared(); }
77 private:
78     SkSharedMutex& fLock;
79 };
80 
81 #define SkAutoSharedMutexShared(...) SK_REQUIRE_LOCAL_VAR(SkAutoSharedMutexShared)
82 
83 #endif // SkSharedLock_DEFINED
84