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 "SkAtomics.h"
12#include "SkSemaphore.h"
13#include "SkTypes.h"
14
15#ifdef SK_DEBUG
16    #include "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.
28class SkSharedMutex {
29public:
30    SkSharedMutex();
31    ~SkSharedMutex();
32    // Acquire lock for exclusive use.
33    void acquire();
34
35    // Release lock for exclusive use.
36    void release();
37
38    // Fail if exclusive is not held.
39    void assertHeld() const;
40
41    // Acquire lock for shared use.
42    void acquireShared();
43
44    // Release lock for shared use.
45    void releaseShared();
46
47    // Fail if shared lock not held.
48    void assertHeldShared() const;
49
50private:
51#ifdef SK_DEBUG
52    class ThreadIDSet;
53    std::unique_ptr<ThreadIDSet> fCurrentShared;
54    std::unique_ptr<ThreadIDSet> fWaitingExclusive;
55    std::unique_ptr<ThreadIDSet> fWaitingShared;
56    int fSharedQueueSelect{0};
57    mutable SkMutex fMu;
58    SkSemaphore fSharedQueue[2];
59    SkSemaphore fExclusiveQueue;
60#else
61    SkAtomic<int32_t> fQueueCounts;
62    SkSemaphore       fSharedQueue;
63    SkSemaphore       fExclusiveQueue;
64#endif  // SK_DEBUG
65};
66
67#ifndef SK_DEBUG
68inline void SkSharedMutex::assertHeld() const {};
69inline void SkSharedMutex::assertHeldShared() const {};
70#endif  // SK_DEBUG
71
72class SkAutoSharedMutexShared {
73public:
74    SkAutoSharedMutexShared(SkSharedMutex& lock) : fLock(lock) { lock.acquireShared(); }
75    ~SkAutoSharedMutexShared() { fLock.releaseShared(); }
76private:
77    SkSharedMutex& fLock;
78};
79
80#define SkAutoSharedMutexShared(...) SK_REQUIRE_LOCAL_VAR(SkAutoSharedMutexShared)
81
82#endif // SkSharedLock_DEFINED
83