Flutter Impeller
thread.h
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef FLUTTER_IMPELLER_BASE_THREAD_H_
6 #define FLUTTER_IMPELLER_BASE_THREAD_H_
7 
8 #include <chrono>
9 #include <condition_variable>
10 #include <functional>
11 #include <memory>
12 #include <mutex>
13 #include <shared_mutex>
14 #include <thread>
15 
17 
18 namespace impeller {
19 
20 class ConditionVariable;
21 
22 class IPLR_CAPABILITY("mutex") Mutex {
23  public:
24  Mutex() = default;
25 
26  ~Mutex() = default;
27 
28  void Lock() IPLR_ACQUIRE() { mutex_.lock(); }
29 
30  void Unlock() IPLR_RELEASE() { mutex_.unlock(); }
31 
32  private:
33  friend class ConditionVariable;
34 
35  std::mutex mutex_;
36 
37  Mutex(const Mutex&) = delete;
38 
39  Mutex(Mutex&&) = delete;
40 
41  Mutex& operator=(const Mutex&) = delete;
42 
43  Mutex& operator=(Mutex&&) = delete;
44 };
45 
46 class IPLR_CAPABILITY("mutex") RWMutex {
47  public:
48  RWMutex() = default;
49 
50  ~RWMutex() = default;
51 
52  void LockWriter() IPLR_ACQUIRE() { mutex_.lock(); }
53 
54  void UnlockWriter() IPLR_RELEASE() { mutex_.unlock(); }
55 
56  void LockReader() IPLR_ACQUIRE_SHARED() { mutex_.lock_shared(); }
57 
58  void UnlockReader() IPLR_RELEASE_SHARED() { mutex_.unlock_shared(); }
59 
60  private:
61  std::shared_mutex mutex_;
62 
63  RWMutex(const RWMutex&) = delete;
64 
65  RWMutex(RWMutex&&) = delete;
66 
67  RWMutex& operator=(const RWMutex&) = delete;
68 
69  RWMutex& operator=(RWMutex&&) = delete;
70 };
71 
73  public:
74  explicit Lock(Mutex& mutex) IPLR_ACQUIRE(mutex) : mutex_(mutex) {
75  mutex_.Lock();
76  }
77 
78  ~Lock() IPLR_RELEASE() { mutex_.Unlock(); }
79 
80  private:
81  Mutex& mutex_;
82 
83  Lock(const Lock&) = delete;
84 
85  Lock(Lock&&) = delete;
86 
87  Lock& operator=(const Lock&) = delete;
88 
89  Lock& operator=(Lock&&) = delete;
90 };
91 
93  public:
94  explicit ReaderLock(RWMutex& mutex) IPLR_ACQUIRE_SHARED(mutex)
95  : mutex_(mutex) {
96  mutex_.LockReader();
97  }
98 
99  ~ReaderLock() IPLR_RELEASE() { mutex_.UnlockReader(); }
100 
101  private:
102  RWMutex& mutex_;
103 
104  ReaderLock(const ReaderLock&) = delete;
105 
106  ReaderLock(ReaderLock&&) = delete;
107 
108  ReaderLock& operator=(const ReaderLock&) = delete;
109 
110  ReaderLock& operator=(ReaderLock&&) = delete;
111 };
112 
114  public:
115  explicit WriterLock(RWMutex& mutex) IPLR_ACQUIRE(mutex) : mutex_(mutex) {
116  mutex_.LockWriter();
117  }
118 
119  ~WriterLock() IPLR_RELEASE() { mutex_.UnlockWriter(); }
120 
121  private:
122  RWMutex& mutex_;
123 
124  WriterLock(const WriterLock&) = delete;
125 
126  WriterLock(WriterLock&&) = delete;
127 
128  WriterLock& operator=(const WriterLock&) = delete;
129 
130  WriterLock& operator=(WriterLock&&) = delete;
131 };
132 
133 //------------------------------------------------------------------------------
134 /// @brief A condition variable exactly similar to the one in libcxx with
135 /// two major differences:
136 ///
137 /// * On the Wait, WaitFor, and WaitUntil calls, static analysis
138 /// annotation are respected.
139 /// * There is no ability to wait on a condition variable and also
140 /// be susceptible to spurious wakes. This is because the
141 /// predicate is mandatory.
142 ///
144  public:
145  ConditionVariable() = default;
146 
147  ~ConditionVariable() = default;
148 
150 
152 
153  void NotifyOne() { cv_.notify_one(); }
154 
155  void NotifyAll() { cv_.notify_all(); }
156 
157  using Predicate = std::function<bool()>;
158 
159  //----------------------------------------------------------------------------
160  /// @brief Atomically unlocks the mutex and waits on the condition
161  /// variable up to a specified time point. Lock will be reacquired
162  /// when the wait exits. Spurious wakes may happen before the time
163  /// point is reached. In such cases the predicate is invoked and
164  /// it must return `false` for the wait to continue. The predicate
165  /// will be invoked with the mutex locked.
166  ///
167  /// @note Since the predicate is invoked with the mutex locked, if it
168  /// accesses other guarded resources, the predicate itself must be
169  /// decorated with the IPLR_REQUIRES directive. For instance,
170  ///
171  /// ```c++
172  /// [] () IPLR_REQUIRES(mutex) {
173  /// return my_guarded_resource.should_stop_waiting;
174  /// }
175  /// ```
176  ///
177  /// @param mutex The mutex.
178  /// @param[in] time_point The time point to wait to.
179  /// @param[in] should_stop_waiting The predicate invoked on spurious wakes.
180  /// Must return false for the wait to
181  /// continue.
182  ///
183  /// @tparam Clock The clock type.
184  /// @tparam Duration The duration type.
185  ///
186  /// @return The value of the predicate at the end of the wait.
187  ///
188  template <class Clock, class Duration>
189  bool WaitUntil(Mutex& mutex,
190  const std::chrono::time_point<Clock, Duration>& time_point,
191  const Predicate& should_stop_waiting) IPLR_REQUIRES(mutex) {
192  std::unique_lock lock(mutex.mutex_, std::adopt_lock);
193  const auto result = cv_.wait_until(lock, time_point, should_stop_waiting);
194  lock.release();
195  return result;
196  }
197 
198  //----------------------------------------------------------------------------
199  /// @brief Atomically unlocks the mutex and waits on the condition
200  /// variable for a designated duration. Lock will be reacquired
201  /// when the wait exits. Spurious wakes may happen before the time
202  /// point is reached. In such cases the predicate is invoked and
203  /// it must return `false` for the wait to continue. The predicate
204  /// will be invoked with the mutex locked.
205  ///
206  /// @note Since the predicate is invoked with the mutex locked, if it
207  /// accesses other guarded resources, the predicate itself must be
208  /// decorated with the IPLR_REQUIRES directive. For instance,
209  ///
210  /// ```c++
211  /// [] () IPLR_REQUIRES(mutex) {
212  /// return my_guarded_resource.should_stop_waiting;
213  /// }
214  /// ```
215  ///
216  /// @param mutex The mutex.
217  /// @param[in] duration The duration to wait for.
218  /// @param[in] should_stop_waiting The predicate invoked on spurious wakes.
219  /// Must return false for the wait to
220  /// continue.
221  ///
222  /// @tparam Representation The duration representation type.
223  /// @tparam Period The duration period type.
224  ///
225  /// @return The value of the predicate at the end of the wait.
226  ///
227  template <class Representation, class Period>
228  bool WaitFor(Mutex& mutex,
229  const std::chrono::duration<Representation, Period>& duration,
230  const Predicate& should_stop_waiting) IPLR_REQUIRES(mutex) {
231  return WaitUntil(mutex, std::chrono::steady_clock::now() + duration,
232  should_stop_waiting);
233  }
234 
235  //----------------------------------------------------------------------------
236  /// @brief Atomically unlocks the mutex and waits on the condition
237  /// variable indefinitely till the predicate determines that the
238  /// wait must end. Lock will be reacquired when the wait exits.
239  /// Spurious wakes may happen before the time point is reached. In
240  /// such cases the predicate is invoked and it must return `false`
241  /// for the wait to continue. The predicate will be invoked with
242  /// the mutex locked.
243  ///
244  /// @note Since the predicate is invoked with the mutex locked, if it
245  /// accesses other guarded resources, the predicate itself must be
246  /// decorated with the IPLR_REQUIRES directive. For instance,
247  ///
248  /// ```c++
249  /// [] () IPLR_REQUIRES(mutex) {
250  /// return my_guarded_resource.should_stop_waiting;
251  /// }
252  /// ```
253  ///
254  /// @param mutex The mutex
255  /// @param[in] should_stop_waiting The should stop waiting
256  ///
257  void Wait(Mutex& mutex, const Predicate& should_stop_waiting)
258  IPLR_REQUIRES(mutex) {
259  std::unique_lock lock(mutex.mutex_, std::adopt_lock);
260  cv_.wait(lock, should_stop_waiting);
261  lock.release();
262  }
263 
264  private:
265  std::condition_variable cv_;
266 };
267 
268 } // namespace impeller
269 
270 #endif // FLUTTER_IMPELLER_BASE_THREAD_H_
A condition variable exactly similar to the one in libcxx with two major differences:
Definition: thread.h:143
bool WaitFor(Mutex &mutex, const std::chrono::duration< Representation, Period > &duration, const Predicate &should_stop_waiting) IPLR_REQUIRES(mutex)
Atomically unlocks the mutex and waits on the condition variable for a designated duration....
Definition: thread.h:228
bool WaitUntil(Mutex &mutex, const std::chrono::time_point< Clock, Duration > &time_point, const Predicate &should_stop_waiting) IPLR_REQUIRES(mutex)
Atomically unlocks the mutex and waits on the condition variable up to a specified time point....
Definition: thread.h:189
ConditionVariable(const ConditionVariable &)=delete
void Wait(Mutex &mutex, const Predicate &should_stop_waiting) IPLR_REQUIRES(mutex)
Atomically unlocks the mutex and waits on the condition variable indefinitely till the predicate dete...
Definition: thread.h:257
std::function< bool()> Predicate
Definition: thread.h:157
ConditionVariable & operator=(const ConditionVariable &)=delete
~Lock() IPLR_RELEASE()
Definition: thread.h:78
Lock(Mutex &mutex) IPLR_ACQUIRE(mutex)
Definition: thread.h:74
~ReaderLock() IPLR_RELEASE()
Definition: thread.h:99
ReaderLock(RWMutex &mutex) IPLR_ACQUIRE_SHARED(mutex)
Definition: thread.h:94
~WriterLock() IPLR_RELEASE()
Definition: thread.h:119
WriterLock(RWMutex &mutex) IPLR_ACQUIRE(mutex)
Definition: thread.h:115
class IPLR_CAPABILITY("mutex") Mutex
Definition: thread.h:22
#define IPLR_REQUIRES(...)
Definition: thread_safety.h:30
#define IPLR_ACQUIRE_SHARED(...)
Definition: thread_safety.h:39
#define IPLR_ACQUIRE(...)
Definition: thread_safety.h:36
#define IPLR_RELEASE(...)
Definition: thread_safety.h:42
#define IPLR_RELEASE_SHARED(...)
Definition: thread_safety.h:45
#define IPLR_SCOPED_CAPABILITY
Definition: thread_safety.h:16