| 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/strings.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;
81 ConditionVariable cv;
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;
151 ConditionVariable cv;
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;
196 ConditionVariable cv;
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