4
4
5
5
#pragma once
6
6
7
+ #include < chrono>
8
+ #include < condition_variable>
9
+ #include < functional>
7
10
#include < memory>
8
11
#include < mutex>
9
12
#include < thread>
14
17
15
18
namespace impeller {
16
19
20
+ class ConditionVariable ;
21
+
17
22
class IPLR_CAPABILITY (" mutex" ) Mutex {
18
23
public:
19
24
Mutex () = default ;
@@ -25,6 +30,8 @@ class IPLR_CAPABILITY("mutex") Mutex {
25
30
void Unlock () IPLR_RELEASE () { mutex_.unlock (); }
26
31
27
32
private:
33
+ friend class ConditionVariable ;
34
+
28
35
std::mutex mutex_;
29
36
30
37
Mutex (const Mutex&) = delete ;
@@ -124,4 +131,134 @@ class IPLR_SCOPED_CAPABILITY WriterLock {
124
131
WriterLock& operator =(WriterLock&&) = delete ;
125
132
};
126
133
134
+ // ------------------------------------------------------------------------------
135
+ // / @brief A condition variable exactly similar to the one in libcxx with
136
+ // / two major differences:
137
+ // /
138
+ // / * On the Wait, WaitFor, and WaitUntil calls, static analysis
139
+ // / annotation are respected.
140
+ // / * There is no ability to wait on a condition variable and also
141
+ // / be susceptible to spurious wakes. This is because the
142
+ // / predicate is mandatory.
143
+ // /
144
+ class ConditionVariable {
145
+ public:
146
+ ConditionVariable () = default ;
147
+
148
+ ~ConditionVariable () = default ;
149
+
150
+ ConditionVariable (const ConditionVariable&) = delete ;
151
+
152
+ ConditionVariable& operator =(const ConditionVariable&) = delete ;
153
+
154
+ void NotifyOne () { cv_.notify_one (); }
155
+
156
+ void NotifyAll () { cv_.notify_all (); }
157
+
158
+ using Predicate = std::function<bool ()>;
159
+
160
+ // ----------------------------------------------------------------------------
161
+ // / @brief Atomically unlocks the mutex and waits on the condition
162
+ // / variable up to a specified time point. Spurious wakes may
163
+ // / happen before the time point is reached. In such cases the
164
+ // / predicate is invoked and it must return `false` for the wait
165
+ // / to continue. The predicate will be invoked with the mutex
166
+ // / locked.
167
+ // /
168
+ // / @note Since the predicate is invoked with the mutex locked, if it
169
+ // / accesses other guarded resources, the predicate itself must be
170
+ // / decorated with the IPLR_REQUIRES directive. For instance,
171
+ // /
172
+ // / ```c++
173
+ // / [] () IPLR_REQUIRES(mutex) {
174
+ // / return my_guarded_resource.should_stop_waiting;
175
+ // / }
176
+ // / ```
177
+ // /
178
+ // / @param mutex The mutex.
179
+ // / @param[in] time_point The time point to wait to.
180
+ // / @param[in] should_stop_waiting The predicate invoked on spurious wakes.
181
+ // / Must return false for the wait to
182
+ // / continue.
183
+ // /
184
+ // / @tparam Clock The clock type.
185
+ // / @tparam Duration The duration type.
186
+ // /
187
+ // / @return The value of the predicate at the end of the wait.
188
+ // /
189
+ template <class Clock , class Duration >
190
+ bool WaitUntil (Mutex& mutex,
191
+ const std::chrono::time_point<Clock, Duration>& time_point,
192
+ const Predicate& should_stop_waiting) IPLR_REQUIRES(mutex) {
193
+ std::unique_lock lock (mutex.mutex_ , std::adopt_lock);
194
+ return cv_.wait_until (lock, time_point, should_stop_waiting);
195
+ }
196
+
197
+ // ----------------------------------------------------------------------------
198
+ // / @brief Atomically unlocks the mutex and waits on the condition
199
+ // / variable for a designated duration. Spurious wakes may happen
200
+ // / before the time point is reached. In such cases the predicate
201
+ // / is invoked and it must return `false` for the wait to
202
+ // / continue. The predicate will be invoked with the mutex locked.
203
+ // /
204
+ // / @note Since the predicate is invoked with the mutex locked, if it
205
+ // / accesses other guarded resources, the predicate itself must be
206
+ // / decorated with the IPLR_REQUIRES directive. For instance,
207
+ // /
208
+ // / ```c++
209
+ // / [] () IPLR_REQUIRES(mutex) {
210
+ // / return my_guarded_resource.should_stop_waiting;
211
+ // / }
212
+ // / ```
213
+ // /
214
+ // / @param mutex The mutex.
215
+ // / @param[in] duration The duration to wait for.
216
+ // / @param[in] should_stop_waiting The predicate invoked on spurious wakes.
217
+ // / Must return false for the wait to
218
+ // / continue.
219
+ // /
220
+ // / @tparam Representation The duration representation type.
221
+ // / @tparam Period The duration period type.
222
+ // /
223
+ // / @return The value of the predicate at the end of the wait.
224
+ // /
225
+ template <class Representation , class Period >
226
+ bool WaitFor (Mutex& mutex,
227
+ const std::chrono::duration<Representation, Period>& duration,
228
+ const Predicate& should_stop_waiting) IPLR_REQUIRES(mutex) {
229
+ return WaitUntil (mutex, std::chrono::steady_clock::now () + duration,
230
+ should_stop_waiting);
231
+ }
232
+
233
+ // ----------------------------------------------------------------------------
234
+ // / @brief Atomically unlocks the mutex and waits on the condition
235
+ // / variable indefinitely till the predicate determines that the
236
+ // / wait must end. Spurious wakes may happen before the time point
237
+ // / is reached. In such cases the predicate is invoked and it must
238
+ // / return `false` for the wait to continue. The predicate will be
239
+ // / invoked with the mutex locked.
240
+ // /
241
+ // / @note Since the predicate is invoked with the mutex locked, if it
242
+ // / accesses other guarded resources, the predicate itself must be
243
+ // / decorated with the IPLR_REQUIRES directive. For instance,
244
+ // /
245
+ // / ```c++
246
+ // / [] () IPLR_REQUIRES(mutex) {
247
+ // / return my_guarded_resource.should_stop_waiting;
248
+ // / }
249
+ // / ```
250
+ // /
251
+ // / @param mutex The mutex
252
+ // / @param[in] should_stop_waiting The should stop waiting
253
+ // /
254
+ void Wait (Mutex& mutex, const Predicate& should_stop_waiting)
255
+ IPLR_REQUIRES(mutex) {
256
+ std::unique_lock lock (mutex.mutex_ , std::adopt_lock);
257
+ cv_.wait (lock, should_stop_waiting);
258
+ }
259
+
260
+ private:
261
+ std::condition_variable cv_;
262
+ };
263
+
127
264
} // namespace impeller
0 commit comments