Flutter Engine
raster_thread_merger_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/raster_thread_merger.h"
8 
9 #include <atomic>
10 #include <thread>
11 
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 namespace fml {
19 namespace testing {
20 
21 TEST(RasterThreadMerger, RemainMergedTillLeaseExpires) {
22  fml::MessageLoop* loop1 = nullptr;
25  std::thread thread1([&loop1, &latch1, &term1]() {
28  latch1.Signal();
29  term1.Wait();
30  });
31 
32  fml::MessageLoop* loop2 = nullptr;
35  std::thread thread2([&loop2, &latch2, &term2]() {
38  latch2.Signal();
39  term2.Wait();
40  });
41 
42  latch1.Wait();
43  latch2.Wait();
44 
46  fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
47  const auto raster_thread_merger_ =
48  fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);
49  const int kNumFramesMerged = 5;
50 
51  ASSERT_FALSE(raster_thread_merger_->IsMerged());
52 
53  raster_thread_merger_->MergeWithLease(kNumFramesMerged);
54 
55  for (int i = 0; i < kNumFramesMerged; i++) {
56  ASSERT_TRUE(raster_thread_merger_->IsMerged());
57  raster_thread_merger_->DecrementLease();
58  }
59 
60  ASSERT_FALSE(raster_thread_merger_->IsMerged());
61 
62  term1.Signal();
63  term2.Signal();
64  thread1.join();
65  thread2.join();
66 }
67 
68 TEST(RasterThreadMerger, IsNotOnRasterizingThread) {
69  fml::MessageLoop* loop1 = nullptr;
71  std::thread thread1([&loop1, &latch1]() {
74  loop1->GetTaskRunner()->PostTask([&]() { latch1.Signal(); });
75  loop1->Run();
76  });
77 
78  fml::MessageLoop* loop2 = nullptr;
80  std::thread thread2([&loop2, &latch2]() {
83  loop2->GetTaskRunner()->PostTask([&]() { latch2.Signal(); });
84  loop2->Run();
85  });
86 
87  latch1.Wait();
88  latch2.Wait();
89 
91  fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
92  const auto raster_thread_merger_ =
93  fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);
94 
95  fml::CountDownLatch pre_merge(2), post_merge(2), post_unmerge(2);
96 
97  loop1->GetTaskRunner()->PostTask([&]() {
98  ASSERT_FALSE(raster_thread_merger_->IsOnRasterizingThread());
99  ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
100  ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
101  pre_merge.CountDown();
102  });
103 
104  loop2->GetTaskRunner()->PostTask([&]() {
105  ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
106  ASSERT_FALSE(raster_thread_merger_->IsOnPlatformThread());
107  ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2);
108  pre_merge.CountDown();
109  });
110 
111  pre_merge.Wait();
112 
113  raster_thread_merger_->MergeWithLease(1);
114 
115  loop1->GetTaskRunner()->PostTask([&]() {
116  ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
117  ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
118  ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
119  post_merge.CountDown();
120  });
121 
122  loop2->GetTaskRunner()->PostTask([&]() {
123  // this will be false since this is going to be run
124  // on loop1 really.
125  ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
126  ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
127  ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
128  post_merge.CountDown();
129  });
130 
131  post_merge.Wait();
132 
133  raster_thread_merger_->DecrementLease();
134 
135  loop1->GetTaskRunner()->PostTask([&]() {
136  ASSERT_FALSE(raster_thread_merger_->IsOnRasterizingThread());
137  ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
138  ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
139  post_unmerge.CountDown();
140  });
141 
142  loop2->GetTaskRunner()->PostTask([&]() {
143  ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
144  ASSERT_FALSE(raster_thread_merger_->IsOnPlatformThread());
145  ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2);
146  post_unmerge.CountDown();
147  });
148 
149  post_unmerge.Wait();
150 
151  loop1->GetTaskRunner()->PostTask([&]() { loop1->Terminate(); });
152 
153  loop2->GetTaskRunner()->PostTask([&]() { loop2->Terminate(); });
154 
155  thread1.join();
156  thread2.join();
157 }
158 
159 TEST(RasterThreadMerger, LeaseExtension) {
160  fml::MessageLoop* loop1 = nullptr;
163  std::thread thread1([&loop1, &latch1, &term1]() {
165  loop1 = &fml::MessageLoop::GetCurrent();
166  latch1.Signal();
167  term1.Wait();
168  });
169 
170  fml::MessageLoop* loop2 = nullptr;
173  std::thread thread2([&loop2, &latch2, &term2]() {
175  loop2 = &fml::MessageLoop::GetCurrent();
176  latch2.Signal();
177  term2.Wait();
178  });
179 
180  latch1.Wait();
181  latch2.Wait();
182 
183  fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
184  fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
185  const auto raster_thread_merger_ =
186  fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);
187  const int kNumFramesMerged = 5;
188 
189  ASSERT_FALSE(raster_thread_merger_->IsMerged());
190 
191  raster_thread_merger_->MergeWithLease(kNumFramesMerged);
192 
193  // let there be one more turn till the leases expire.
194  for (int i = 0; i < kNumFramesMerged - 1; i++) {
195  ASSERT_TRUE(raster_thread_merger_->IsMerged());
196  raster_thread_merger_->DecrementLease();
197  }
198 
199  // extend the lease once.
200  raster_thread_merger_->ExtendLeaseTo(kNumFramesMerged);
201 
202  // we will NOT last for 1 extra turn, we just set it.
203  for (int i = 0; i < kNumFramesMerged; i++) {
204  ASSERT_TRUE(raster_thread_merger_->IsMerged());
205  raster_thread_merger_->DecrementLease();
206  }
207 
208  ASSERT_FALSE(raster_thread_merger_->IsMerged());
209 
210  term1.Signal();
211  term2.Signal();
212  thread1.join();
213  thread2.join();
214 }
215 
216 TEST(RasterThreadMerger, WaitUntilMerged) {
217  fml::RefPtr<fml::RasterThreadMerger> raster_thread_merger;
218 
219  fml::AutoResetWaitableEvent create_thread_merger_latch;
220  fml::MessageLoop* loop_platform = nullptr;
221  fml::AutoResetWaitableEvent latch_platform;
222  fml::AutoResetWaitableEvent term_platform;
223  fml::AutoResetWaitableEvent latch_merged;
224  std::thread thread_platform([&]() {
226  loop_platform = &fml::MessageLoop::GetCurrent();
227  latch_platform.Signal();
228  create_thread_merger_latch.Wait();
229  raster_thread_merger->WaitUntilMerged();
230  latch_merged.Signal();
231  term_platform.Wait();
232  });
233 
234  const int kNumFramesMerged = 5;
235  fml::MessageLoop* loop_raster = nullptr;
236  fml::AutoResetWaitableEvent term_raster;
237  std::thread thread_raster([&]() {
239  loop_raster = &fml::MessageLoop::GetCurrent();
240  latch_platform.Wait();
241  fml::TaskQueueId qid_platform =
242  loop_platform->GetTaskRunner()->GetTaskQueueId();
243  fml::TaskQueueId qid_raster =
244  loop_raster->GetTaskRunner()->GetTaskQueueId();
245  raster_thread_merger =
246  fml::MakeRefCounted<fml::RasterThreadMerger>(qid_platform, qid_raster);
247  ASSERT_FALSE(raster_thread_merger->IsMerged());
248  create_thread_merger_latch.Signal();
249  raster_thread_merger->MergeWithLease(kNumFramesMerged);
250  term_raster.Wait();
251  });
252 
253  latch_merged.Wait();
254  ASSERT_TRUE(raster_thread_merger->IsMerged());
255 
256  for (int i = 0; i < kNumFramesMerged; i++) {
257  ASSERT_TRUE(raster_thread_merger->IsMerged());
258  raster_thread_merger->DecrementLease();
259  }
260 
261  ASSERT_FALSE(raster_thread_merger->IsMerged());
262 
263  term_platform.Signal();
264  term_raster.Signal();
265  thread_platform.join();
266  thread_raster.join();
267 }
268 
269 TEST(RasterThreadMerger, HandleTaskQueuesAreTheSame) {
270  fml::MessageLoop* loop1 = nullptr;
273  std::thread thread1([&loop1, &latch1, &term1]() {
275  loop1 = &fml::MessageLoop::GetCurrent();
276  latch1.Signal();
277  term1.Wait();
278  });
279 
280  latch1.Wait();
281 
282  fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
283  fml::TaskQueueId qid2 = qid1;
284  const auto raster_thread_merger_ =
285  fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);
286  // Statically merged.
287  ASSERT_TRUE(raster_thread_merger_->IsMerged());
288 
289  // Test decrement lease and unmerge are both no-ops.
290  // The task queues should be always merged.
291  const int kNumFramesMerged = 5;
292  raster_thread_merger_->MergeWithLease(kNumFramesMerged);
293 
294  for (int i = 0; i < kNumFramesMerged; i++) {
295  ASSERT_TRUE(raster_thread_merger_->IsMerged());
296  raster_thread_merger_->DecrementLease();
297  }
298 
299  ASSERT_TRUE(raster_thread_merger_->IsMerged());
300 
301  // Wait until merged should also return immediately.
302  raster_thread_merger_->WaitUntilMerged();
303  ASSERT_TRUE(raster_thread_merger_->IsMerged());
304 
305  term1.Signal();
306  thread1.join();
307 }
308 
310  fml::MessageLoop* loop1 = nullptr;
313  std::thread thread1([&loop1, &latch1, &term1]() {
315  loop1 = &fml::MessageLoop::GetCurrent();
316  latch1.Signal();
317  term1.Wait();
318  });
319 
320  fml::MessageLoop* loop2 = nullptr;
323  std::thread thread2([&loop2, &latch2, &term2]() {
325  loop2 = &fml::MessageLoop::GetCurrent();
326  latch2.Signal();
327  term2.Wait();
328  });
329 
330  latch1.Wait();
331  latch2.Wait();
332 
333  fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
334  fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
335  const auto raster_thread_merger_ =
336  fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);
337 
338  raster_thread_merger_->Disable();
339  raster_thread_merger_->MergeWithLease(1);
340  ASSERT_FALSE(raster_thread_merger_->IsMerged());
341 
342  raster_thread_merger_->Enable();
343  ASSERT_FALSE(raster_thread_merger_->IsMerged());
344 
345  raster_thread_merger_->MergeWithLease(1);
346  ASSERT_TRUE(raster_thread_merger_->IsMerged());
347 
348  raster_thread_merger_->DecrementLease();
349  ASSERT_FALSE(raster_thread_merger_->IsMerged());
350 
351  term1.Signal();
352  term2.Signal();
353  thread1.join();
354  thread2.join();
355 }
356 
358  fml::MessageLoop* loop1 = nullptr;
361  std::thread thread1([&loop1, &latch1, &term1]() {
363  loop1 = &fml::MessageLoop::GetCurrent();
364  latch1.Signal();
365  term1.Wait();
366  });
367 
368  fml::MessageLoop* loop2 = nullptr;
371  std::thread thread2([&loop2, &latch2, &term2]() {
373  loop2 = &fml::MessageLoop::GetCurrent();
374  latch2.Signal();
375  term2.Wait();
376  });
377 
378  latch1.Wait();
379  latch2.Wait();
380 
381  fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
382  fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
383  const auto raster_thread_merger_ =
384  fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);
385 
386  raster_thread_merger_->Disable();
387  ASSERT_FALSE(raster_thread_merger_->IsMerged());
388 
389  raster_thread_merger_->MergeWithLease(1);
390  ASSERT_FALSE(raster_thread_merger_->IsMerged());
391 
392  raster_thread_merger_->Enable();
393  raster_thread_merger_->MergeWithLease(1);
394  ASSERT_TRUE(raster_thread_merger_->IsMerged());
395 
396  raster_thread_merger_->Disable();
397  raster_thread_merger_->UnMergeNow();
398  ASSERT_TRUE(raster_thread_merger_->IsMerged());
399 
400  {
401  auto decrement_result = raster_thread_merger_->DecrementLease();
402  ASSERT_EQ(fml::RasterThreadStatus::kRemainsMerged, decrement_result);
403  }
404 
405  ASSERT_TRUE(raster_thread_merger_->IsMerged());
406 
407  raster_thread_merger_->Enable();
408  raster_thread_merger_->UnMergeNow();
409  ASSERT_FALSE(raster_thread_merger_->IsMerged());
410 
411  raster_thread_merger_->MergeWithLease(1);
412 
413  ASSERT_TRUE(raster_thread_merger_->IsMerged());
414 
415  {
416  auto decrement_result = raster_thread_merger_->DecrementLease();
417  ASSERT_EQ(fml::RasterThreadStatus::kUnmergedNow, decrement_result);
418  }
419 
420  ASSERT_FALSE(raster_thread_merger_->IsMerged());
421 
422  term1.Signal();
423  term2.Signal();
424  thread1.join();
425  thread2.join();
426 }
427 
429  fml::MessageLoop* loop1 = nullptr;
432  std::thread thread1([&loop1, &latch1, &term1]() {
434  loop1 = &fml::MessageLoop::GetCurrent();
435  latch1.Signal();
436  term1.Wait();
437  });
438 
439  fml::MessageLoop* loop2 = nullptr;
442  std::thread thread2([&loop2, &latch2, &term2]() {
444  loop2 = &fml::MessageLoop::GetCurrent();
445  latch2.Signal();
446  term2.Wait();
447  });
448 
449  latch1.Wait();
450  latch2.Wait();
451 
452  fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
453  fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
454  const auto raster_thread_merger_ =
455  fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);
456  ASSERT_TRUE(raster_thread_merger_->IsEnabled());
457 
458  raster_thread_merger_->Disable();
459  ASSERT_FALSE(raster_thread_merger_->IsEnabled());
460 
461  raster_thread_merger_->Enable();
462  ASSERT_TRUE(raster_thread_merger_->IsEnabled());
463 
464  term1.Signal();
465  term2.Signal();
466  thread1.join();
467  thread2.join();
468 }
469 
470 TEST(RasterThreadMerger, RunExpiredTasksWhileFirstTaskMergesThreads) {
471  fml::MessageLoop* loop_platform = nullptr;
473  std::thread thread_platform([&loop_platform, &latch1]() {
475  loop_platform = &fml::MessageLoop::GetCurrent();
476  loop_platform->GetTaskRunner()->PostTask([&]() { latch1.Signal(); });
477  loop_platform->Run();
478  });
479 
480  fml::MessageLoop* loop_raster = nullptr;
482  std::thread thread_raster([&loop_raster, &loop_platform, &latch1, &latch2]() {
483  latch1.Wait();
484 
486  loop_raster = &fml::MessageLoop::GetCurrent();
487  fml::TaskQueueId qid_platform =
488  loop_platform->GetTaskRunner()->GetTaskQueueId();
489  fml::TaskQueueId qid_raster =
490  loop_raster->GetTaskRunner()->GetTaskQueueId();
491  fml::CountDownLatch post_merge(2);
492  const auto raster_thread_merger_ =
493  fml::MakeRefCounted<fml::RasterThreadMerger>(qid_platform, qid_raster);
494  loop_raster->GetTaskRunner()->PostTask([&]() {
495  ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
496  ASSERT_FALSE(raster_thread_merger_->IsOnPlatformThread());
497  ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid_raster);
498  raster_thread_merger_->MergeWithLease(1);
499  post_merge.CountDown();
500  });
501 
502  loop_raster->GetTaskRunner()->PostTask([&]() {
503  ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
504  ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
505  ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid_platform);
506  raster_thread_merger_->DecrementLease();
507  post_merge.CountDown();
508  });
509 
510  loop_raster->RunExpiredTasksNow();
511  post_merge.Wait();
512  latch2.Signal();
513  });
514 
515  latch2.Wait();
516  loop_platform->GetTaskRunner()->PostTask(
517  [&]() { loop_platform->Terminate(); });
518 
519  thread_platform.join();
520  thread_raster.join();
521 }
522 
523 TEST(RasterThreadMerger, RunExpiredTasksWhileFirstTaskUnMergesThreads) {
524  fml::MessageLoop* loop_platform = nullptr;
526  std::thread thread_platform([&loop_platform, &latch1]() {
528  loop_platform = &fml::MessageLoop::GetCurrent();
529  loop_platform->GetTaskRunner()->PostTask([&]() { latch1.Signal(); });
530  loop_platform->Run();
531  });
532 
533  fml::MessageLoop* loop_raster = nullptr;
535  std::thread thread_raster([&loop_raster, &loop_platform, &latch1, &latch2]() {
536  latch1.Wait();
537 
539  loop_raster = &fml::MessageLoop::GetCurrent();
540  fml::TaskQueueId qid_platform =
541  loop_platform->GetTaskRunner()->GetTaskQueueId();
542  fml::TaskQueueId qid_raster =
543  loop_raster->GetTaskRunner()->GetTaskQueueId();
544  fml::CountDownLatch post_merge(2);
545 
546  const auto raster_thread_merger_ =
547  fml::MakeRefCounted<fml::RasterThreadMerger>(qid_platform, qid_raster);
548  loop_raster->GetTaskRunner()->PostTask([&]() {
549  raster_thread_merger_->MergeWithLease(1);
550  post_merge.CountDown();
551  });
552 
553  loop_raster->RunExpiredTasksNow();
554 
555  loop_raster->GetTaskRunner()->PostTask([&]() {
556  ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
557  ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
558  ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid_platform);
559  raster_thread_merger_->DecrementLease();
560  post_merge.CountDown();
561  });
562 
563  loop_raster->GetTaskRunner()->PostTask([&]() {
564  ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
565  ASSERT_FALSE(raster_thread_merger_->IsOnPlatformThread());
566  ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid_platform);
567  post_merge.CountDown();
568  });
569 
570  loop_raster->RunExpiredTasksNow();
571  post_merge.Wait();
572  latch2.Signal();
573  });
574 
575  latch2.Wait();
576  loop_platform->GetTaskRunner()->PostTask(
577  [&]() { loop_platform->Terminate(); });
578 
579  thread_platform.join();
580  thread_raster.join();
581 }
582 
583 TEST(RasterThreadMerger, SetMergeUnmergeCallback) {
584  fml::MessageLoop* loop1 = nullptr;
587  std::thread thread1([&loop1, &latch1, &term1]() {
589  loop1 = &fml::MessageLoop::GetCurrent();
590  latch1.Signal();
591  term1.Wait();
592  });
593 
594  fml::MessageLoop* loop2 = nullptr;
597  std::thread thread2([&loop2, &latch2, &term2]() {
599  loop2 = &fml::MessageLoop::GetCurrent();
600  latch2.Signal();
601  term2.Wait();
602  });
603 
604  latch1.Wait();
605  latch2.Wait();
606 
607  fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
608  fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
609 
610  const auto raster_thread_merger =
611  fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);
612 
613  int callbacks = 0;
614  raster_thread_merger->SetMergeUnmergeCallback(
615  [&callbacks]() { callbacks++; });
616 
617  ASSERT_EQ(0, callbacks);
618 
619  raster_thread_merger->MergeWithLease(1);
620  ASSERT_EQ(1, callbacks);
621 
622  raster_thread_merger->DecrementLease();
623  ASSERT_EQ(2, callbacks);
624 
625  term1.Signal();
626  term2.Signal();
627  thread1.join();
628  thread2.join();
629 }
630 
631 } // namespace testing
632 } // namespace fml
virtual TaskQueueId GetTaskQueueId()
Definition: task_runner.cc:38
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
Definition: message_loop.cc:19
virtual void PostTask(const fml::closure &task)
Definition: task_runner.cc:24
static void EnsureInitializedForCurrentThread()
Definition: message_loop.cc:27
Definition: ascii_trie.cc:9
void MergeWithLease(size_t lease_term)
fml::RefPtr< fml::TaskRunner > GetTaskRunner() const
Definition: message_loop.cc:56
static TaskQueueId GetCurrentTaskQueueId()
Definition: message_loop.cc:76
TEST(BacktraceTest, CanGatherBacktrace)
RasterThreadStatus DecrementLease()