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 <atomic>
8#include <thread>
9
10#include "flutter/fml/message_loop.h"
11#include "flutter/fml/raster_thread_merger.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
17TEST(RasterThreadMerger, RemainMergedTillLeaseExpires) {
18 fml::MessageLoop* loop1 = nullptr;
19 fml::AutoResetWaitableEvent latch1;
20 fml::AutoResetWaitableEvent term1;
21 std::thread thread1([&loop1, &latch1, &term1]() {
22 fml::MessageLoop::EnsureInitializedForCurrentThread();
23 loop1 = &fml::MessageLoop::GetCurrent();
24 latch1.Signal();
25 term1.Wait();
26 });
27
28 fml::MessageLoop* loop2 = nullptr;
29 fml::AutoResetWaitableEvent latch2;
30 fml::AutoResetWaitableEvent term2;
31 std::thread thread2([&loop2, &latch2, &term2]() {
32 fml::MessageLoop::EnsureInitializedForCurrentThread();
33 loop2 = &fml::MessageLoop::GetCurrent();
34 latch2.Signal();
35 term2.Wait();
36 });
37
38 latch1.Wait();
39 latch2.Wait();
40
41 fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
42 fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
43 const auto raster_thread_merger_ =
44 fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);
45 const int kNumFramesMerged = 5;
46
47 ASSERT_FALSE(raster_thread_merger_->IsMerged());
48
49 raster_thread_merger_->MergeWithLease(kNumFramesMerged);
50
51 for (int i = 0; i < kNumFramesMerged; i++) {
52 ASSERT_TRUE(raster_thread_merger_->IsMerged());
53 raster_thread_merger_->DecrementLease();
54 }
55
56 ASSERT_FALSE(raster_thread_merger_->IsMerged());
57
58 term1.Signal();
59 term2.Signal();
60 thread1.join();
61 thread2.join();
62}
63
64TEST(RasterThreadMerger, IsNotOnRasterizingThread) {
65 fml::MessageLoop* loop1 = nullptr;
66 fml::AutoResetWaitableEvent latch1;
67 std::thread thread1([&loop1, &latch1]() {
68 fml::MessageLoop::EnsureInitializedForCurrentThread();
69 loop1 = &fml::MessageLoop::GetCurrent();
70 loop1->GetTaskRunner()->PostTask([&]() { latch1.Signal(); });
71 loop1->Run();
72 });
73
74 fml::MessageLoop* loop2 = nullptr;
75 fml::AutoResetWaitableEvent latch2;
76 std::thread thread2([&loop2, &latch2]() {
77 fml::MessageLoop::EnsureInitializedForCurrentThread();
78 loop2 = &fml::MessageLoop::GetCurrent();
79 loop2->GetTaskRunner()->PostTask([&]() { latch2.Signal(); });
80 loop2->Run();
81 });
82
83 latch1.Wait();
84 latch2.Wait();
85
86 fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
87 fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
88 const auto raster_thread_merger_ =
89 fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);
90
91 fml::CountDownLatch pre_merge(2), post_merge(2), post_unmerge(2);
92
93 loop1->GetTaskRunner()->PostTask([&]() {
94 ASSERT_FALSE(raster_thread_merger_->IsOnRasterizingThread());
95 ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
96 ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
97 pre_merge.CountDown();
98 });
99
100 loop2->GetTaskRunner()->PostTask([&]() {
101 ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
102 ASSERT_FALSE(raster_thread_merger_->IsOnPlatformThread());
103 ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2);
104 pre_merge.CountDown();
105 });
106
107 pre_merge.Wait();
108
109 raster_thread_merger_->MergeWithLease(1);
110
111 loop1->GetTaskRunner()->PostTask([&]() {
112 ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
113 ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
114 ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
115 post_merge.CountDown();
116 });
117
118 loop2->GetTaskRunner()->PostTask([&]() {
119 // this will be false since this is going to be run
120 // on loop1 really.
121 ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
122 ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
123 ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
124 post_merge.CountDown();
125 });
126
127 post_merge.Wait();
128
129 raster_thread_merger_->DecrementLease();
130
131 loop1->GetTaskRunner()->PostTask([&]() {
132 ASSERT_FALSE(raster_thread_merger_->IsOnRasterizingThread());
133 ASSERT_TRUE(raster_thread_merger_->IsOnPlatformThread());
134 ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid1);
135 post_unmerge.CountDown();
136 });
137
138 loop2->GetTaskRunner()->PostTask([&]() {
139 ASSERT_TRUE(raster_thread_merger_->IsOnRasterizingThread());
140 ASSERT_FALSE(raster_thread_merger_->IsOnPlatformThread());
141 ASSERT_EQ(fml::MessageLoop::GetCurrentTaskQueueId(), qid2);
142 post_unmerge.CountDown();
143 });
144
145 post_unmerge.Wait();
146
147 loop1->GetTaskRunner()->PostTask([&]() { loop1->Terminate(); });
148
149 loop2->GetTaskRunner()->PostTask([&]() { loop2->Terminate(); });
150
151 thread1.join();
152 thread2.join();
153}
154
155TEST(RasterThreadMerger, LeaseExtension) {
156 fml::MessageLoop* loop1 = nullptr;
157 fml::AutoResetWaitableEvent latch1;
158 fml::AutoResetWaitableEvent term1;
159 std::thread thread1([&loop1, &latch1, &term1]() {
160 fml::MessageLoop::EnsureInitializedForCurrentThread();
161 loop1 = &fml::MessageLoop::GetCurrent();
162 latch1.Signal();
163 term1.Wait();
164 });
165
166 fml::MessageLoop* loop2 = nullptr;
167 fml::AutoResetWaitableEvent latch2;
168 fml::AutoResetWaitableEvent term2;
169 std::thread thread2([&loop2, &latch2, &term2]() {
170 fml::MessageLoop::EnsureInitializedForCurrentThread();
171 loop2 = &fml::MessageLoop::GetCurrent();
172 latch2.Signal();
173 term2.Wait();
174 });
175
176 latch1.Wait();
177 latch2.Wait();
178
179 fml::TaskQueueId qid1 = loop1->GetTaskRunner()->GetTaskQueueId();
180 fml::TaskQueueId qid2 = loop2->GetTaskRunner()->GetTaskQueueId();
181 const auto raster_thread_merger_ =
182 fml::MakeRefCounted<fml::RasterThreadMerger>(qid1, qid2);
183 const int kNumFramesMerged = 5;
184
185 ASSERT_FALSE(raster_thread_merger_->IsMerged());
186
187 raster_thread_merger_->MergeWithLease(kNumFramesMerged);
188
189 // let there be one more turn till the leases expire.
190 for (int i = 0; i < kNumFramesMerged - 1; i++) {
191 ASSERT_TRUE(raster_thread_merger_->IsMerged());
192 raster_thread_merger_->DecrementLease();
193 }
194
195 // extend the lease once.
196 raster_thread_merger_->ExtendLeaseTo(kNumFramesMerged);
197
198 // we will NOT last for 1 extra turn, we just set it.
199 for (int i = 0; i < kNumFramesMerged; i++) {
200 ASSERT_TRUE(raster_thread_merger_->IsMerged());
201 raster_thread_merger_->DecrementLease();
202 }
203
204 ASSERT_FALSE(raster_thread_merger_->IsMerged());
205
206 term1.Signal();
207 term2.Signal();
208 thread1.join();
209 thread2.join();
210}
211