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"
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 
21 
23 
24 namespace testing {
25 
26 struct Foo {
27  Mutex mtx;
29 };
30 
31 struct RWFoo {
32  RWMutex 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;
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;
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;
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 
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  {
343  MyMask m = ~MyMaskBits::kBar;
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  {
376  MyMask m = x | MyMaskBits::kBaz;
377  ASSERT_TRUE(m);
378  }
379 
380  {
382  MyMask m = MyMaskBits::kBaz | x;
383  ASSERT_TRUE(m);
384  }
385 
386  {
388  MyMask m = x & MyMaskBits::kBaz;
389  ASSERT_FALSE(m);
390  }
391 
392  {
394  MyMask m = MyMaskBits::kBaz & x;
395  ASSERT_FALSE(m);
396  }
397 
398  {
400  MyMask m = x ^ MyMaskBits::kBaz;
401  ASSERT_TRUE(m);
402  }
403 
404  {
406  MyMask m = MyMaskBits::kBaz ^ x;
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
void set_value(const T &value)
Definition: promise.h:38
std::future< T > get_future()
Definition: promise.h:36
int32_t x
TEST(AllocationSizeTest, CanCreateTypedAllocations)
IMPELLER_ENUM_IS_MASK(MyMaskBits)
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(...)
Definition: thread_safety.h:30