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 | |
17 | TEST(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 | |
64 | TEST(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 | |
155 | TEST(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 | |