Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit bc1824c

Browse files
committed
Made platform message responses threadsafe for macos.
1 parent fa924c7 commit bc1824c

File tree

5 files changed

+86
-10
lines changed

5 files changed

+86
-10
lines changed

shell/platform/darwin/macos/framework/Source/FlutterEngine.mm

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ - (instancetype)initWithConnection:(NSNumber*)connection
7373
*/
7474
@interface FlutterEngine () <FlutterBinaryMessenger>
7575

76+
/**
77+
* A mutable array that holds one bool value that determines if responses to platform messages are
78+
* clear to execute. This value should be read or written only inside of a synchronized block and
79+
* will return `NO` after the FlutterEngine has been dealloc'd.
80+
*/
81+
@property(nonatomic, strong) NSMutableArray<NSNumber*>* isResponseValid;
82+
7683
/**
7784
* Sends the list of user-preferred locales to the Flutter engine.
7885
*/
@@ -243,6 +250,8 @@ - (instancetype)initWithName:(NSString*)labelPrefix
243250
_allowHeadlessExecution = allowHeadlessExecution;
244251
_semanticsEnabled = NO;
245252
_viewProvider = [[FlutterViewEngineProvider alloc] initWithEngine:self];
253+
_isResponseValid = [[NSMutableArray alloc] initWithCapacity:1];
254+
[_isResponseValid addObject:@YES];
246255

247256
_embedderAPI.struct_size = sizeof(FlutterEngineProcTable);
248257
FlutterEngineGetProcAddresses(&_embedderAPI);
@@ -263,6 +272,10 @@ - (instancetype)initWithName:(NSString*)labelPrefix
263272
}
264273

265274
- (void)dealloc {
275+
@synchronized(_isResponseValid) {
276+
[_isResponseValid removeAllObjects];
277+
[_isResponseValid addObject:@NO];
278+
}
266279
[self shutDownEngine];
267280
if (_aotData) {
268281
_embedderAPI.CollectAOTData(_aotData);
@@ -639,17 +652,25 @@ - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
639652
}
640653
NSString* channel = @(message->channel);
641654
__block const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle;
642-
655+
__block FlutterEngine* weakSelf = self;
656+
NSMutableArray* isResponseValid = self.isResponseValid;
657+
FlutterEngineSendPlatformMessageResponseFnPtr sendPlatformMessageResponse =
658+
_embedderAPI.SendPlatformMessageResponse;
643659
FlutterBinaryReply binaryResponseHandler = ^(NSData* response) {
644-
if (responseHandle) {
645-
_embedderAPI.SendPlatformMessageResponse(self->_engine, responseHandle,
646-
static_cast<const uint8_t*>(response.bytes),
647-
response.length);
648-
responseHandle = NULL;
649-
} else {
650-
NSLog(@"Error: Message responses can be sent only once. Ignoring duplicate response "
651-
"on channel '%@'.",
652-
channel);
660+
@synchronized(isResponseValid) {
661+
if (![isResponseValid[0] boolValue]) {
662+
// Ignore, engine was killed.
663+
return;
664+
}
665+
if (responseHandle) {
666+
sendPlatformMessageResponse(weakSelf->_engine, responseHandle,
667+
static_cast<const uint8_t*>(response.bytes), response.length);
668+
responseHandle = NULL;
669+
} else {
670+
NSLog(@"Error: Message responses can be sent only once. Ignoring duplicate response "
671+
"on channel '%@'.",
672+
channel);
673+
}
653674
}
654675
};
655676

shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,48 @@ @interface FlutterEngine (Test)
622622
EXPECT_TRUE(value);
623623
}
624624

625+
TEST_F(FlutterEngineTest, ResponseAfterEngineDied) {
626+
FlutterEngine* engine = GetFlutterEngine();
627+
FlutterBasicMessageChannel* channel = [[FlutterBasicMessageChannel alloc]
628+
initWithName:@"foo"
629+
binaryMessenger:engine.binaryMessenger
630+
codec:[FlutterStandardMessageCodec sharedInstance]];
631+
__block BOOL didCallCallback = NO;
632+
[channel setMessageHandler:^(id message, FlutterReply callback) {
633+
ShutDownEngine();
634+
callback(nil);
635+
didCallCallback = YES;
636+
}];
637+
EXPECT_TRUE([engine runWithEntrypoint:@"sendFooMessage"]);
638+
engine = nil;
639+
640+
while (!didCallCallback) {
641+
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
642+
}
643+
}
644+
645+
TEST_F(FlutterEngineTest, ResponseFromBackgroundThread) {
646+
FlutterEngine* engine = GetFlutterEngine();
647+
FlutterBasicMessageChannel* channel = [[FlutterBasicMessageChannel alloc]
648+
initWithName:@"foo"
649+
binaryMessenger:engine.binaryMessenger
650+
codec:[FlutterStandardMessageCodec sharedInstance]];
651+
__block BOOL didCallCallback = NO;
652+
[channel setMessageHandler:^(id message, FlutterReply callback) {
653+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
654+
callback(nil);
655+
dispatch_async(dispatch_get_main_queue(), ^{
656+
didCallCallback = YES;
657+
});
658+
});
659+
}];
660+
EXPECT_TRUE([engine runWithEntrypoint:@"sendFooMessage"]);
661+
662+
while (!didCallCallback) {
663+
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
664+
}
665+
}
666+
625667
} // namespace flutter::testing
626668

627669
// NOLINTEND(clang-analyzer-core.StackAddressEscape)

shell/platform/darwin/macos/framework/Source/FlutterEngineTestUtils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ class FlutterEngineTest : public ::testing::Test {
2323

2424
static void IsolateCreateCallback(void* user_data);
2525

26+
void ShutDownEngine();
27+
2628
private:
2729
inline static std::shared_ptr<TestDartNativeResolver> native_resolver_;
2830

shell/platform/darwin/macos/framework/Source/FlutterEngineTestUtils.mm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131
native_resolver_.reset();
3232
}
3333

34+
void FlutterEngineTest::ShutDownEngine() {
35+
[engine_ shutDownEngine];
36+
engine_ = nil;
37+
}
38+
3439
void FlutterEngineTest::IsolateCreateCallback(void* user_data) {
3540
native_resolver_->SetNativeResolverForIsolate();
3641
}

shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'dart:io';
6+
import 'dart:typed_data';
67
import 'dart:ui';
78

89
@pragma('vm:external-name', 'SignalNativeTest')
@@ -63,3 +64,8 @@ void backgroundTest() {
6364
PlatformDispatcher.instance.views.first.render(SceneBuilder().build());
6465
signalNativeTest(); // should look black
6566
}
67+
68+
@pragma('vm:entry-point')
69+
void sendFooMessage() {
70+
PlatformDispatcher.instance.sendPlatformMessage('foo', null, (ByteData? result) {});
71+
}

0 commit comments

Comments
 (0)