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 
6 
7 #import "flutter/fml/synchronization/waitable_event.h"
8 #import "flutter/testing/testing.h"
9 
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 
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 
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) {
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) {
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) {
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) {
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) {
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 }
-[FlutterThreadSynchronizerTestScaffold init]
nullable instancetype init()
Definition: FlutterThreadSynchronizerTest.mm:31
FlutterThreadSynchronizerTestScaffold::synchronizer
FlutterThreadSynchronizer * synchronizer
Definition: FlutterThreadSynchronizerTest.mm:12
-[FlutterThreadSynchronizerTestScaffold joinMain]
void joinMain()
Definition: FlutterThreadSynchronizerTest.mm:49
-[FlutterThreadSynchronizer registerView:]
void registerView:(FlutterViewIdentifier viewIdentifier)
Definition: FlutterThreadSynchronizer.mm:180
_mainLatch
std::shared_ptr< fml::AutoResetWaitableEvent > _mainLatch
Definition: FlutterThreadSynchronizerTest.mm:21
-[FlutterThreadSynchronizerTestScaffold dispatchRenderTask:]
void dispatchRenderTask:(nonnull void(^ task)())
Definition: FlutterThreadSynchronizerTest.mm:45
FlutterThreadSynchronizer
Definition: FlutterThreadSynchronizer.h:18
_synchronizer
FlutterThreadSynchronizer * _synchronizer
Definition: FlutterThreadSynchronizerTest.mm:28
TEST
TEST(FlutterThreadSynchronizerTest, RegularCommit)
Definition: FlutterThreadSynchronizerTest.mm:69
-[FlutterThreadSynchronizer performCommitForView:size:notify:]
void performCommitForView:size:notify:(FlutterViewIdentifier viewIdentifier,[size] CGSize size,[notify] nonnull dispatch_block_t notify)
Definition: FlutterThreadSynchronizer.mm:137
-[FlutterThreadSynchronizer beginResizeForView:size:notify:]
void beginResizeForView:size:notify:(FlutterViewIdentifier viewIdentifier,[size] CGSize size,[notify] nonnull dispatch_block_t notify)
Definition: FlutterThreadSynchronizer.mm:102
_renderLatch
std::shared_ptr< fml::AutoResetWaitableEvent > _renderLatch
Definition: FlutterThreadSynchronizerTest.mm:26
FlutterThreadSynchronizer.h
_renderQueue
dispatch_queue_t _renderQueue
Definition: FlutterThreadSynchronizerTest.mm:25
-[FlutterThreadSynchronizerTestScaffold joinRender]
void joinRender()
Definition: FlutterThreadSynchronizerTest.mm:58
-[FlutterThreadSynchronizer shutdown]
void shutdown()
Definition: FlutterThreadSynchronizer.mm:192
-[FlutterThreadSynchronizerTestScaffold dispatchMainTask:]
void dispatchMainTask:(nonnull void(^ task)())
Definition: FlutterThreadSynchronizerTest.mm:41
FlutterThreadSynchronizerTestScaffold
Definition: FlutterThreadSynchronizerTest.mm:10