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