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