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 #define FML_USED_ON_EMBEDDER
6
7 #include <iostream>
8 #include <thread>
9
10 #include "flutter/fml/concurrent_message_loop.h"
11 #include "flutter/fml/message_loop.h"
12 #include "flutter/fml/synchronization/count_down_latch.h"
13 #include "flutter/fml/synchronization/waitable_event.h"
14 #include "flutter/fml/task_runner.h"
15 #include "gtest/gtest.h"
16
17 #define TIME_SENSITIVE(x) TimeSensitiveTest_##x
18 #if OS_WIN
19 #define PLATFORM_SPECIFIC_CAPTURE(...) [ __VA_ARGS__, count ]
20 #else
21 #define PLATFORM_SPECIFIC_CAPTURE(...) [__VA_ARGS__]
22 #endif
23
TEST(MessageLoop,GetCurrent)24 TEST(MessageLoop, GetCurrent) {
25 std::thread thread([]() {
26 fml::MessageLoop::EnsureInitializedForCurrentThread();
27 ASSERT_TRUE(fml::MessageLoop::GetCurrent().GetTaskRunner());
28 });
29 thread.join();
30 }
31
TEST(MessageLoop,DifferentThreadsHaveDifferentLoops)32 TEST(MessageLoop, DifferentThreadsHaveDifferentLoops) {
33 fml::MessageLoop* loop1 = nullptr;
34 fml::AutoResetWaitableEvent latch1;
35 fml::AutoResetWaitableEvent term1;
36 std::thread thread1([&loop1, &latch1, &term1]() {
37 fml::MessageLoop::EnsureInitializedForCurrentThread();
38 loop1 = &fml::MessageLoop::GetCurrent();
39 latch1.Signal();
40 term1.Wait();
41 });
42
43 fml::MessageLoop* loop2 = nullptr;
44 fml::AutoResetWaitableEvent latch2;
45 fml::AutoResetWaitableEvent term2;
46 std::thread thread2([&loop2, &latch2, &term2]() {
47 fml::MessageLoop::EnsureInitializedForCurrentThread();
48 loop2 = &fml::MessageLoop::GetCurrent();
49 latch2.Signal();
50 term2.Wait();
51 });
52 latch1.Wait();
53 latch2.Wait();
54 ASSERT_FALSE(loop1 == loop2);
55 term1.Signal();
56 term2.Signal();
57 thread1.join();
58 thread2.join();
59 }
60
TEST(MessageLoop,CanRunAndTerminate)61 TEST(MessageLoop, CanRunAndTerminate) {
62 bool started = false;
63 bool terminated = false;
64 std::thread thread([&started, &terminated]() {
65 fml::MessageLoop::EnsureInitializedForCurrentThread();
66 auto& loop = fml::MessageLoop::GetCurrent();
67 ASSERT_TRUE(loop.GetTaskRunner());
68 loop.GetTaskRunner()->PostTask([&terminated]() {
69 fml::MessageLoop::GetCurrent().Terminate();
70 terminated = true;
71 });
72 loop.Run();
73 started = true;
74 });
75 thread.join();
76 ASSERT_TRUE(started);
77 ASSERT_TRUE(terminated);
78 }
79
TEST(MessageLoop,NonDelayedTasksAreRunInOrder)80 TEST(MessageLoop, NonDelayedTasksAreRunInOrder) {
81 const size_t count = 100;
82 bool started = false;
83 bool terminated = false;
84 std::thread thread([&started, &terminated, count]() {
85 fml::MessageLoop::EnsureInitializedForCurrentThread();
86 auto& loop = fml::MessageLoop::GetCurrent();
87 size_t current = 0;
88 for (size_t i = 0; i < count; i++) {
89 loop.GetTaskRunner()->PostTask(
90 PLATFORM_SPECIFIC_CAPTURE(&terminated, i, ¤t)() {
91 ASSERT_EQ(current, i);
92 current++;
93 if (count == i + 1) {
94 fml::MessageLoop::GetCurrent().Terminate();
95 terminated = true;
96 }
97 });
98 }
99 loop.Run();
100 ASSERT_EQ(current, count);
101 started = true;
102 });
103 thread.join();
104 ASSERT_TRUE(started);
105 ASSERT_TRUE(terminated);
106 }
107
TEST(MessageLoop,DelayedTasksAtSameTimeAreRunInOrder)108 TEST(MessageLoop, DelayedTasksAtSameTimeAreRunInOrder) {
109 const size_t count = 100;
110 bool started = false;
111 bool terminated = false;
112 std::thread thread([&started, &terminated, count]() {
113 fml::MessageLoop::EnsureInitializedForCurrentThread();
114 auto& loop = fml::MessageLoop::GetCurrent();
115 size_t current = 0;
116 const auto now_plus_some =
117 fml::TimePoint::Now() + fml::TimeDelta::FromMilliseconds(2);
118 for (size_t i = 0; i < count; i++) {
119 loop.GetTaskRunner()->PostTaskForTime(
120 PLATFORM_SPECIFIC_CAPTURE(&terminated, i, ¤t)() {
121 ASSERT_EQ(current, i);
122 current++;
123 if (count == i + 1) {
124 fml::MessageLoop::GetCurrent().Terminate();
125 terminated = true;
126 }
127 },
128 now_plus_some);
129 }
130 loop.Run();
131 ASSERT_EQ(current, count);
132 started = true;
133 });
134 thread.join();
135 ASSERT_TRUE(started);
136 ASSERT_TRUE(terminated);
137 }
138
TEST(MessageLoop,CheckRunsTaskOnCurrentThread)139 TEST(MessageLoop, CheckRunsTaskOnCurrentThread) {
140 fml::RefPtr<fml::TaskRunner> runner;
141 fml::AutoResetWaitableEvent latch;
142 std::thread thread([&runner, &latch]() {
143 fml::MessageLoop::EnsureInitializedForCurrentThread();
144 auto& loop = fml::MessageLoop::GetCurrent();
145 runner = loop.GetTaskRunner();
146 latch.Signal();
147 ASSERT_TRUE(loop.GetTaskRunner()->RunsTasksOnCurrentThread());
148 });
149 latch.Wait();
150 ASSERT_TRUE(runner);
151 ASSERT_FALSE(runner->RunsTasksOnCurrentThread());
152 thread.join();
153 }
154
TEST(MessageLoop,TIME_SENSITIVE (SingleDelayedTaskByDelta))155 TEST(MessageLoop, TIME_SENSITIVE(SingleDelayedTaskByDelta)) {
156 bool checked = false;
157 std::thread thread([&checked]() {
158 fml::MessageLoop::EnsureInitializedForCurrentThread();
159 auto& loop = fml::MessageLoop::GetCurrent();
160 auto begin = fml::TimePoint::Now();
161 loop.GetTaskRunner()->PostDelayedTask(
162 [begin, &checked]() {
163 auto delta = fml::TimePoint::Now() - begin;
164 auto ms = delta.ToMillisecondsF();
165 ASSERT_GE(ms, 3);
166 ASSERT_LE(ms, 7);
167 checked = true;
168 fml::MessageLoop::GetCurrent().Terminate();
169 },
170 fml::TimeDelta::FromMilliseconds(5));
171 loop.Run();
172 });
173 thread.join();
174 ASSERT_TRUE(checked);
175 }
176
TEST(MessageLoop,TIME_SENSITIVE (SingleDelayedTaskForTime))177 TEST(MessageLoop, TIME_SENSITIVE(SingleDelayedTaskForTime)) {
178 bool checked = false;
179 std::thread thread([&checked]() {
180 fml::MessageLoop::EnsureInitializedForCurrentThread();
181 auto& loop = fml::MessageLoop::GetCurrent();
182 auto begin = fml::TimePoint::Now();
183 loop.GetTaskRunner()->PostTaskForTime(
184 [begin, &checked]() {
185 auto delta = fml::TimePoint::Now() - begin;
186 auto ms = delta.ToMillisecondsF();
187 ASSERT_GE(ms, 3);
188 ASSERT_LE(ms, 7);
189 checked = true;
190 fml::MessageLoop::GetCurrent().Terminate();
191 },
192 fml::TimePoint::Now() + fml::TimeDelta::FromMilliseconds(5));
193 loop.Run();
194 });
195 thread.join();
196 ASSERT_TRUE(checked);
197 }
198
TEST(MessageLoop,TIME_SENSITIVE (MultipleDelayedTasksWithIncreasingDeltas))199 TEST(MessageLoop, TIME_SENSITIVE(MultipleDelayedTasksWithIncreasingDeltas)) {
200 const auto count = 10;
201 int checked = false;
202 std::thread thread(PLATFORM_SPECIFIC_CAPTURE(&checked)() {
203 fml::MessageLoop::EnsureInitializedForCurrentThread();
204 auto& loop = fml::MessageLoop::GetCurrent();
205 for (int target_ms = 0 + 2; target_ms < count + 2; target_ms++) {
206 auto begin = fml::TimePoint::Now();
207 loop.GetTaskRunner()->PostDelayedTask(
208 PLATFORM_SPECIFIC_CAPTURE(begin, target_ms, &checked)() {
209 auto delta = fml::TimePoint::Now() - begin;
210 auto ms = delta.ToMillisecondsF();
211 ASSERT_GE(ms, target_ms - 2);
212 ASSERT_LE(ms, target_ms + 2);
213 checked++;
214 if (checked == count) {
215 fml::MessageLoop::GetCurrent().Terminate();
216 }
217 },
218 fml::TimeDelta::FromMilliseconds(target_ms));
219 }
220 loop.Run();
221 });
222 thread.join();
223 ASSERT_EQ(checked, count);
224 }
225
TEST(MessageLoop,TIME_SENSITIVE (MultipleDelayedTasksWithDecreasingDeltas))226 TEST(MessageLoop, TIME_SENSITIVE(MultipleDelayedTasksWithDecreasingDeltas)) {
227 const auto count = 10;
228 int checked = false;
229 std::thread thread(PLATFORM_SPECIFIC_CAPTURE(&checked)() {
230 fml::MessageLoop::EnsureInitializedForCurrentThread();
231 auto& loop = fml::MessageLoop::GetCurrent();
232 for (int target_ms = count + 2; target_ms > 0 + 2; target_ms--) {
233 auto begin = fml::TimePoint::Now();
234 loop.GetTaskRunner()->PostDelayedTask(
235 PLATFORM_SPECIFIC_CAPTURE(begin, target_ms, &checked)() {
236 auto delta = fml::TimePoint::Now() - begin;
237 auto ms = delta.ToMillisecondsF();
238 ASSERT_GE(ms, target_ms - 2);
239 ASSERT_LE(ms, target_ms + 2);
240 checked++;
241 if (checked == count) {
242 fml::MessageLoop::GetCurrent().Terminate();
243 }
244 },
245 fml::TimeDelta::FromMilliseconds(target_ms));
246 }
247 loop.Run();
248 });
249 thread.join();
250 ASSERT_EQ(checked, count);
251 }
252
TEST(MessageLoop,TaskObserverFire)253 TEST(MessageLoop, TaskObserverFire) {
254 bool started = false;
255 bool terminated = false;
256 std::thread thread([&started, &terminated]() {
257 fml::MessageLoop::EnsureInitializedForCurrentThread();
258 const size_t count = 25;
259 auto& loop = fml::MessageLoop::GetCurrent();
260 size_t task_count = 0;
261 size_t obs_count = 0;
262 auto obs = PLATFORM_SPECIFIC_CAPTURE(&obs_count)() { obs_count++; };
263 for (size_t i = 0; i < count; i++) {
264 loop.GetTaskRunner()->PostTask(
265 PLATFORM_SPECIFIC_CAPTURE(&terminated, i, &task_count)() {
266 ASSERT_EQ(task_count, i);
267 task_count++;
268 if (count == i + 1) {
269 fml::MessageLoop::GetCurrent().Terminate();
270 terminated = true;
271 }
272 });
273 }
274 loop.AddTaskObserver(0, obs);
275 loop.Run();
276 ASSERT_EQ(task_count, count);
277 ASSERT_EQ(obs_count, count);
278 started = true;
279 });
280 thread.join();
281 ASSERT_TRUE(started);
282 ASSERT_TRUE(terminated);
283 }
284
TEST(MessageLoop,CanCreateAndShutdownConcurrentMessageLoopsOverAndOver)285 TEST(MessageLoop, CanCreateAndShutdownConcurrentMessageLoopsOverAndOver) {
286 for (size_t i = 0; i < 10; ++i) {
287 auto loop = fml::ConcurrentMessageLoop::Create(i + 1);
288 ASSERT_EQ(loop->GetWorkerCount(), i + 1);
289 }
290 }
291
TEST(MessageLoop,CanCreateConcurrentMessageLoop)292 TEST(MessageLoop, CanCreateConcurrentMessageLoop) {
293 auto loop = fml::ConcurrentMessageLoop::Create();
294 auto task_runner = loop->GetTaskRunner();
295 const size_t kCount = 10;
296 fml::CountDownLatch latch(kCount);
297 std::mutex thread_ids_mutex;
298 std::set<std::thread::id> thread_ids;
299 for (size_t i = 0; i < kCount; ++i) {
300 task_runner->PostTask([&]() {
301 std::this_thread::sleep_for(std::chrono::seconds(1));
302 std::cout << "Ran on thread: " << std::this_thread::get_id() << std::endl;
303 std::scoped_lock lock(thread_ids_mutex);
304 thread_ids.insert(std::this_thread::get_id());
305 latch.CountDown();
306 });
307 }
308 latch.Wait();
309 ASSERT_GE(thread_ids.size(), 1u);
310 }
311