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"
7 #include "impeller/base/thread.h"
8 
9 namespace impeller {
10 namespace testing {
11 
12 struct Foo {
13  Mutex mtx;
14  int a IPLR_GUARDED_BY(mtx);
15 };
16 
17 struct RWFoo {
18  RWMutex mtx;
19  int a IPLR_GUARDED_BY(mtx);
20 };
21 
22 TEST(ThreadTest, CanCreateMutex) {
23  Foo f = {};
24 
25  // f.a = 100; <--- Static analysis error.
26  f.mtx.Lock();
27  f.a = 100;
28  f.mtx.Unlock();
29 }
30 
31 TEST(ThreadTest, CanCreateMutexLock) {
32  Foo f = {};
33 
34  // f.a = 100; <--- Static analysis error.
35  auto a = Lock(f.mtx);
36  f.a = 100;
37 }
38 
39 TEST(ThreadTest, CanCreateRWMutex) {
40  RWFoo f = {};
41 
42  // f.a = 100; <--- Static analysis error.
43  f.mtx.LockWriter();
44  f.a = 100;
45  f.mtx.UnlockWriter();
46  // int b = f.a; <--- Static analysis error.
47  f.mtx.LockReader();
48  int b = f.a; // NOLINT(clang-analyzer-deadcode.DeadStores)
49  FML_ALLOW_UNUSED_LOCAL(b);
50  f.mtx.UnlockReader();
51 }
52 
53 TEST(ThreadTest, CanCreateRWMutexLock) {
54  RWFoo f = {};
55 
56  // f.a = 100; <--- Static analysis error.
57  {
58  auto write_lock = WriterLock{f.mtx};
59  f.a = 100;
60  }
61 
62  // int b = f.a; <--- Static analysis error.
63  {
64  auto read_lock = ReaderLock(f.mtx);
65  int b = f.a; // NOLINT(clang-analyzer-deadcode.DeadStores)
66  FML_ALLOW_UNUSED_LOCAL(b);
67  }
68 
69  // f.mtx.UnlockReader(); <--- Static analysis error.
70 }
71 
72 TEST(StringsTest, CanSPrintF) {
73  ASSERT_EQ(SPrintF("%sx%d", "Hello", 12), "Hellox12");
74  ASSERT_EQ(SPrintF(""), "");
75  ASSERT_EQ(SPrintF("Hello"), "Hello");
76  ASSERT_EQ(SPrintF("%sx%.2f", "Hello", 12.122222), "Hellox12.12");
77 }
78 
79 struct CVTest {
80  Mutex mutex;
82  uint32_t rando_ivar IPLR_GUARDED_BY(mutex) = 0;
83 };
84 
85 TEST(ConditionVariableTest, WaitUntil) {
86  CVTest test;
87  // test.rando_ivar = 12; // <--- Static analysis error
88  for (size_t i = 0; i < 2; ++i) {
89  test.mutex.Lock(); // <--- Static analysis error without this.
90  auto result = test.cv.WaitUntil(
91  test.mutex,
92  std::chrono::high_resolution_clock::now() +
93  std::chrono::milliseconds{10},
94  [&]() IPLR_REQUIRES(test.mutex) {
95  test.rando_ivar = 12; // <-- Static analysics error without the
96  // IPLR_REQUIRES on the pred.
97  return false;
98  });
99  test.mutex.Unlock();
100  ASSERT_FALSE(result);
101  }
102  Lock lock(test.mutex); // <--- Static analysis error without this.
103  // The predicate never returns true. So return has to be due to a non-spurious
104  // wake.
105  ASSERT_EQ(test.rando_ivar, 12u);
106 }
107 
108 TEST(ConditionVariableTest, WaitFor) {
109  CVTest test;
110  // test.rando_ivar = 12; // <--- Static analysis error
111  for (size_t i = 0; i < 2; ++i) {
112  test.mutex.Lock(); // <--- Static analysis error without this.
113  auto result = test.cv.WaitFor(
114  test.mutex, std::chrono::milliseconds{10},
115  [&]() IPLR_REQUIRES(test.mutex) {
116  test.rando_ivar = 12; // <-- Static analysics error without the
117  // IPLR_REQUIRES on the pred.
118  return false;
119  });
120  test.mutex.Unlock();
121  ASSERT_FALSE(result);
122  }
123  Lock lock(test.mutex); // <--- Static analysis error without this.
124  // The predicate never returns true. So return has to be due to a non-spurious
125  // wake.
126  ASSERT_EQ(test.rando_ivar, 12u);
127 }
128 
129 TEST(ConditionVariableTest, WaitForever) {
130  CVTest test;
131  // test.rando_ivar = 12; // <--- Static analysis error
132  for (size_t i = 0; i < 2; ++i) {
133  test.mutex.Lock(); // <--- Static analysis error without this.
134  test.cv.Wait(test.mutex, [&]() IPLR_REQUIRES(test.mutex) {
135  test.rando_ivar = 12; // <-- Static analysics error without
136  // the IPLR_REQUIRES on the pred.
137  return true;
138  });
139  test.mutex.Unlock();
140  }
141  Lock lock(test.mutex); // <--- Static analysis error without this.
142  // The wake only happens when the predicate returns true.
143  ASSERT_EQ(test.rando_ivar, 12u);
144 }
145 
146 TEST(ConditionVariableTest, TestsCriticalSectionAfterWaitForUntil) {
147  std::vector<std::thread> threads;
148  const auto kThreadCount = 10u;
149 
150  Mutex mtx;
152  size_t sum = 0u;
153 
154  std::condition_variable start_cv;
155  std::mutex start_mtx;
156  bool start = false;
157  auto start_predicate = [&start]() { return start; };
158  auto thread_main = [&]() {
159  {
160  std::unique_lock start_lock(start_mtx);
161  start_cv.wait(start_lock, start_predicate);
162  }
163 
164  mtx.Lock();
165  cv.WaitFor(mtx, std::chrono::milliseconds{0u}, []() { return true; });
166  auto old_val = sum;
167  std::this_thread::sleep_for(std::chrono::milliseconds{100u});
168  sum = old_val + 1u;
169  mtx.Unlock();
170  };
171  // Launch all threads. They will wait for the start CV to be signaled.
172  for (size_t i = 0; i < kThreadCount; i++) {
173  threads.emplace_back(thread_main);
174  }
175  // Notify all threads that the test may start.
176  {
177  {
178  std::scoped_lock start_lock(start_mtx);
179  start = true;
180  }
181  start_cv.notify_all();
182  }
183  // Join all threads.
184  ASSERT_EQ(threads.size(), kThreadCount);
185  for (size_t i = 0; i < kThreadCount; i++) {
186  threads[i].join();
187  }
188  ASSERT_EQ(sum, kThreadCount);
189 }
190 
191 TEST(ConditionVariableTest, TestsCriticalSectionAfterWait) {
192  std::vector<std::thread> threads;
193  const auto kThreadCount = 10u;
194 
195  Mutex mtx;
197  size_t sum = 0u;
198 
199  std::condition_variable start_cv;
200  std::mutex start_mtx;
201  bool start = false;
202  auto start_predicate = [&start]() { return start; };
203  auto thread_main = [&]() {
204  {
205  std::unique_lock start_lock(start_mtx);
206  start_cv.wait(start_lock, start_predicate);
207  }
208 
209  mtx.Lock();
210  cv.Wait(mtx, []() { return true; });
211  auto old_val = sum;
212  std::this_thread::sleep_for(std::chrono::milliseconds{100u});
213  sum = old_val + 1u;
214  mtx.Unlock();
215  };
216  // Launch all threads. They will wait for the start CV to be signaled.
217  for (size_t i = 0; i < kThreadCount; i++) {
218  threads.emplace_back(thread_main);
219  }
220  // Notify all threads that the test may start.
221  {
222  {
223  std::scoped_lock start_lock(start_mtx);
224  start = true;
225  }
226  start_cv.notify_all();
227  }
228  // Join all threads.
229  ASSERT_EQ(threads.size(), kThreadCount);
230  for (size_t i = 0; i < kThreadCount; i++) {
231  threads[i].join();
232  }
233  ASSERT_EQ(sum, kThreadCount);
234 }
235 
236 } // namespace testing
237 } // namespace impeller
impeller::WriterLock
Definition: thread.h:116
impeller::testing::RWFoo
Definition: base_unittests.cc:17
impeller::Lock
Definition: thread.h:75
impeller::testing::RWFoo::mtx
RWMutex mtx
Definition: base_unittests.cc:18
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::testing::Foo::IPLR_GUARDED_BY
int a IPLR_GUARDED_BY(mtx)
impeller::ReaderLock
Definition: thread.h:95
impeller::SPrintF
std::string SPrintF(const char *format,...)
Definition: strings.cc:12
impeller::testing::TEST
TEST(CanvasRecorder, Save)
Definition: canvas_recorder_unittests.cc:61
strings.h
impeller::testing::CVTest::cv
ConditionVariable cv
Definition: base_unittests.cc:81
impeller::testing::Foo
Definition: base_unittests.cc:12
impeller::testing::CVTest
Definition: base_unittests.cc:79
impeller::ConditionVariable
A condition variable exactly similar to the one in libcxx with two major differences:
Definition: thread.h:146
impeller::testing::CVTest::IPLR_GUARDED_BY
uint32_t rando_ivar IPLR_GUARDED_BY(mutex)=0
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
thread.h
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_context.cc:10
impeller::testing::RWFoo::IPLR_GUARDED_BY
int a IPLR_GUARDED_BY(mtx)
impeller::testing::Foo::mtx
Mutex mtx
Definition: base_unittests.cc:13
impeller::testing::CVTest::mutex
Mutex mutex
Definition: base_unittests.cc:80
IPLR_REQUIRES
#define IPLR_REQUIRES(...)
Definition: thread_safety.h:30