Flutter Engine
 
Loading...
Searching...
No Matches
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
10
11namespace impeller {
12
13enum class MyMaskBits : uint32_t {
14 kFoo = 0,
15 kBar = 1 << 0,
16 kBaz = 1 << 1,
17 kBang = 1 << 2,
18};
19
21
23
24namespace testing {
25
26struct Foo {
27 Mutex mtx;
29};
30
31struct RWFoo {
32 RWMutex mtx;
34};
35
36TEST(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
45TEST(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
53TEST(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
66TEST(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
84struct CVTest {
85 Mutex mutex;
87 uint32_t rando_ivar IPLR_GUARDED_BY(mutex) = 0;
88};
89
90TEST(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
113TEST(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
134TEST(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
151TEST(ConditionVariableTest, TestsCriticalSectionAfterWaitForUntil) {
152 std::vector<std::thread> threads;
153 const auto kThreadCount = 10u;
154
155 Mutex mtx;
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
197TEST(ConditionVariableTest, TestsCriticalSectionAfterWait) {
198 std::vector<std::thread> threads;
199 const auto kThreadCount = 10u;
200
201 Mutex mtx;
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
243TEST(BaseTest, NoExceptionPromiseValue) {
245 std::future future = wrapper.get_future();
246 wrapper.set_value(123);
247 ASSERT_EQ(future.get(), 123);
248}
249
250TEST(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
259TEST(BaseTest, CanUseTypedMasks) {
260 {
261 MyMask mask;
262 ASSERT_EQ(static_cast<uint32_t>(mask), 0u);
263 ASSERT_FALSE(mask);
264 }
265
266 {
268 ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
269 ASSERT_TRUE(mask);
270 }
271
272 {
274 MyMask mask(mask2);
275 ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
276 ASSERT_TRUE(mask);
277 }
278
279 {
281 MyMask mask(std::move(mask2)); // NOLINT
282 ASSERT_EQ(static_cast<uint32_t>(mask), 1u);
283 ASSERT_TRUE(mask);
284 }
285
292
293 {
296 ASSERT_EQ(static_cast<uint32_t>(m1 & m2), 0u);
297 ASSERT_FALSE(m1 & m2);
298 }
299
300 {
303 ASSERT_EQ(static_cast<uint32_t>(m1 | m2), ((1u << 0u) | (1u << 1u)));
304 ASSERT_TRUE(m1 | m2);
305 }
306
307 {
310 ASSERT_EQ(static_cast<uint32_t>(m1 ^ m2), ((1u << 0u) ^ (1u << 1u)));
311 ASSERT_TRUE(m1 ^ m2);
312 }
313
314 {
316 ASSERT_EQ(static_cast<uint32_t>(~m1), (~(1u << 0u)));
317 ASSERT_TRUE(m1);
318 }
319
320 {
323 m2 = m1;
324 ASSERT_EQ(m2, MyMaskBits::kBar);
325 }
326
327 {
329 ASSERT_TRUE(m);
330 }
331
332 {
334 ASSERT_FALSE(m);
335 }
336
337 {
339 ASSERT_TRUE(m);
340 }
341
342 {
344 ASSERT_TRUE(m);
345 }
346
347 {
350 m2 |= m1;
351 ASSERT_EQ(m1, MyMaskBits::kBar);
353 ASSERT_EQ(m2, pred);
354 }
355
356 {
359 m2 &= m1;
360 ASSERT_EQ(m1, MyMaskBits::kBar);
362 ASSERT_EQ(m2, pred);
363 }
364
365 {
368 m2 ^= m1;
369 ASSERT_EQ(m1, MyMaskBits::kBar);
371 ASSERT_EQ(m2, pred);
372 }
373
374 {
377 ASSERT_TRUE(m);
378 }
379
380 {
383 ASSERT_TRUE(m);
384 }
385
386 {
389 ASSERT_FALSE(m);
390 }
391
392 {
395 ASSERT_FALSE(m);
396 }
397
398 {
401 ASSERT_TRUE(m);
402 }
403
404 {
407 ASSERT_TRUE(m);
408 }
409
410 {
412 MyMask m = ~x;
413 ASSERT_TRUE(m);
414 }
415
416 {
419 ASSERT_TRUE(x < m);
420 ASSERT_TRUE(x <= m);
421 }
422
423 {
426 ASSERT_FALSE(x == m);
427 }
428
429 {
432 ASSERT_TRUE(x != m);
433 }
434
435 {
438 ASSERT_FALSE(x > m);
439 ASSERT_FALSE(x >= m);
440 }
441}
442
443} // namespace testing
444} // namespace impeller
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
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::future< T > get_future()
Definition promise.h:36
void set_value(const T &value)
Definition promise.h:38
int32_t x
static constexpr uint64_t kThreadCount
#define IMPELLER_ENUM_IS_MASK(enum_name)
Declare this in the "impeller" namespace to make the enum maskable.
Definition mask.h:21
TEST(FrameTimingsRecorderTest, RecordVsync)
Mask< MyMaskBits > MyMask
A mask of typed enums.
Definition mask.h:33
uint32_t rando_ivar IPLR_GUARDED_BY(mutex)=0
int a IPLR_GUARDED_BY(mtx)
const size_t start
#define IPLR_REQUIRES(...)