| Flutter macOS Embedder |

FlutterThreadSynchronizerTest.mm
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 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h"
6
7 #import "flutter/fml/synchronization/waitable_event.h"
8 #import "flutter/testing/testing.h"
9
10 namespace flutter::testing {
11
12 namespace {} // namespace
13
14 } // namespace flutter::testing
15
16 @interface FlutterThreadSynchronizerTestScaffold : NSObject
17
18 @property(nonatomic, readonly, nonnull) FlutterThreadSynchronizer* synchronizer;
19
20 - (nullable instancetype)init;
21 - (void)dispatchMainTask:(nonnull void (^)())task;
22 - (void)dispatchRenderTask:(nonnull void (^)())task;
23 - (void)joinMain;
24 - (void)joinRender;
25 @end
26
27 @implementation FlutterThreadSynchronizerTestScaffold {
28 dispatch_queue_t _mainQueue;
29 std::shared_ptr<fml::AutoResetWaitableEvent> _mainLatch;
30
31 dispatch_queue_t _renderQueue;
32 std::shared_ptr<fml::AutoResetWaitableEvent> _renderLatch;
33
34 FlutterThreadSynchronizer* _synchronizer;
35 }
36
37 @synthesize synchronizer = _synchronizer;
38
39 - (nullable instancetype)init {
40 self = [super init];
41 if (self != nil) {
42 _mainQueue = dispatch_queue_create("MAIN", DISPATCH_QUEUE_SERIAL);
43 _renderQueue = dispatch_queue_create("RENDER", DISPATCH_QUEUE_SERIAL);
44 _synchronizer = [[FlutterThreadSynchronizer alloc] initWithMainQueue:_mainQueue];
45 }
46 return self;
47 }
48
49 - (void)dispatchMainTask:(nonnull void (^)())task {
50 dispatch_async(_mainQueue, task);
51 }
52
53 - (void)dispatchRenderTask:(nonnull void (^)())task {
54 dispatch_async(_renderQueue, task);
55 }
56
57 - (void)joinMain {
58 fml::AutoResetWaitableEvent latch;
59 fml::AutoResetWaitableEvent* pLatch = &latch;
60 dispatch_async(_mainQueue, ^{
61 pLatch->Signal();
62 });
63 latch.Wait();
64 }
65
66 - (void)joinRender {
67 fml::AutoResetWaitableEvent latch;
68 fml::AutoResetWaitableEvent* pLatch = &latch;
69 dispatch_async(_renderQueue, ^{
70 pLatch->Signal();
71 });
72 latch.Wait();
73 }
74
75 @end
76
77 TEST(FlutterThreadSynchronizerTest, RegularCommit) {
78 FlutterThreadSynchronizerTestScaffold* scaffold =
79 [[FlutterThreadSynchronizerTestScaffold alloc] init];
80 FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer;
81
82 // Initial resize: does not block until the first frame.
83 __block int notifiedResize = 0;
84 [scaffold dispatchMainTask:^{
85 [synchronizer registerView:1];
86 [synchronizer beginResizeForView:1
87 size:CGSize{5, 5}
88 notify:^{
89 notifiedResize += 1;
90 }];
91 }];
92 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
93 [scaffold joinMain];
94 EXPECT_EQ(notifiedResize, 1);
95
96 // Still does not block.
97 [scaffold dispatchMainTask:^{
98 [synchronizer beginResizeForView:1
99 size:CGSize{7, 7}
100 notify:^{
101 notifiedResize += 1;
102 }];
103 }];
104 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
105 [scaffold joinMain];
106 EXPECT_EQ(notifiedResize, 2);
107
108 // First frame
109 __block int notifiedCommit = 0;
110 [scaffold dispatchRenderTask:^{
111 [synchronizer performCommitForView:1
112 size:CGSize{7, 7}
113 notify:^{
114 notifiedCommit += 1;
115 }];
116 }];
117 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
118 [scaffold joinRender];
119 EXPECT_EQ(notifiedCommit, 1);
120 }
121
122 TEST(FlutterThreadSynchronizerTest, ResizingBlocksRenderingUntilSizeMatches) {
123 FlutterThreadSynchronizerTestScaffold* scaffold =
124 [[FlutterThreadSynchronizerTestScaffold alloc] init];
125 FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer;
126 // A latch to ensure that a beginResizeForView: call has at least executed
127 // something, so that the isWaitingWhenMutexIsAvailable: call correctly stops
128 // at either when beginResizeForView: finishes or waits half way.
129 fml::AutoResetWaitableEvent begunResizingLatch;
130 fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch;
131
132 // Initial resize: does not block until the first frame.
133 [scaffold dispatchMainTask:^{
134 [synchronizer registerView:1];
135 [synchronizer beginResizeForView:1
136 size:CGSize{5, 5}
137 notify:^{
138 }];
139 }];
140 [scaffold joinMain];
141 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
142
143 // First frame.
144 [scaffold dispatchRenderTask:^{
145 [synchronizer performCommitForView:1
146 size:CGSize{5, 5}
147 notify:^{
148 }];
149 }];
150 [scaffold joinRender];
151 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
152
153 // Resize to (7, 7): blocks until the next frame.
154 [scaffold dispatchMainTask:^{
155 [synchronizer beginResizeForView:1
156 size:CGSize{7, 7}
157 notify:^{
158 begunResizing->Signal();
159 }];
160 }];
161 begunResizing->Wait();
162 EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]);
163
164 // Render with old size.
165 [scaffold dispatchRenderTask:^{
166 [synchronizer performCommitForView:1
167 size:CGSize{5, 5}
168 notify:^{
169 }];
170 }];
171 [scaffold joinRender];
172 EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]);
173
174 // Render with new size.
175 [scaffold dispatchRenderTask:^{
176 [synchronizer performCommitForView:1
177 size:CGSize{7, 7}
178 notify:^{
179 }];
180 }];
181 [scaffold joinRender];
182 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
183
184 [scaffold joinMain];
185 }
186
187 TEST(FlutterThreadSynchronizerTest, ShutdownMakesEverythingNonBlocking) {
188 FlutterThreadSynchronizerTestScaffold* scaffold =
189 [[FlutterThreadSynchronizerTestScaffold alloc] init];
190 FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer;
191 fml::AutoResetWaitableEvent begunResizingLatch;
192 fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch;
193
194 // Initial resize
195 [scaffold dispatchMainTask:^{
196 [synchronizer registerView:1];
197 [synchronizer beginResizeForView:1
198 size:CGSize{5, 5}
199 notify:^{
200 }];
201 }];
202 [scaffold joinMain];
203 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
204
205 // Push a frame.
206 [scaffold dispatchRenderTask:^{
207 [synchronizer performCommitForView:1
208 size:CGSize{5, 5}
209 notify:^{
210 }];
211 }];
212 [scaffold joinRender];
213 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
214
215 [scaffold dispatchMainTask:^{
216 [synchronizer shutdown];
217 }];
218
219 // Resize to (7, 7). Should not block any frames since it has shut down.
220 [scaffold dispatchMainTask:^{
221 [synchronizer beginResizeForView:1
222 size:CGSize{7, 7}
223 notify:^{
224 begunResizing->Signal();
225 }];
226 }];
227 begunResizing->Wait();
228 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
229 [scaffold joinMain];
230
231 // All further calls should be unblocking.
232 [scaffold dispatchRenderTask:^{
233 [synchronizer performCommitForView:1
234 size:CGSize{9, 9}
235 notify:^{
236 }];
237 }];
238 [scaffold joinRender];
239 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
240 }
241
242 TEST(FlutterThreadSynchronizerTest, RegularCommitForMultipleViews) {
243 FlutterThreadSynchronizerTestScaffold* scaffold =
244 [[FlutterThreadSynchronizerTestScaffold alloc] init];
245 FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer;
246 fml::AutoResetWaitableEvent begunResizingLatch;
247 fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch;
248
249 // Initial resize: does not block until the first frame.
250 [scaffold dispatchMainTask:^{
251 [synchronizer registerView:1];
252 [synchronizer registerView:2];
253 [synchronizer beginResizeForView:1
254 size:CGSize{5, 5}
255 notify:^{
256 }];
257 [synchronizer beginResizeForView:2
258 size:CGSize{15, 15}
259 notify:^{
260 begunResizing->Signal();
261 }];
262 }];
263 begunResizing->Wait();
264 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
265 [scaffold joinMain];
266
267 // Still does not block.
268 [scaffold dispatchMainTask:^{
269 [synchronizer beginResizeForView:1
270 size:CGSize{7, 7}
271 notify:^{
272 begunResizing->Signal();
273 }];
274 }];
275 begunResizing->Signal();
276 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
277 [scaffold joinMain];
278
279 // First frame
280 [scaffold dispatchRenderTask:^{
281 [synchronizer performCommitForView:1
282 size:CGSize{7, 7}
283 notify:^{
284 }];
285 [synchronizer performCommitForView:2
286 size:CGSize{15, 15}
287 notify:^{
288 }];
289 }];
290 [scaffold joinRender];
291 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
292 }
293
294 TEST(FlutterThreadSynchronizerTest, ResizingForMultipleViews) {
295 FlutterThreadSynchronizerTestScaffold* scaffold =
296 [[FlutterThreadSynchronizerTestScaffold alloc] init];
297 FlutterThreadSynchronizer* synchronizer = scaffold.synchronizer;
298 fml::AutoResetWaitableEvent begunResizingLatch;
299 fml::AutoResetWaitableEvent* begunResizing = &begunResizingLatch;
300
301 // Initial resize: does not block until the first frame.
302 [scaffold dispatchMainTask:^{
303 [synchronizer registerView:1];
304 [synchronizer registerView:2];
305 [synchronizer beginResizeForView:1
306 size:CGSize{5, 5}
307 notify:^{
308 }];
309 [synchronizer beginResizeForView:2
310 size:CGSize{15, 15}
311 notify:^{
312 }];
313 }];
314 [scaffold joinMain];
315 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
316
317 // First frame.
318 [scaffold dispatchRenderTask:^{
319 [synchronizer performCommitForView:1
320 size:CGSize{5, 5}
321 notify:^{
322 }];
323 [synchronizer performCommitForView:2
324 size:CGSize{15, 15}
325 notify:^{
326 }];
327 }];
328 [scaffold joinRender];
329 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
330
331 // Resize view 2 to (17, 17): blocks until the next frame.
332 [scaffold dispatchMainTask:^{
333 [synchronizer beginResizeForView:2
334 size:CGSize{17, 17}
335 notify:^{
336 begunResizing->Signal();
337 }];
338 }];
339 begunResizing->Wait();
340 EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]);
341
342 // Render view 1 with the size. Still blocking.
343 [scaffold dispatchRenderTask:^{
344 [synchronizer performCommitForView:1
345 size:CGSize{5, 5}
346 notify:^{
347 }];
348 }];
349 [scaffold joinRender];
350 EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]);
351
352 // Render view 2 with the old size. Still blocking.
353 [scaffold dispatchRenderTask:^{
354 [synchronizer performCommitForView:1
355 size:CGSize{15, 15}
356 notify:^{
357 }];
358 }];
359 [scaffold joinRender];
360 EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]);
361
362 // Render view 1 with the size.
363 [scaffold dispatchRenderTask:^{
364 [synchronizer performCommitForView:1
365 size:CGSize{5, 5}
366 notify:^{
367 }];
368 }];
369 [scaffold joinRender];
370 EXPECT_TRUE([synchronizer isWaitingWhenMutexIsAvailable]);
371
372 // Render view 2 with the new size. Unblocks.
373 [scaffold dispatchRenderTask:^{
374 [synchronizer performCommitForView:2
375 size:CGSize{17, 17}
376 notify:^{
377 }];
378 }];
379 [scaffold joinRender];
380 [scaffold joinMain];
381 EXPECT_FALSE([synchronizer isWaitingWhenMutexIsAvailable]);
382 }
-[FlutterThreadSynchronizerTestScaffold init]
nullable instancetype init()
Definition: FlutterThreadSynchronizerTest.mm:39
-[FlutterThreadSynchronizer performCommitForView:size:notify:]
void performCommitForView:size:notify:(int64_t viewId,[size] CGSize size,[notify] nonnull dispatch_block_t notify)
Definition: FlutterThreadSynchronizer.mm:137
FlutterThreadSynchronizerTestScaffold::synchronizer
FlutterThreadSynchronizer * synchronizer
Definition: FlutterThreadSynchronizerTest.mm:18
-[FlutterThreadSynchronizerTestScaffold joinMain]
void joinMain()
Definition: FlutterThreadSynchronizerTest.mm:57
flutter::testing
Definition: AccessibilityBridgeMacTest.mm:13
_mainLatch
std::shared_ptr< fml::AutoResetWaitableEvent > _mainLatch
Definition: FlutterThreadSynchronizerTest.mm:27
-[FlutterThreadSynchronizerTestScaffold dispatchRenderTask:]
void dispatchRenderTask:(nonnull void(^ task)())
Definition: FlutterThreadSynchronizerTest.mm:53
FlutterThreadSynchronizer
Definition: FlutterThreadSynchronizer.h:13
-[FlutterThreadSynchronizer registerView:]
void registerView:(int64_t viewId)
Definition: FlutterThreadSynchronizer.mm:167
_synchronizer
FlutterThreadSynchronizer * _synchronizer
Definition: FlutterThreadSynchronizerTest.mm:34
TEST
TEST(FlutterThreadSynchronizerTest, RegularCommit)
Definition: FlutterThreadSynchronizerTest.mm:77
-[FlutterThreadSynchronizer beginResizeForView:size:notify:]
void beginResizeForView:size:notify:(int64_t viewId,[size] CGSize size,[notify] nonnull dispatch_block_t notify)
Definition: FlutterThreadSynchronizer.mm:102
_renderLatch
std::shared_ptr< fml::AutoResetWaitableEvent > _renderLatch
Definition: FlutterThreadSynchronizerTest.mm:32
FlutterThreadSynchronizer.h
_renderQueue
dispatch_queue_t _renderQueue
Definition: FlutterThreadSynchronizerTest.mm:31
-[FlutterThreadSynchronizerTestScaffold joinRender]
void joinRender()
Definition: FlutterThreadSynchronizerTest.mm:66
-[FlutterThreadSynchronizer shutdown]
void shutdown()
Definition: FlutterThreadSynchronizer.mm:179
-[FlutterThreadSynchronizerTestScaffold dispatchMainTask:]
void dispatchMainTask:(nonnull void(^ task)())
Definition: FlutterThreadSynchronizerTest.mm:49
FlutterThreadSynchronizerTestScaffold
Definition: FlutterThreadSynchronizerTest.mm:16