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