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