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 <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 
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 
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 
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) {
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) {
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) {
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) {
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) {
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