Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -2744,6 +2744,9 @@ ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTex
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObjectTest.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextureRegistrar.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextureRegistrar.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterUmbrellaImportTests.m + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.mm + ../../../flutter/LICENSE
Expand Down Expand Up @@ -5410,6 +5413,7 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextu
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextureRegistrar.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizer.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterThreadSynchronizerTest.mm
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterUmbrellaImportTests.m
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h
FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterView.mm
Expand Down
1 change: 1 addition & 0 deletions shell/platform/darwin/macos/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ executable("flutter_desktop_darwin_unittests") {
"framework/Source/FlutterSurfaceManagerTest.mm",
"framework/Source/FlutterTextInputPluginTest.mm",
"framework/Source/FlutterTextInputSemanticsObjectTest.mm",
"framework/Source/FlutterThreadSynchronizerTest.mm",
"framework/Source/FlutterViewControllerTest.mm",
"framework/Source/FlutterViewControllerTestUtils.h",
"framework/Source/FlutterViewControllerTestUtils.mm",
Expand Down
16 changes: 10 additions & 6 deletions shell/platform/darwin/macos/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,8 @@ @implementation FlutterEngine {
// A method channel for miscellaneous platform functionality.
FlutterMethodChannel* _platformChannel;

FlutterThreadSynchronizer* _threadSynchronizer;

int _nextViewId;
}

Expand Down Expand Up @@ -427,6 +429,7 @@ - (instancetype)initWithName:(NSString*)labelPrefix
object:nil];

_platformViewController = [[FlutterPlatformViewController alloc] init];
_threadSynchronizer = [[FlutterThreadSynchronizer alloc] init];
[self setUpPlatformViewChannel];
[self setUpAccessibilityChannel];
[self setUpNotificationCenterListeners];
Expand Down Expand Up @@ -589,7 +592,7 @@ - (void)registerViewController:(FlutterViewController*)controller forId:(Flutter
NSAssert(![controller attached],
@"The incoming view controller is already attached to an engine.");
NSAssert([_viewControllers objectForKey:@(viewId)] == nil, @"The requested view ID is occupied.");
[controller attachToEngine:self withId:viewId];
[controller setUpWithEngine:self viewId:viewId threadSynchronizer:_threadSynchronizer];
NSAssert(controller.viewId == viewId, @"Failed to assign view ID.");
[_viewControllers setObject:controller forKey:@(viewId)];
}
Expand Down Expand Up @@ -928,11 +931,8 @@ - (void)shutDownEngine {
return;
}

NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
FlutterViewController* nextViewController;
while ((nextViewController = [viewControllerEnumerator nextObject])) {
[nextViewController.flutterView shutdown];
}
[_threadSynchronizer shutdown];
_threadSynchronizer = nil;

FlutterEngineResult result = _embedderAPI.Deinitialize(_engine);
if (result != kSuccess) {
Expand Down Expand Up @@ -1117,6 +1117,10 @@ - (NSPasteboard*)pasteboard {
return flutter::GetSwitchesFromEnvironment();
}

- (FlutterThreadSynchronizer*)testThreadSynchronizer {
return _threadSynchronizer;
}

#pragma mark - FlutterBinaryMessenger

- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@
// CREATE_NATIVE_ENTRY and MOCK_ENGINE_PROC are leaky by design
// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)

constexpr int64_t kDefaultViewId = 0ll;

@interface FlutterEngine (Test)
/**
* The FlutterCompositor object currently in use by the FlutterEngine.
*
* May be nil if the compositor has not been initialized yet.
*/
@property(nonatomic, readonly, nullable) flutter::FlutterCompositor* macOSCompositor;

@end

@interface TestPlatformViewFactory : NSObject <FlutterPlatformViewFactory>
Expand Down Expand Up @@ -438,7 +441,7 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable
result:^(id result){
}];

[viewController.flutterView.threadSynchronizer blockUntilFrameAvailable];
[engine.testThreadSynchronizer blockUntilFrameAvailable];

CALayer* rootLayer = viewController.flutterView.layer;

Expand Down Expand Up @@ -629,9 +632,10 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable
[threadSynchronizer shutdown];

std::thread rasterThread([&threadSynchronizer] {
[threadSynchronizer performCommit:CGSizeMake(100, 100)
notify:^{
}];
[threadSynchronizer performCommitForView:kDefaultViewId
size:CGSizeMake(100, 100)
notify:^{
}];
});

rasterThread.join();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,8 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) {

@end

@interface FlutterEngine (Tests)
- (nonnull FlutterThreadSynchronizer*)testThreadSynchronizer;
@end

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import <Cocoa/Cocoa.h>

/**
* Takes care of synchronization between raster and platform thread.
*
* All methods of this class must be called from the platform thread,
* except for performCommitForView:size:notify:.
*/
@interface FlutterThreadSynchronizer : NSObject

/**
* Blocks current thread until there is frame available.
* Used in FlutterEngineTest.
* Creates a FlutterThreadSynchronizer that uses the OS main thread as the
* platform thread.
*/
- (void)blockUntilFrameAvailable;
- (nullable instancetype)init;

/**
* Called from platform thread. Blocks until commit with given size (or empty)
* is requested.
* Blocks until all views have a commit with their given sizes (or empty) is requested.
*/
- (void)beginResize:(CGSize)size notify:(nonnull dispatch_block_t)notify;
- (void)beginResizeForView:(int64_t)viewId
size:(CGSize)size
notify:(nonnull dispatch_block_t)notify;

/**
* Called from raster thread. Schedules the given block on platform thread
Expand All @@ -26,11 +34,65 @@
*
* The notify block is guaranteed to be called within a core animation transaction.
*/
- (void)performCommit:(CGSize)size notify:(nonnull dispatch_block_t)notify;
- (void)performCommitForView:(int64_t)viewId
size:(CGSize)size
notify:(nonnull dispatch_block_t)notify;

/**
* Requests the synchronizer to track another view.
*
* A view must be registered before calling begineResizeForView: or
* performCommitForView:. It is typically done when the view controller is
* created.
*/
- (void)registerView:(int64_t)viewId;

/**
* Requests the synchronizer to no longer track a view.
*
* It is typically done when the view controller is destroyed.
*/
- (void)deregisterView:(int64_t)viewId;

/**
* Called when shutting down. Unblocks everything and prevents any further synchronization.
* Called when the engine shuts down.
*
* Prevents any further synchronization and no longer blocks any threads.
*/
- (void)shutdown;

@end

@interface FlutterThreadSynchronizer (TestUtils)

/**
* Creates a FlutterThreadSynchronizer that uses the specified queue as the
* platform thread.
*/
- (nullable instancetype)initWithMainQueue:(nonnull dispatch_queue_t)queue;

/**
* Blocks current thread until the mutex is available, then return whether the
* synchronizer is waiting for a correct commit during resizing.
*
* After calling an operation of the thread synchronizer, call this method,
* and when it returns, the thread synchronizer can be at one of the following 3
* states:
*
* 1. The operation has not started at all (with a return value FALSE.)
* 2. The operation has ended (with a return value FALSE.)
* 3. beginResizeForView: is in progress, waiting (with a return value TRUE.)
*
* By eliminating the 1st case (such as using the notify callback), we can use
* this return value to decide whether the synchronizer is in case 2 or case 3,
* that is whether the resizing is blocked by a mismatching commit.
*/
- (BOOL)isWaitingWhenMutexIsAvailable;

/**
* Blocks current thread until there is frame available.
* Used in FlutterEngineTest.
*/
- (void)blockUntilFrameAvailable;

@end
Loading