diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 9fc52d4d55..b5398514d2 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -1359,34 +1359,37 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS - (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); }); }