• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &current)() {
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, &current)() {
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