| Flutter Impeller |

base_unittests.cc
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 #include "flutter/testing/testing.h"
6 #include "impeller/base/mask.h"
7 #include "impeller/base/promise.h"
8 #include "impeller/base/strings.h"
9 #include "impeller/base/thread.h"
10
11 namespace impeller {
12
13 enum class MyMaskBits : uint32_t {
14 kFoo = 0,
15 kBar = 1 << 0,
16 kBaz = 1 << 1,
17 kBang = 1 << 2,
18 };
19
20 using MyMask = Mask<MyMaskBits>;
21
22 IMPELLER_ENUM_IS_MASK(MyMaskBits);
23
24 namespace testing {
25
26 struct Foo {
27 Mutex mtx;
28 int a IPLR_GUARDED_BY(mtx);
29 };
30
31 struct RWFoo {
32 RWMutex mtx;
33 int a IPLR_GUARDED_BY(mtx);
34 };
35
36 TEST(ThreadTest, CanCreateMutex) {
37 Foo f = {};
38
39 // f.a = 100; <--- Static analysis error.
40 f.mtx.Lock();
41 f.a = 100;
42 f.mtx.Unlock();
43 }
44
45 TEST(ThreadTest, CanCreateMutexLock) {
46 Foo f = {};
47
48 // f.a = 100; <--- Static analysis error.
49 auto a = Lock(f.mtx);
50 f.a = 100;
51 }
52
53 TEST(ThreadTest, CanCreateRWMutex) {
54 RWFoo f = {};
55
56 // f.a = 100; <--- Static analysis error.
57 f.mtx.LockWriter();
58 f.a = 100;
59 f.mtx.UnlockWriter();
60 // int b = f.a; <--- Static analysis error.
61 f.mtx.LockReader();
62 [[maybe_unused]] int b = f.a; // NOLINT(clang-analyzer-deadcode.DeadStores)
63 f.mtx.UnlockReader();
64 }
65
66 TEST(ThreadTest, CanCreateRWMutexLock) {
67 RWFoo f = {};
68
69 // f.a = 100; <--- Static analysis error.
70 {
71 auto write_lock = WriterLock{f.mtx};
72 f.a = 100;
73 }
74
75 // int b = f.a; <--- Static analysis error.
76 {
77 auto read_lock = ReaderLock(f.mtx);
78 [[maybe_unused]] int b = f.a; // NOLINT(clang-analyzer-deadcode.DeadStores)
79 }
80
81 // f.mtx.UnlockReader(); <--- Static analysis error.
82 }
83
84 struct CVTest {
85 Mutex mutex;
86 ConditionVariable cv;
87 uint32_t rando_ivar IPLR_GUARDED_BY(mutex) = 0;
88 };
89
90 TEST(ConditionVariableTest, WaitUntil) {
91 CVTest test;
92 // test.rando_ivar = 12; // <--- Static analysis error
93 for (size_t i = 0; i < 2; ++i) {
94 test.mutex.Lock(); // <--- Static analysis error without this.
95 auto result = test.cv.WaitUntil(
96 test.mutex,
97 std::chrono::high_resolution_clock::now() +
98 std::chrono::milliseconds{10},
99 [&]() IPLR_REQUIRES(test.mutex) {
100 test.rando_ivar = 12; // <-- Static analysics error without the
101 // IPLR_REQUIRES on the pred.
102 return false;
103 });
104 test.mutex.Unlock();
105 ASSERT_FALSE(result);
106 }
107 Lock lock(test.mutex); // <--- Static analysis error without this.
108 // The predicate never returns true. So return has to be due to a non-spurious
109 // wake.
110 ASSERT_EQ(test.rando_ivar, 12u);
111 }
112
113 TEST(ConditionVariableTest, WaitFor) {
114 CVTest test;
115 // test.rando_ivar = 12; // <--- Static analysis error
116 for (size_t i = 0; i < 2; ++i) {
117 test.mutex.Lock(); // <--- Static analysis error without this.
118 auto result = test.cv.WaitFor(
119 test.mutex, std::chrono::milliseconds{10},
120 [&]() IPLR_REQUIRES(test.mutex) {
121 test.rando_ivar = 12; // <-- Static analysics error without the
122 // IPLR_REQUIRES on the pred.
123 return false;
124 });
125 test.mutex.Unlock();
126 ASSERT_FALSE(result);
127 }
128 Lock lock(test.mutex); // <--- Static analysis error without this.
129 // The predicate never returns true. So return has to be due to a non-spurious
130 // wake.
131 ASSERT_EQ(test.rando_ivar, 12u);
132 }
133
134 TEST(ConditionVariableTest, WaitForever) {
135 CVTest test;
136 // test.rando_ivar = 12; // <--- Static analysis error
137 for (size_t i = 0; i < 2; ++i) {
138 test.mutex.Lock(); // <--- Static analysis error without this.
139 test.cv.Wait(test.mutex, [&]() IPLR_REQUIRES(test.mutex) {
140 test.rando_ivar = 12; // <-- Static analysics error without
141 // the IPLR_REQUIRES on the pred.
142 return true;
143 });
144 test.mutex.Unlock();
145 }
146 Lock lock(test.mutex); // <--- Static analysis error without this.
147 // The wake only happens when the predicate returns true.
148 ASSERT_EQ(test.rando_ivar, 12u);
149 }
150
151 TEST(ConditionVariableTest, TestsCriticalSectionAfterWaitForUntil) {
152 std::vector<std::thread> threads;
153 const auto kThreadCount = 10u;
154
155 Mutex mtx;
156 ConditionVariable cv;
157 size_t sum = 0u;
158
159 std::condition_variable start_cv;
160 std::mutex start_mtx;
161 bool start = false;
162 auto start_predicate = [&start]() { return start; };
163 auto thread_main = [&]() {
164 {
165 std::unique_lock start_lock(start_mtx);
166 start_cv.wait(start_lock, start_predicate);
167 }
168
169 mtx.Lock();
170 cv.WaitFor(mtx, std::chrono::milliseconds{0u}, []() { return true; });
171 auto old_val = sum;
172 std::this_thread::sleep_for(std::chrono::milliseconds{100u});
173 sum = old_val + 1u;
174 mtx.Unlock();
175 };
176 // Launch all threads. They will wait for the start CV to be signaled.
177 threads.reserve(kThreadCount);
178 for (size_t i = 0; i < kThreadCount; i++) {
179 threads.emplace_back(thread_main);
180 }
181 // Notify all threads that the test may start.
182 {
183 {
184 std::scoped_lock start_lock(start_mtx);
185 start = true;
186 }
187 start_cv.notify_all();
188 }
189 // Join all threads.
190 ASSERT_EQ(threads.size(), kThreadCount);
191 for (size_t i = 0; i < kThreadCount; i++) {
192 threads[i].join();
193 }
194 ASSERT_EQ(sum, kThreadCount);
195 }
196
197 TEST(ConditionVariableTest, TestsCriticalSectionAfterWait) {
198 std::vector<std::thread> threads;
199 const auto kThreadCount = 10u;
200
201 Mutex mtx;
202 ConditionVariable cv;
203 size_t sum = 0u;
204
205 std::condition_variable start_cv;
206 std::mutex start_mtx;
207 bool start = false;
208 auto start_predicate = [&start]() { return start; };
209 auto thread_main = [&]() {
210 {
211 std::unique_lock start_lock(start_mtx);
212 start_cv.wait(start_lock, start_predicate);
213 }
214
215 mtx.Lock();
216 cv.Wait(mtx, []() { return true; });
217 auto old_val = sum;
218 std::this_thread::sleep_for(std::chrono::milliseconds{100u});
219 sum = old_val + 1u;
220 mtx.Unlock();
221 };
222 // Launch all threads. They will wait for the start CV to be signaled.
223 threads.reserve(kThreadCount);
224 for (size_t i = 0; i < kThreadCount; i++) {
225 threads.emplace_back(thread_main);
226 }
227 // Notify all threads that the test may start.
228 {
229 {
230 std::scoped_lock start_lock(start_mtx);
231 start = true;
232 }
233 start_cv.notify_all();
234 }
235 // Join all threads.
236 ASSERT_EQ(threads.size(), kThreadCount);
237 for (size_t i = 0; i < kThreadCount; i++) {
238 threads[i].join();
239 }
240 ASSERT_EQ(sum, kThreadCount);
241 }
242
243 TEST(BaseTest, NoExceptionPromiseValue) {
244 NoExceptionPromise<int> wrapper;
245 std::future future = wrapper.get_future();
246 wrapper.set_value(123);
247 ASSERT_EQ(future.get(), 123);
248 }
249
250 TEST(BaseTest, NoExceptionPromiseEmpty) {
251 auto wrapper = std::make_shared<NoExceptionPromise<int>>();
252 std::future future = wrapper->get_future();
253
254 // Destroy the empty promise with the future still pending. Verify that the
255 // process does not abort while destructing the promise.
256 wrapper.reset();
257 }
258
259 TEST(BaseTest, CanUseTypedMasks) {
260 {
261 MyMask mask;
262 ASSERT_EQ(static_cast<uint32_t>(mask), 0u);
263 ASSERT_FALSE(mask);
264 }
265
266 {
267 MyMask mask(MyMaskBits::kBar);
268 ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
269 ASSERT_TRUE(mask);
270 }
271
272 {
273 MyMask mask2(MyMaskBits::kBar);
274 MyMask mask(mask2);
275 ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
276 ASSERT_TRUE(mask);
277 }
278
279 {
280 MyMask mask2(MyMaskBits::kBar);
281 MyMask mask(std::move(mask2)); // NOLINT
282 ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
283 ASSERT_TRUE(mask);
284 }
285
286 ASSERT_LT(MyMaskBits::kBar, MyMaskBits::kBaz);
287 ASSERT_LE(MyMaskBits::kBar, MyMaskBits::kBaz);
288 ASSERT_GT(MyMaskBits::kBaz, MyMaskBits::kBar);
289 ASSERT_GE(MyMaskBits::kBaz, MyMaskBits::kBar);
290 ASSERT_EQ(MyMaskBits::kBaz, MyMaskBits::kBaz);
291 ASSERT_NE(MyMaskBits::kBaz, MyMaskBits::kBang);
292
293 {
294 MyMask m1(MyMaskBits::kBar);
295 MyMask m2(MyMaskBits::kBaz);
296 ASSERT_EQ(static_cast<uint32_t>(m1 & m2), 0u);
297 ASSERT_FALSE(m1 & m2);
298 }
299
300 {
301 MyMask m1(MyMaskBits::kBar);
302 MyMask m2(MyMaskBits::kBaz);
303 ASSERT_EQ(static_cast<uint32_t>(m1 | m2), ((1u << 0u) | (1u << 1u)));
304 ASSERT_TRUE(m1 | m2);
305 }
306
307 {
308 MyMask m1(MyMaskBits::kBar);
309 MyMask m2(MyMaskBits::kBaz);
310 ASSERT_EQ(static_cast<uint32_t>(m1 ^ m2), ((1u << 0u) ^ (1u << 1u)));
311 ASSERT_TRUE(m1 ^ m2);
312 }
313
314 {
315 MyMask m1(MyMaskBits::kBar);
316 ASSERT_EQ(static_cast<uint32_t>(~m1), (~(1u << 0u)));
317 ASSERT_TRUE(m1);
318 }
319
320 {
321 MyMask m1 = MyMaskBits::kBar;
322 MyMask m2 = MyMaskBits::kBaz;
323 m2 = m1;
324 ASSERT_EQ(m2, MyMaskBits::kBar);
325 }
326
327 {
328 MyMask m = MyMaskBits::kBar | MyMaskBits::kBaz;
329 ASSERT_TRUE(m);
330 }
331
332 {
333 MyMask m = MyMaskBits::kBar & MyMaskBits::kBaz;
334 ASSERT_FALSE(m);
335 }
336
337 {
338 MyMask m = MyMaskBits::kBar ^ MyMaskBits::kBaz;
339 ASSERT_TRUE(m);
340 }
341
342 {
343 MyMask m = ~MyMaskBits::kBar;
344 ASSERT_TRUE(m);
345 }
346
347 {
348 MyMask m1 = MyMaskBits::kBar;
349 MyMask m2 = MyMaskBits::kBaz;
350 m2 |= m1;
351 ASSERT_EQ(m1, MyMaskBits::kBar);
352 MyMask pred = MyMaskBits::kBar | MyMaskBits::kBaz;
353 ASSERT_EQ(m2, pred);
354 }
355
356 {
357 MyMask m1 = MyMaskBits::kBar;
358 MyMask m2 = MyMaskBits::kBaz;
359 m2 &= m1;
360 ASSERT_EQ(m1, MyMaskBits::kBar);
361 MyMask pred = MyMaskBits::kBar & MyMaskBits::kBaz;
362 ASSERT_EQ(m2, pred);
363 }
364
365 {
366 MyMask m1 = MyMaskBits::kBar;
367 MyMask m2 = MyMaskBits::kBaz;
368 m2 ^= m1;
369 ASSERT_EQ(m1, MyMaskBits::kBar);
370 MyMask pred = MyMaskBits::kBar ^ MyMaskBits::kBaz;
371 ASSERT_EQ(m2, pred);
372 }
373
374 {
375 MyMask x = MyMaskBits::kBar;
376 MyMask m = x | MyMaskBits::kBaz;
377 ASSERT_TRUE(m);
378 }
379
380 {
381 MyMask x = MyMaskBits::kBar;
382 MyMask m = MyMaskBits::kBaz | x;
383 ASSERT_TRUE(m);
384 }
385
386 {
387 MyMask x = MyMaskBits::kBar;
388 MyMask m = x & MyMaskBits::kBaz;
389 ASSERT_FALSE(m);
390 }
391
392 {
393 MyMask x = MyMaskBits::kBar;
394 MyMask m = MyMaskBits::kBaz & x;
395 ASSERT_FALSE(m);
396 }
397
398 {
399 MyMask x = MyMaskBits::kBar;
400 MyMask m = x ^ MyMaskBits::kBaz;
401 ASSERT_TRUE(m);
402 }
403
404 {
405 MyMask x = MyMaskBits::kBar;
406 MyMask m = MyMaskBits::kBaz ^ x;
407 ASSERT_TRUE(m);
408 }
409
410 {
411 MyMask x = MyMaskBits::kBar;
412 MyMask m = ~x;
413 ASSERT_TRUE(m);
414 }
415
416 {
417 MyMaskBits x = MyMaskBits::kBar;
418 MyMask m = MyMaskBits::kBaz;
419 ASSERT_TRUE(x < m);
420 ASSERT_TRUE(x <= m);
421 }
422
423 {
424 MyMaskBits x = MyMaskBits::kBar;
425 MyMask m = MyMaskBits::kBaz;
426 ASSERT_FALSE(x == m);
427 }
428
429 {
430 MyMaskBits x = MyMaskBits::kBar;
431 MyMask m = MyMaskBits::kBaz;
432 ASSERT_TRUE(x != m);
433 }
434
435 {
436 MyMaskBits x = MyMaskBits::kBar;
437 MyMask m = MyMaskBits::kBaz;
438 ASSERT_FALSE(x > m);
439 ASSERT_FALSE(x >= m);
440 }
441 }
442
443 } // namespace testing
444 } // namespace impeller
impeller::ConditionVariable
A condition variable exactly similar to the one in libcxx with two major differences:
Definition: thread.h:143
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:228
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:189
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:257
impeller::Lock
Definition: thread.h:72
impeller::NoExceptionPromise
Definition: promise.h:26
impeller::NoExceptionPromise::set_value
void set_value(const T &value)
Definition: promise.h:38
impeller::NoExceptionPromise::get_future
std::future< T > get_future()
Definition: promise.h:36
impeller::ReaderLock
Definition: thread.h:92
impeller::WriterLock
Definition: thread.h:113
x
int32_t x
Definition: dl_golden_unittests.cc:458
mask.h
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::testing::TEST
TEST(AllocationSizeTest, CanCreateTypedAllocations)
Definition: allocation_size_unittests.cc:10
impeller
Definition: allocation.cc:12
impeller::IMPELLER_ENUM_IS_MASK
IMPELLER_ENUM_IS_MASK(MyMaskBits)
impeller::MyMaskBits
MyMaskBits
Definition: base_unittests.cc:13
impeller::MyMaskBits::kFoo
@ kFoo
impeller::MyMaskBits::kBang
@ kBang
impeller::MyMaskBits::kBaz
@ kBaz
impeller::MyMaskBits::kBar
@ kBar
promise.h
strings.h
impeller::Mask
A mask of typed enums.
Definition: mask.h:33
impeller::testing::CVTest
Definition: base_unittests.cc:84
impeller::testing::CVTest::cv
ConditionVariable cv
Definition: base_unittests.cc:86
impeller::testing::CVTest::mutex
Mutex mutex
Definition: base_unittests.cc:85
impeller::testing::CVTest::IPLR_GUARDED_BY
uint32_t rando_ivar IPLR_GUARDED_BY(mutex)=0
impeller::testing::Foo
Definition: base_unittests.cc:26
impeller::testing::Foo::mtx
Mutex mtx
Definition: base_unittests.cc:27
impeller::testing::Foo::IPLR_GUARDED_BY
int a IPLR_GUARDED_BY(mtx)
impeller::testing::RWFoo
Definition: base_unittests.cc:31
impeller::testing::RWFoo::mtx
RWMutex mtx
Definition: base_unittests.cc:32
impeller::testing::RWFoo::IPLR_GUARDED_BY
int a IPLR_GUARDED_BY(mtx)
start
const size_t start
Definition: tessellator_libtess.cc:62
thread.h
IPLR_REQUIRES
#define IPLR_REQUIRES(...)
Definition: thread_safety.h:30