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 
10 namespace flutter::testing {
11 
12 namespace {} // namespace
13 
14 } // namespace flutter::testing
15 
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 
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 
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) {
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) {
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) {
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) {
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) {
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