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