From b85a5408f19a56d07b83d01ee7f2936d1ce9a5c9 Mon Sep 17 00:00:00 2001 From: Matej Knopp Date: Fri, 30 Jul 2021 22:04:31 +0200 Subject: [PATCH] Fix race condition in FlutterSurfaceManager There is a small window in which the onIdle timer might not be cancelled so it needs to be guarded with a synchronized variable. --- .../framework/Source/FlutterSurfaceManager.mm | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm index ca703306feff2..054aaa22ff618 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterSurfaceManager.mm @@ -27,6 +27,7 @@ @implementation FlutterIOSurfaceManager { CGSize _surfaceSize; FlutterIOSurfaceHolder* _ioSurfaces[kFlutterSurfaceManagerBufferCount]; + BOOL _frameInProgress; } - (instancetype)initWithLayer:(CALayer*)containingLayer contentTransform:(CATransform3D)transform { @@ -66,23 +67,33 @@ - (void)swapBuffers { _ioSurfaces[kFlutterSurfaceManagerFrontBuffer]); [_delegate onSwapBuffers]; - dispatch_async(dispatch_get_main_queue(), ^{ - [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(onIdle) object:nil]; - [self performSelector:@selector(onIdle) withObject:nil afterDelay:kIdleDelay]; - }); + // performSelector:withObject:afterDelay needs to be performed on RunLoop thread + [self performSelectorOnMainThread:@selector(reschedule) withObject:nil waitUntilDone:NO]; + + @synchronized(self) { + _frameInProgress = NO; + } +} + +- (void)reschedule { + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(onIdle) object:nil]; + [self performSelector:@selector(onIdle) withObject:nil afterDelay:kIdleDelay]; } - (void)onIdle { @synchronized(self) { - // Release the back buffer and notify delegate. The buffer will be restored - // on demand in ensureBackBuffer - _ioSurfaces[kFlutterSurfaceManagerBackBuffer] = nil; - [self.delegate onSurfaceReleased:kFlutterSurfaceManagerBackBuffer]; + if (!_frameInProgress) { + // Release the back buffer and notify delegate. The buffer will be restored + // on demand in ensureBackBuffer + _ioSurfaces[kFlutterSurfaceManagerBackBuffer] = nil; + [self.delegate onSurfaceReleased:kFlutterSurfaceManagerBackBuffer]; + } } } - (void)ensureBackBuffer { @synchronized(self) { + _frameInProgress = YES; if (_ioSurfaces[kFlutterSurfaceManagerBackBuffer] == nil) { // Restore previously released backbuffer _ioSurfaces[kFlutterSurfaceManagerBackBuffer] = [[FlutterIOSurfaceHolder alloc] init];