Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 23 additions & 20 deletions shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1359,34 +1359,37 @@ - (void)onLocaleUpdated:(NSNotification*)notification {
- (void)waitForFirstFrame:(NSTimeInterval)timeout
callback:(void (^_Nonnull)(BOOL didTimeout))callback {
dispatch_queue_t queue = dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0);
dispatch_group_t group = dispatch_group_create();

__weak FlutterEngine* weakSelf = self;
dispatch_async(queue, ^{
__block BOOL didTimeout = NO;
dispatch_group_async(group, queue, ^{
FlutterEngine* strongSelf = weakSelf;
if (!strongSelf) {
return;
}

fml::TimeDelta waitTime = fml::TimeDelta::FromMilliseconds(timeout * 1000);
BOOL didTimeout =
didTimeout =
strongSelf.shell.WaitForFirstFrame(waitTime).code() == fml::StatusCode::kDeadlineExceeded;
dispatch_async(dispatch_get_main_queue(), ^{
// Capture strongSelf to ensure that destruction does not occur on a background thread.
//
// The containing block, executed on a background thread, strongly captures self, then makes a
// blocking call to self.shell.WaitForFirstFrame(). If, during this time, all other instances
// of self are released, the containing block's reference would be the last one, resulting in
// `[FlutterEngine dealloc]` being called when it goes out of scope at the end of that block,
// on a background thread. FlutterEngine owns a reference to a PlatformViewsController, which
// owns a WeakPtrFactory whose destructor asserts that it be freed on the platform thread. To
// avoid this, we capture strongSelf in the current block, which is executed on the platform
// thread.
//
// strongSelf is never nil here since it's a strong reference that's verified non-nil above,
// but we use a conditional check to avoid and unused expression compiler warning.
if (strongSelf) {
callback(didTimeout);
}
});
});

// Only execute the main queue task once the background task has completely finished executing.
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// Strongly capture self on the task dispatched to the main thread.
//
// When we capture weakSelf strongly in the above block on a background thread, we risk the
// possibility that all other strong references to FlutterEngine go out of scope while the block
// executes and that the engine is dealloc'ed at the end of the above block on a background
// thread. FlutterEngine is not safe to release on any thread other than the main thread.
//
// self is never nil here since it's a strong reference that's verified non-nil above, but we
// use a conditional check to avoid an unused expression compiler warning.
FlutterEngine* strongSelf = self;
if (!strongSelf) {
return;
}
callback(didTimeout);
});
}

Expand Down