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