diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index 14ee5daa917..b56081a0600 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,9 @@ +## 24.2.0 + +* Adjusts task queues to use a shared task queue for all methods in a single + API instance, to give the same ordering guarantees as non-task-queue usage. +* [swift] Adds task queue support to the Swift generator. + ## 24.1.1 * [swift, kotlin] Adds an error message when a ProxyAPI callback method that returns a non-null diff --git a/packages/pigeon/lib/src/generator_tools.dart b/packages/pigeon/lib/src/generator_tools.dart index e6d7be1623b..e6cc6cde6ce 100644 --- a/packages/pigeon/lib/src/generator_tools.dart +++ b/packages/pigeon/lib/src/generator_tools.dart @@ -14,7 +14,7 @@ import 'ast.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '24.1.1'; +const String pigeonVersion = '24.2.0'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/lib/src/java/java_generator.dart b/packages/pigeon/lib/src/java/java_generator.dart index c5fd0007b8f..818639d2a92 100644 --- a/packages/pigeon/lib/src/java/java_generator.dart +++ b/packages/pigeon/lib/src/java/java_generator.dart @@ -6,7 +6,7 @@ import '../ast.dart'; import '../functional.dart'; import '../generator.dart'; import '../generator_tools.dart'; -import '../pigeon_lib.dart' show TaskQueueType; +import '../types/task_queue.dart'; /// Documentation open symbol. const String _docCommentPrefix = '/**'; @@ -835,15 +835,21 @@ if (wrapped == null) { indent.addScoped('{', '}', () { indent.writeln( 'messageChannelSuffix = messageChannelSuffix.isEmpty() ? "" : "." + messageChannelSuffix;'); + String? serialBackgroundQueue; + if (api.methods.any((Method m) => + m.taskQueueType == TaskQueueType.serialBackgroundThread)) { + serialBackgroundQueue = 'taskQueue'; + indent.writeln( + 'BinaryMessenger.TaskQueue $serialBackgroundQueue = binaryMessenger.makeBackgroundTaskQueue();'); + } for (final Method method in api.methods) { - _writeMethodSetUp( - generatorOptions, - root, - indent, - api, - method, - dartPackageName: dartPackageName, - ); + _writeHostMethodMessageHandler( + generatorOptions, root, indent, api, method, + dartPackageName: dartPackageName, + serialBackgroundQueue: + method.taskQueueType == TaskQueueType.serialBackgroundThread + ? serialBackgroundQueue + : null); } }); }); @@ -887,34 +893,27 @@ if (wrapped == null) { indent.writeln('$returnType ${method.name}(${argSignature.join(', ')});'); } - /// Write a static setUp function in the interface. - /// Example: - /// static void setUp(BinaryMessenger binaryMessenger, Foo api) {...} - void _writeMethodSetUp( + /// Write a single method's handler for the setUp function. + void _writeHostMethodMessageHandler( JavaOptions generatorOptions, Root root, Indent indent, Api api, final Method method, { required String dartPackageName, + String? serialBackgroundQueue, }) { final String channelName = makeChannelName(api, method, dartPackageName); indent.write(''); indent.addScoped('{', '}', () { - String? taskQueue; - if (method.taskQueueType != TaskQueueType.serial) { - taskQueue = 'taskQueue'; - indent.writeln( - 'BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue();'); - } indent.writeln('BasicMessageChannel channel ='); indent.nest(2, () { indent.writeln('new BasicMessageChannel<>('); indent.nest(2, () { indent.write( 'binaryMessenger, "$channelName" + messageChannelSuffix, getCodec()'); - if (taskQueue != null) { - indent.addln(', $taskQueue);'); + if (serialBackgroundQueue != null) { + indent.addln(', $serialBackgroundQueue);'); } else { indent.addln(');'); } diff --git a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart index 6a59d7594d0..963ac46e9ee 100644 --- a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart +++ b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart @@ -8,7 +8,7 @@ import '../ast.dart'; import '../functional.dart'; import '../generator.dart'; import '../generator_tools.dart'; -import '../pigeon_lib.dart' show TaskQueueType; +import '../types/task_queue.dart'; import 'templates.dart'; /// Documentation open symbol. @@ -654,6 +654,13 @@ if (wrapped == null) { indent.addScoped('{', '}', () { indent.writeln( r'val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""'); + String? serialBackgroundQueue; + if (api.methods.any((Method m) => + m.taskQueueType == TaskQueueType.serialBackgroundThread)) { + serialBackgroundQueue = 'taskQueue'; + indent.writeln( + 'val $serialBackgroundQueue = binaryMessenger.makeBackgroundTaskQueue()'); + } for (final Method method in api.methods) { _writeHostMethodMessageHandler( indent, @@ -664,6 +671,10 @@ if (wrapped == null) { parameters: method.parameters, returnType: method.returnType, isAsynchronous: method.isAsynchronous, + serialBackgroundQueue: + method.taskQueueType == TaskQueueType.serialBackgroundThread + ? serialBackgroundQueue + : null, ); } }); @@ -1071,8 +1082,8 @@ if (wrapped == null) { fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { sink.error(errorCode, errorMessage, errorDetails) } - - fun endOfStream() { + + fun endOfStream() { sink.endOfStream() } } @@ -1249,24 +1260,18 @@ if (wrapped == null) { required TypeDeclaration returnType, String setHandlerCondition = 'api != null', bool isAsynchronous = false, + String? serialBackgroundQueue, String Function(List safeArgNames, {required String apiVarName})? onCreateCall, }) { indent.write('run '); indent.addScoped('{', '}', () { - String? taskQueue; - if (taskQueueType != TaskQueueType.serial) { - taskQueue = 'taskQueue'; - indent.writeln( - 'val $taskQueue = binaryMessenger.makeBackgroundTaskQueue()'); - } - indent.write( 'val channel = BasicMessageChannel(binaryMessenger, "$channelName", codec', ); - if (taskQueue != null) { - indent.addln(', $taskQueue)'); + if (serialBackgroundQueue != null) { + indent.addln(', $serialBackgroundQueue)'); } else { indent.addln(')'); } diff --git a/packages/pigeon/lib/src/objc/objc_generator.dart b/packages/pigeon/lib/src/objc/objc_generator.dart index e37d3bbcba8..84780c894a1 100644 --- a/packages/pigeon/lib/src/objc/objc_generator.dart +++ b/packages/pigeon/lib/src/objc/objc_generator.dart @@ -6,7 +6,8 @@ import '../ast.dart'; import '../functional.dart'; import '../generator.dart'; import '../generator_tools.dart'; -import '../pigeon_lib.dart' show Error, TaskQueueType; +import '../pigeon_lib.dart' show Error; +import '../types/task_queue.dart'; /// Documentation comment open symbol. const String _docCommentPrefix = '///'; @@ -860,24 +861,33 @@ if (self.wrapped == nil) { indent.addScoped('{', '}', () { indent.writeln( 'messageChannelSuffix = messageChannelSuffix.length > 0 ? [NSString stringWithFormat: @".%@", messageChannelSuffix] : @"";'); + String? serialBackgroundQueue; + if (api.methods.any((Method m) => + m.taskQueueType == TaskQueueType.serialBackgroundThread)) { + serialBackgroundQueue = 'taskQueue'; + // See https://github.com/flutter/flutter/issues/162613 for why this + // is an ifdef instead of just a respondsToSelector: check. + indent.format(''' +#if TARGET_OS_IOS + NSObject *$serialBackgroundQueue = [binaryMessenger makeBackgroundTaskQueue]; +#else + NSObject *$serialBackgroundQueue = nil; +#endif'''); + } for (final Method func in api.methods) { addDocumentationComments( indent, func.documentationComments, _docCommentSpec); indent.writeScoped('{', '}', () { - String? taskQueue; - if (func.taskQueueType != TaskQueueType.serial) { - taskQueue = 'taskQueue'; - indent.writeln( - 'NSObject *$taskQueue = [binaryMessenger makeBackgroundTaskQueue];'); - } _writeChannelAllocation( generatorOptions, indent, api, func, channelName, - taskQueue, + func.taskQueueType == TaskQueueType.serialBackgroundThread + ? serialBackgroundQueue + : null, dartPackageName: dartPackageName, ); indent.write('if (api) '); @@ -1112,7 +1122,15 @@ static FlutterError *createConnectionError(NSString *channelName) { if (taskQueue != null) { indent.newln(); - indent.addln('taskQueue:$taskQueue];'); + // See https://github.com/flutter/flutter/issues/162613 for why this + // is in an ifdef instead of just relying on the parameter being + // nullable. + indent.format(''' +#ifdef TARGET_OS_IOS +taskQueue:$taskQueue +#endif +]; +'''); } else { indent.addln('];'); } diff --git a/packages/pigeon/lib/src/pigeon_lib.dart b/packages/pigeon/lib/src/pigeon_lib.dart index 45ed062ca52..72eb4f50942 100644 --- a/packages/pigeon/lib/src/pigeon_lib.dart +++ b/packages/pigeon/lib/src/pigeon_lib.dart @@ -36,6 +36,9 @@ import 'java/java_generator.dart'; import 'kotlin/kotlin_generator.dart'; import 'objc/objc_generator.dart'; import 'swift/swift_generator.dart'; +import 'types/task_queue.dart'; + +export 'types/task_queue.dart' show TaskQueueType; class _Asynchronous { const _Asynchronous(); @@ -211,21 +214,6 @@ class SwiftClass { const SwiftClass(); } -/// Type of TaskQueue which determines how handlers are dispatched for -/// HostApi's. -enum TaskQueueType { - /// Handlers are invoked serially on the default thread. This is the value if - /// unspecified. - serial, - - /// Handlers are invoked serially on a background thread. - serialBackgroundThread, - - // TODO(gaaclarke): Add support for concurrent task queues. - // /// Handlers are invoked concurrently on a background thread. - // concurrentBackgroundThread, -} - /// Metadata annotation to control how handlers are dispatched for HostApi's. /// Note that the TaskQueue API might not be available on the target version of /// Flutter, see also: diff --git a/packages/pigeon/lib/src/swift/swift_generator.dart b/packages/pigeon/lib/src/swift/swift_generator.dart index 848c4bfa323..843b45595fa 100644 --- a/packages/pigeon/lib/src/swift/swift_generator.dart +++ b/packages/pigeon/lib/src/swift/swift_generator.dart @@ -9,6 +9,7 @@ import '../ast.dart'; import '../functional.dart'; import '../generator.dart'; import '../generator_tools.dart'; +import '../types/task_queue.dart'; import 'templates.dart'; /// Documentation comment open symbol. @@ -736,6 +737,21 @@ if (wrapped == nil) { indent.addScoped('{', '}', () { indent.writeln( r'let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""'); + String? serialBackgroundQueue; + if (api.methods.any((Method m) => + m.taskQueueType == TaskQueueType.serialBackgroundThread)) { + serialBackgroundQueue = 'taskQueue'; + // TODO(stuartmorgan): Remove the ? once macOS supports task queues + // and this is no longer an optional protocol method. + // See https://github.com/flutter/flutter/issues/162613 for why this + // is an ifdef instead of just relying on the optionality check. + indent.format(''' +#if os(iOS) + let $serialBackgroundQueue = binaryMessenger.makeBackgroundTaskQueue?() +#else + let $serialBackgroundQueue: FlutterTaskQueue? = nil +#endif'''); + } for (final Method method in api.methods) { _writeHostMethodMessageHandler( indent, @@ -747,6 +763,10 @@ if (wrapped == nil) { isAsynchronous: method.isAsynchronous, swiftFunction: method.swiftFunction, documentationComments: method.documentationComments, + serialBackgroundQueue: + method.taskQueueType == TaskQueueType.serialBackgroundThread + ? serialBackgroundQueue + : null, ); } }); @@ -1404,7 +1424,7 @@ private func nilOrValue(_ value: Any?) -> T? { func endOfStream() { sink(FlutterEndOfEventStream) } - + } '''); } @@ -1413,7 +1433,7 @@ private func nilOrValue(_ value: Any?) -> T? { for (final Method func in api.methods) { indent.format(''' class ${toUpperCamelCase(func.name)}StreamHandler: PigeonEventChannelWrapper<${_swiftTypeForDartType(func.returnType)}> { - static func register(with messenger: FlutterBinaryMessenger, + static func register(with messenger: FlutterBinaryMessenger, instanceName: String = "", streamHandler: ${toUpperCamelCase(func.name)}StreamHandler) { var channelName = "${makeChannelName(api, func, dartPackageName)}" @@ -1535,6 +1555,7 @@ private func nilOrValue(_ value: Any?) -> T? { required TypeDeclaration returnType, required bool isAsynchronous, required String? swiftFunction, + String? serialBackgroundQueue, String setHandlerCondition = 'let api = api', List documentationComments = const [], String Function(List safeArgNames, {required String apiVarName})? @@ -1549,8 +1570,30 @@ private func nilOrValue(_ value: Any?) -> T? { final String varChannelName = '${name}Channel'; addDocumentationComments(indent, documentationComments, _docCommentSpec); - indent.writeln( - 'let $varChannelName = FlutterBasicMessageChannel(name: "$channelName", binaryMessenger: binaryMessenger, codec: codec)'); + final String baseArgs = 'name: "$channelName", ' + 'binaryMessenger: binaryMessenger, codec: codec'; + // The version with taskQueue: is an optional protocol method that isn't + // implemented on macOS yet, so the call has to be conditionalized even + // though the taskQueue argument is nullable. The runtime branching can be + // removed once macOS supports task queues. The condition is on the task + // queue variable not being nil because the earlier code to set it will + // return nil on macOS where the optional parts of the protocol are not + // implemented. + final String channelCreationWithoutTaskQueue = + 'FlutterBasicMessageChannel($baseArgs)'; + if (serialBackgroundQueue == null) { + indent.writeln('let $varChannelName = $channelCreationWithoutTaskQueue'); + } else { + final String channelCreationWithTaskQueue = + 'FlutterBasicMessageChannel($baseArgs, taskQueue: $serialBackgroundQueue)'; + + indent.write('let $varChannelName = $serialBackgroundQueue == nil'); + indent.addScoped('', '', () { + indent.writeln('? $channelCreationWithoutTaskQueue'); + indent.writeln(': $channelCreationWithTaskQueue'); + }); + } + indent.write('if $setHandlerCondition '); indent.addScoped('{', '}', () { indent.write('$varChannelName.setMessageHandler '); diff --git a/packages/pigeon/lib/src/types/task_queue.dart b/packages/pigeon/lib/src/types/task_queue.dart new file mode 100644 index 00000000000..63869f5dfab --- /dev/null +++ b/packages/pigeon/lib/src/types/task_queue.dart @@ -0,0 +1,18 @@ +// 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. + +/// Type of TaskQueue which determines how handlers are dispatched for +/// HostApi's. +enum TaskQueueType { + /// Handlers are invoked serially on the default thread. This is the value if + /// unspecified. + serial, + + /// Handlers are invoked serially on a background thread. + serialBackgroundThread, + + // TODO(gaaclarke): Add support for concurrent task queues. + // /// Handlers are invoked concurrently on a background thread. + // concurrentBackgroundThread, +} diff --git a/packages/pigeon/pigeons/background_platform_channels.dart b/packages/pigeon/pigeons/background_platform_channels.dart deleted file mode 100644 index 643b880aa48..00000000000 --- a/packages/pigeon/pigeons/background_platform_channels.dart +++ /dev/null @@ -1,11 +0,0 @@ -// 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 'package:pigeon/pigeon.dart'; - -@HostApi() -abstract class BackgroundApi2Host { - @TaskQueue(type: TaskQueueType.serialBackgroundThread) - int add(int x, int y); -} diff --git a/packages/pigeon/pigeons/core_tests.dart b/packages/pigeon/pigeons/core_tests.dart index a644ce5b3e0..8a154c8b62d 100644 --- a/packages/pigeon/pigeons/core_tests.dart +++ b/packages/pigeon/pigeons/core_tests.dart @@ -838,6 +838,17 @@ abstract class HostIntegrationCoreApi { @SwiftFunction('echoAsyncNullable(_:)') AnotherEnum? echoAnotherAsyncNullableEnum(AnotherEnum? anotherEnum); + // ========== TaskQueue tests ========== + + /// Returns true if the handler is run on a main thread, which should be + /// true since there is no TaskQueue annotation. + bool defaultIsMainThread(); + + /// Returns true if the handler is run on a non-main thread, which should be + /// true for any platform with TaskQueue support. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + bool taskQueueIsBackgroundThread(); + // ========== Flutter API test wrappers ========== @async diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/AlternateLanguageTestPlugin.java b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/AlternateLanguageTestPlugin.java index 8580645a27d..a6a39ecec94 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/AlternateLanguageTestPlugin.java +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/AlternateLanguageTestPlugin.java @@ -4,6 +4,7 @@ package com.example.alternate_language_test_plugin; +import android.os.Looper; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.example.alternate_language_test_plugin.CoreTests.AllClassesWrapper; @@ -615,6 +616,16 @@ public void echoAnotherAsyncNullableEnum( result.success(anotherEnum); } + @Override + public @NonNull Boolean defaultIsMainThread() { + return Thread.currentThread() == Looper.getMainLooper().getThread(); + } + + @Override + public @NonNull Boolean taskQueueIsBackgroundThread() { + return Thread.currentThread() != Looper.getMainLooper().getThread(); + } + @Override public void callFlutterNoop(@NonNull VoidResult result) { assert flutterApi != null; diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java index 4707969aab1..7cd093e4996 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/android/src/main/java/com/example/alternate_language_test_plugin/CoreTests.java @@ -3166,6 +3166,18 @@ void echoAsyncNullableClassMap( /** Returns the passed enum, to test asynchronous serialization and deserialization. */ void echoAnotherAsyncNullableEnum( @Nullable AnotherEnum anotherEnum, @NonNull NullableResult result); + /** + * Returns true if the handler is run on a main thread, which should be true since there is no + * TaskQueue annotation. + */ + @NonNull + Boolean defaultIsMainThread(); + /** + * Returns true if the handler is run on a non-main thread, which should be true for any + * platform with TaskQueue support. + */ + @NonNull + Boolean taskQueueIsBackgroundThread(); void callFlutterNoop(@NonNull VoidResult result); @@ -3340,6 +3352,7 @@ static void setUp( @NonNull String messageChannelSuffix, @Nullable HostIntegrationCoreApi api) { messageChannelSuffix = messageChannelSuffix.isEmpty() ? "" : "." + messageChannelSuffix; + BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -6109,6 +6122,53 @@ public void error(Throwable error) { channel.setMessageHandler(null); } } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.defaultIsMainThread" + + messageChannelSuffix, + getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList<>(); + try { + Boolean output = api.defaultIsMainThread(); + wrapped.add(0, output); + } catch (Throwable exception) { + wrapped = wrapError(exception); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.taskQueueIsBackgroundThread" + + messageChannelSuffix, + getCodec(), + taskQueue); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList<>(); + try { + Boolean output = api.taskQueueIsBackgroundThread(); + wrapped.add(0, output); + } catch (Throwable exception) { + wrapped = wrapError(exception); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } { BasicMessageChannel channel = new BasicMessageChannel<>( diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m index d684acfd55e..4f2b9d69450 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/AlternateLanguageTestPlugin.m @@ -600,6 +600,19 @@ - (void)echoAnotherAsyncNullableEnum:(nullable FLTAnotherEnumBox *)AnotherEnumBo completion(AnotherEnumBoxed, nil); } +- (nullable NSNumber *)defaultIsMainThreadWithError:(FlutterError *_Nullable *_Nonnull)error { + // Using boxing on an inline expression results in the Dart side receiving an int, so + // force the right type via numberWithBool. + return [NSNumber numberWithBool:NSThread.isMainThread]; +} + +- (nullable NSNumber *)taskQueueIsBackgroundThreadWithError: + (FlutterError *_Nullable *_Nonnull)error { + // Using boxing on an inline expression results in the Dart side receiving an int, so + // force the right type via numberWithBool. + return [NSNumber numberWithBool:!NSThread.isMainThread]; +} + - (void)callFlutterNoopWithCompletion:(void (^)(FlutterError *_Nullable))completion { [self.flutterAPI noopWithCompletion:^(FlutterError *error) { completion(error); diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h index bd913ca95a9..3a7b01778b9 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.h @@ -695,6 +695,17 @@ NSObject *FLTGetCoreTestsCodec(void); - (void)echoAnotherAsyncNullableEnum:(nullable FLTAnotherEnumBox *)anotherEnumBoxed completion:(void (^)(FLTAnotherEnumBox *_Nullable, FlutterError *_Nullable))completion; +/// Returns true if the handler is run on a main thread, which should be +/// true since there is no TaskQueue annotation. +/// +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *)defaultIsMainThreadWithError:(FlutterError *_Nullable *_Nonnull)error; +/// Returns true if the handler is run on a non-main thread, which should be +/// true for any platform with TaskQueue support. +/// +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *)taskQueueIsBackgroundThreadWithError: + (FlutterError *_Nullable *_Nonnull)error; - (void)callFlutterNoopWithCompletion:(void (^)(FlutterError *_Nullable))completion; - (void)callFlutterThrowErrorWithCompletion:(void (^)(id _Nullable, FlutterError *_Nullable))completion; diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m index 2cc3da7877d..36fb0a516f5 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/ios/Classes/CoreTests.gen.m @@ -696,6 +696,11 @@ void SetUpFLTHostIntegrationCoreApiWithSuffix(id binaryM messageChannelSuffix = messageChannelSuffix.length > 0 ? [NSString stringWithFormat:@".%@", messageChannelSuffix] : @""; +#if TARGET_OS_IOS + NSObject *taskQueue = [binaryMessenger makeBackgroundTaskQueue]; +#else + NSObject *taskQueue = nil; +#endif /// A no-op function taking no arguments and returning no value, to sanity /// test basic calling. { @@ -3319,6 +3324,60 @@ void SetUpFLTHostIntegrationCoreApiWithSuffix(id binaryM [channel setMessageHandler:nil]; } } + /// Returns true if the handler is run on a main thread, which should be + /// true since there is no TaskQueue annotation. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.pigeon_integration_tests." + @"HostIntegrationCoreApi.defaultIsMainThread", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FLTGetCoreTestsCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(defaultIsMainThreadWithError:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(defaultIsMainThreadWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSNumber *output = [api defaultIsMainThreadWithError:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Returns true if the handler is run on a non-main thread, which should be + /// true for any platform with TaskQueue support. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.pigeon_integration_tests." + @"HostIntegrationCoreApi.taskQueueIsBackgroundThread", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FLTGetCoreTestsCodec() +#ifdef TARGET_OS_IOS + taskQueue:taskQueue +#endif + ]; + + if (api) { + NSCAssert([api respondsToSelector:@selector(taskQueueIsBackgroundThreadWithError:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(taskQueueIsBackgroundThreadWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSNumber *output = [api taskQueueIsBackgroundThreadWithError:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] initWithName:[NSString stringWithFormat:@"%@%@", diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m index d684acfd55e..4f2b9d69450 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/AlternateLanguageTestPlugin.m @@ -600,6 +600,19 @@ - (void)echoAnotherAsyncNullableEnum:(nullable FLTAnotherEnumBox *)AnotherEnumBo completion(AnotherEnumBoxed, nil); } +- (nullable NSNumber *)defaultIsMainThreadWithError:(FlutterError *_Nullable *_Nonnull)error { + // Using boxing on an inline expression results in the Dart side receiving an int, so + // force the right type via numberWithBool. + return [NSNumber numberWithBool:NSThread.isMainThread]; +} + +- (nullable NSNumber *)taskQueueIsBackgroundThreadWithError: + (FlutterError *_Nullable *_Nonnull)error { + // Using boxing on an inline expression results in the Dart side receiving an int, so + // force the right type via numberWithBool. + return [NSNumber numberWithBool:!NSThread.isMainThread]; +} + - (void)callFlutterNoopWithCompletion:(void (^)(FlutterError *_Nullable))completion { [self.flutterAPI noopWithCompletion:^(FlutterError *error) { completion(error); diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.h b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.h index bd913ca95a9..3a7b01778b9 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.h +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.h @@ -695,6 +695,17 @@ NSObject *FLTGetCoreTestsCodec(void); - (void)echoAnotherAsyncNullableEnum:(nullable FLTAnotherEnumBox *)anotherEnumBoxed completion:(void (^)(FLTAnotherEnumBox *_Nullable, FlutterError *_Nullable))completion; +/// Returns true if the handler is run on a main thread, which should be +/// true since there is no TaskQueue annotation. +/// +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *)defaultIsMainThreadWithError:(FlutterError *_Nullable *_Nonnull)error; +/// Returns true if the handler is run on a non-main thread, which should be +/// true for any platform with TaskQueue support. +/// +/// @return `nil` only when `error != nil`. +- (nullable NSNumber *)taskQueueIsBackgroundThreadWithError: + (FlutterError *_Nullable *_Nonnull)error; - (void)callFlutterNoopWithCompletion:(void (^)(FlutterError *_Nullable))completion; - (void)callFlutterThrowErrorWithCompletion:(void (^)(id _Nullable, FlutterError *_Nullable))completion; diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.m index 2cc3da7877d..36fb0a516f5 100644 --- a/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.m +++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/macos/Classes/CoreTests.gen.m @@ -696,6 +696,11 @@ void SetUpFLTHostIntegrationCoreApiWithSuffix(id binaryM messageChannelSuffix = messageChannelSuffix.length > 0 ? [NSString stringWithFormat:@".%@", messageChannelSuffix] : @""; +#if TARGET_OS_IOS + NSObject *taskQueue = [binaryMessenger makeBackgroundTaskQueue]; +#else + NSObject *taskQueue = nil; +#endif /// A no-op function taking no arguments and returning no value, to sanity /// test basic calling. { @@ -3319,6 +3324,60 @@ void SetUpFLTHostIntegrationCoreApiWithSuffix(id binaryM [channel setMessageHandler:nil]; } } + /// Returns true if the handler is run on a main thread, which should be + /// true since there is no TaskQueue annotation. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.pigeon_integration_tests." + @"HostIntegrationCoreApi.defaultIsMainThread", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FLTGetCoreTestsCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(defaultIsMainThreadWithError:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(defaultIsMainThreadWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSNumber *output = [api defaultIsMainThreadWithError:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + /// Returns true if the handler is run on a non-main thread, which should be + /// true for any platform with TaskQueue support. + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:[NSString + stringWithFormat:@"%@%@", + @"dev.flutter.pigeon.pigeon_integration_tests." + @"HostIntegrationCoreApi.taskQueueIsBackgroundThread", + messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:FLTGetCoreTestsCodec() +#ifdef TARGET_OS_IOS + taskQueue:taskQueue +#endif + ]; + + if (api) { + NSCAssert([api respondsToSelector:@selector(taskQueueIsBackgroundThreadWithError:)], + @"FLTHostIntegrationCoreApi api (%@) doesn't respond to " + @"@selector(taskQueueIsBackgroundThreadWithError:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + FlutterError *error; + NSNumber *output = [api taskQueueIsBackgroundThreadWithError:&error]; + callback(wrapResult(output, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] initWithName:[NSString stringWithFormat:@"%@%@", diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart index 8b5da977702..d14db88cbd6 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart @@ -2871,6 +2871,28 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { expect(unused, unused); }); + /// Task queues + + testWidgets('non-task-queue handlers run on a the main thread', (_) async { + final HostIntegrationCoreApi api = HostIntegrationCoreApi(); + expect(await api.defaultIsMainThread(), true); + }); + + testWidgets('task queue handlers run on a background thread', (_) async { + final HostIntegrationCoreApi api = HostIntegrationCoreApi(); + // Currently only Android and iOS have task queue support. See + // https://github.com/flutter/flutter/issues/93945 + // Rather than skip the test, this changes the expectation, so that there + // is test coverage of the code path, even though the actual backgrounding + // doesn't happen. This is especially important for macOS, which may need to + // share generated code with iOS, falling back to the main thread since + // background is not supported. + final bool taskQueuesSupported = + defaultTargetPlatform == TargetPlatform.android || + defaultTargetPlatform == TargetPlatform.iOS; + expect(await api.taskQueueIsBackgroundThread(), taskQueuesSupported); + }); + /// Event channels const List eventChannelSupported = [ diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart index 234361ada5a..7a42a15724b 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/core_tests.gen.dart @@ -3539,6 +3539,68 @@ class HostIntegrationCoreApi { } } + /// Returns true if the handler is run on a main thread, which should be + /// true since there is no TaskQueue annotation. + Future defaultIsMainThread() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.defaultIsMainThread$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } + + /// Returns true if the handler is run on a non-main thread, which should be + /// true for any platform with TaskQueue support. + Future taskQueueIsBackgroundThread() async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.taskQueueIsBackgroundThread$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } + Future callFlutterNoop() async { final String pigeonVar_channelName = 'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterNoop$pigeonVar_messageChannelSuffix'; diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt index c5ba2d8b0ca..467de3c5f29 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt @@ -36,6 +36,19 @@ private fun createConnectionError(channelName: String): FlutterError { "channel-error", "Unable to establish connection on channel: '$channelName'.", "") } +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() + enum class AnEnum(val raw: Int) { ONE(0), TWO(1), @@ -890,6 +903,16 @@ interface HostIntegrationCoreApi { anotherEnum: AnotherEnum?, callback: (Result) -> Unit ) + /** + * Returns true if the handler is run on a main thread, which should be true since there is no + * TaskQueue annotation. + */ + fun defaultIsMainThread(): Boolean + /** + * Returns true if the handler is run on a non-main thread, which should be true for any platform + * with TaskQueue support. + */ + fun taskQueueIsBackgroundThread(): Boolean fun callFlutterNoop(callback: (Result) -> Unit) @@ -1099,6 +1122,7 @@ interface HostIntegrationCoreApi { ) { val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val taskQueue = binaryMessenger.makeBackgroundTaskQueue() run { val channel = BasicMessageChannel( @@ -3373,6 +3397,47 @@ interface HostIntegrationCoreApi { channel.setMessageHandler(null) } } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.defaultIsMainThread$separatedMessageChannelSuffix", + codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = + try { + listOf(api.defaultIsMainThread()) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.taskQueueIsBackgroundThread$separatedMessageChannelSuffix", + codec, + taskQueue) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = + try { + listOf(api.taskQueueIsBackgroundThread()) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel( diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt index 3177d6cdc8d..7c6707c25a4 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt @@ -526,6 +526,14 @@ class TestPlugin : FlutterPlugin, HostIntegrationCoreApi { callback(Result.success(anotherEnum)) } + override fun defaultIsMainThread(): Boolean { + return Thread.currentThread() == Looper.getMainLooper().getThread() + } + + override fun taskQueueIsBackgroundThread(): Boolean { + return Thread.currentThread() != Looper.getMainLooper().getThread() + } + override fun callFlutterNoop(callback: (Result) -> Unit) { flutterApi!!.noop { callback(Result.success(Unit)) } } diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift index 4647a66d1aa..b4c2173b388 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift @@ -967,6 +967,12 @@ protocol HostIntegrationCoreApi { /// Returns the passed enum, to test asynchronous serialization and deserialization. func echoAsyncNullable( _ anotherEnum: AnotherEnum?, completion: @escaping (Result) -> Void) + /// Returns true if the handler is run on a main thread, which should be + /// true since there is no TaskQueue annotation. + func defaultIsMainThread() throws -> Bool + /// Returns true if the handler is run on a non-main thread, which should be + /// true for any platform with TaskQueue support. + func taskQueueIsBackgroundThread() throws -> Bool func callFlutterNoop(completion: @escaping (Result) -> Void) func callFlutterThrowError(completion: @escaping (Result) -> Void) func callFlutterThrowErrorFromVoid(completion: @escaping (Result) -> Void) @@ -1089,6 +1095,11 @@ class HostIntegrationCoreApiSetup { messageChannelSuffix: String = "" ) { let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + #if os(iOS) + let taskQueue = binaryMessenger.makeBackgroundTaskQueue?() + #else + let taskQueue: FlutterTaskQueue? = nil + #endif /// A no-op function taking no arguments and returning no value, to sanity /// test basic calling. let noopChannel = FlutterBasicMessageChannel( @@ -3059,6 +3070,49 @@ class HostIntegrationCoreApiSetup { } else { echoAnotherAsyncNullableEnumChannel.setMessageHandler(nil) } + /// Returns true if the handler is run on a main thread, which should be + /// true since there is no TaskQueue annotation. + let defaultIsMainThreadChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.defaultIsMainThread\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + defaultIsMainThreadChannel.setMessageHandler { _, reply in + do { + let result = try api.defaultIsMainThread() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + defaultIsMainThreadChannel.setMessageHandler(nil) + } + /// Returns true if the handler is run on a non-main thread, which should be + /// true for any platform with TaskQueue support. + let taskQueueIsBackgroundThreadChannel = + taskQueue == nil + ? FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.taskQueueIsBackgroundThread\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) + : FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.taskQueueIsBackgroundThread\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue) + + if let api = api { + taskQueueIsBackgroundThreadChannel.setMessageHandler { _, reply in + do { + let result = try api.taskQueueIsBackgroundThread() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + taskQueueIsBackgroundThreadChannel.setMessageHandler(nil) + } let callFlutterNoopChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterNoop\(channelSuffix)", diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift index ddfba5c6d1e..5059fedfeaf 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift @@ -514,6 +514,14 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { completion(.success(anotherEnum)) } + func defaultIsMainThread() -> Bool { + return Thread.isMainThread + } + + func taskQueueIsBackgroundThread() -> Bool { + return !Thread.isMainThread + } + func callFlutterNoop(completion: @escaping (Result) -> Void) { flutterAPI.noop { response in switch response { diff --git a/packages/pigeon/platform_tests/test_plugin/linux/pigeon/core_tests.gen.cc b/packages/pigeon/platform_tests/test_plugin/linux/pigeon/core_tests.gen.cc index 8dee0b40e5e..2f2c55132f8 100644 --- a/packages/pigeon/platform_tests/test_plugin/linux/pigeon/core_tests.gen.cc +++ b/packages/pigeon/platform_tests/test_plugin/linux/pigeon/core_tests.gen.cc @@ -9725,6 +9725,140 @@ core_tests_pigeon_test_host_integration_core_api_echo_another_async_nullable_enu return self; } +struct _CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse { + GObject parent_instance; + + FlValue* value; +}; + +G_DEFINE_TYPE( + CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse, + core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response, + G_TYPE_OBJECT) + +static void +core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_dispose( + GObject* object) { + CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse* self = + CORE_TESTS_PIGEON_TEST_HOST_INTEGRATION_CORE_API_DEFAULT_IS_MAIN_THREAD_RESPONSE( + object); + g_clear_pointer(&self->value, fl_value_unref); + G_OBJECT_CLASS( + core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_parent_class) + ->dispose(object); +} + +static void +core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_init( + CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse* + self) {} + +static void +core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_class_init( + CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponseClass* + klass) { + G_OBJECT_CLASS(klass)->dispose = + core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_dispose; +} + +CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse* +core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_new( + gboolean return_value) { + CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse* self = + CORE_TESTS_PIGEON_TEST_HOST_INTEGRATION_CORE_API_DEFAULT_IS_MAIN_THREAD_RESPONSE( + g_object_new( + core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_get_type(), + nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_bool(return_value)); + return self; +} + +CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse* +core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_new_error( + const gchar* code, const gchar* message, FlValue* details) { + CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse* self = + CORE_TESTS_PIGEON_TEST_HOST_INTEGRATION_CORE_API_DEFAULT_IS_MAIN_THREAD_RESPONSE( + g_object_new( + core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_get_type(), + nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_string(code)); + fl_value_append_take(self->value, + fl_value_new_string(message != nullptr ? message : "")); + fl_value_append_take(self->value, details != nullptr ? fl_value_ref(details) + : fl_value_new_null()); + return self; +} + +struct + _CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse { + GObject parent_instance; + + FlValue* value; +}; + +G_DEFINE_TYPE( + CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse, + core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response, + G_TYPE_OBJECT) + +static void +core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_dispose( + GObject* object) { + CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse* + self = + CORE_TESTS_PIGEON_TEST_HOST_INTEGRATION_CORE_API_TASK_QUEUE_IS_BACKGROUND_THREAD_RESPONSE( + object); + g_clear_pointer(&self->value, fl_value_unref); + G_OBJECT_CLASS( + core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_parent_class) + ->dispose(object); +} + +static void +core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_init( + CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse* + self) {} + +static void +core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_class_init( + CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponseClass* + klass) { + G_OBJECT_CLASS(klass)->dispose = + core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_dispose; +} + +CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse* +core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_new( + gboolean return_value) { + CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse* self = + CORE_TESTS_PIGEON_TEST_HOST_INTEGRATION_CORE_API_TASK_QUEUE_IS_BACKGROUND_THREAD_RESPONSE( + g_object_new( + core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_get_type(), + nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_bool(return_value)); + return self; +} + +CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse* +core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_new_error( + const gchar* code, const gchar* message, FlValue* details) { + CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse* self = + CORE_TESTS_PIGEON_TEST_HOST_INTEGRATION_CORE_API_TASK_QUEUE_IS_BACKGROUND_THREAD_RESPONSE( + g_object_new( + core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_get_type(), + nullptr)); + self->value = fl_value_new_list(); + fl_value_append_take(self->value, fl_value_new_string(code)); + fl_value_append_take(self->value, + fl_value_new_string(message != nullptr ? message : "")); + fl_value_append_take(self->value, details != nullptr ? fl_value_ref(details) + : fl_value_new_null()); + return self; +} + G_DECLARE_FINAL_TYPE( CoreTestsPigeonTestHostIntegrationCoreApiCallFlutterNoopResponse, core_tests_pigeon_test_host_integration_core_api_call_flutter_noop_response, @@ -16327,6 +16461,64 @@ core_tests_pigeon_test_host_integration_core_api_echo_another_async_nullable_enu self->user_data); } +static void +core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_cb( + FlBasicMessageChannel* channel, FlValue* message_, + FlBasicMessageChannelResponseHandle* response_handle, gpointer user_data) { + CoreTestsPigeonTestHostIntegrationCoreApi* self = + CORE_TESTS_PIGEON_TEST_HOST_INTEGRATION_CORE_API(user_data); + + if (self->vtable == nullptr || + self->vtable->default_is_main_thread == nullptr) { + return; + } + + g_autoptr( + CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse) + response = self->vtable->default_is_main_thread(self->user_data); + if (response == nullptr) { + g_warning("No response returned to %s.%s", "HostIntegrationCoreApi", + "defaultIsMainThread"); + return; + } + + g_autoptr(GError) error = NULL; + if (!fl_basic_message_channel_respond(channel, response_handle, + response->value, &error)) { + g_warning("Failed to send response to %s.%s: %s", "HostIntegrationCoreApi", + "defaultIsMainThread", error->message); + } +} + +static void +core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_cb( + FlBasicMessageChannel* channel, FlValue* message_, + FlBasicMessageChannelResponseHandle* response_handle, gpointer user_data) { + CoreTestsPigeonTestHostIntegrationCoreApi* self = + CORE_TESTS_PIGEON_TEST_HOST_INTEGRATION_CORE_API(user_data); + + if (self->vtable == nullptr || + self->vtable->task_queue_is_background_thread == nullptr) { + return; + } + + g_autoptr( + CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse) + response = self->vtable->task_queue_is_background_thread(self->user_data); + if (response == nullptr) { + g_warning("No response returned to %s.%s", "HostIntegrationCoreApi", + "taskQueueIsBackgroundThread"); + return; + } + + g_autoptr(GError) error = NULL; + if (!fl_basic_message_channel_respond(channel, response_handle, + response->value, &error)) { + g_warning("Failed to send response to %s.%s: %s", "HostIntegrationCoreApi", + "taskQueueIsBackgroundThread", error->message); + } +} + static void core_tests_pigeon_test_host_integration_core_api_call_flutter_noop_cb( FlBasicMessageChannel* channel, FlValue* message_, @@ -18641,6 +18833,31 @@ void core_tests_pigeon_test_host_integration_core_api_set_method_handlers( echo_another_async_nullable_enum_channel, core_tests_pigeon_test_host_integration_core_api_echo_another_async_nullable_enum_cb, g_object_ref(api_data), g_object_unref); + g_autofree gchar* default_is_main_thread_channel_name = g_strdup_printf( + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + "defaultIsMainThread%s", + dot_suffix); + g_autoptr(FlBasicMessageChannel) default_is_main_thread_channel = + fl_basic_message_channel_new(messenger, + default_is_main_thread_channel_name, + FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler( + default_is_main_thread_channel, + core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_cb, + g_object_ref(api_data), g_object_unref); + g_autofree gchar* task_queue_is_background_thread_channel_name = + g_strdup_printf( + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + "taskQueueIsBackgroundThread%s", + dot_suffix); + g_autoptr(FlBasicMessageChannel) task_queue_is_background_thread_channel = + fl_basic_message_channel_new(messenger, + task_queue_is_background_thread_channel_name, + FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler( + task_queue_is_background_thread_channel, + core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_cb, + g_object_ref(api_data), g_object_unref); g_autofree gchar* call_flutter_noop_channel_name = g_strdup_printf( "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterNoop%s", @@ -20317,6 +20534,27 @@ void core_tests_pigeon_test_host_integration_core_api_clear_method_handlers( FL_MESSAGE_CODEC(codec)); fl_basic_message_channel_set_message_handler( echo_another_async_nullable_enum_channel, nullptr, nullptr, nullptr); + g_autofree gchar* default_is_main_thread_channel_name = g_strdup_printf( + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + "defaultIsMainThread%s", + dot_suffix); + g_autoptr(FlBasicMessageChannel) default_is_main_thread_channel = + fl_basic_message_channel_new(messenger, + default_is_main_thread_channel_name, + FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler(default_is_main_thread_channel, + nullptr, nullptr, nullptr); + g_autofree gchar* task_queue_is_background_thread_channel_name = + g_strdup_printf( + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + "taskQueueIsBackgroundThread%s", + dot_suffix); + g_autoptr(FlBasicMessageChannel) task_queue_is_background_thread_channel = + fl_basic_message_channel_new(messenger, + task_queue_is_background_thread_channel_name, + FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler( + task_queue_is_background_thread_channel, nullptr, nullptr, nullptr); g_autofree gchar* call_flutter_noop_channel_name = g_strdup_printf( "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." "callFlutterNoop%s", diff --git a/packages/pigeon/platform_tests/test_plugin/linux/pigeon/core_tests.gen.h b/packages/pigeon/platform_tests/test_plugin/linux/pigeon/core_tests.gen.h index dcd6cac75f5..8d33236b071 100644 --- a/packages/pigeon/platform_tests/test_plugin/linux/pigeon/core_tests.gen.h +++ b/packages/pigeon/platform_tests/test_plugin/linux/pigeon/core_tests.gen.h @@ -3448,6 +3448,73 @@ CoreTestsPigeonTestHostIntegrationCoreApiEchoNamedNullableStringResponse* core_tests_pigeon_test_host_integration_core_api_echo_named_nullable_string_response_new_error( const gchar* code, const gchar* message, FlValue* details); +G_DECLARE_FINAL_TYPE( + CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse, + core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response, + CORE_TESTS_PIGEON_TEST, + HOST_INTEGRATION_CORE_API_DEFAULT_IS_MAIN_THREAD_RESPONSE, GObject) + +/** + * core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_new: + * + * Creates a new response to HostIntegrationCoreApi.defaultIsMainThread. + * + * Returns: a new + * #CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse + */ +CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse* +core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_new( + gboolean return_value); + +/** + * core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_new_error: + * @code: error code. + * @message: error message. + * @details: (allow-none): error details or %NULL. + * + * Creates a new error response to HostIntegrationCoreApi.defaultIsMainThread. + * + * Returns: a new + * #CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse + */ +CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse* +core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_new_error( + const gchar* code, const gchar* message, FlValue* details); + +G_DECLARE_FINAL_TYPE( + CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse, + core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response, + CORE_TESTS_PIGEON_TEST, + HOST_INTEGRATION_CORE_API_TASK_QUEUE_IS_BACKGROUND_THREAD_RESPONSE, GObject) + +/** + * core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_new: + * + * Creates a new response to HostIntegrationCoreApi.taskQueueIsBackgroundThread. + * + * Returns: a new + * #CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse + */ +CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse* +core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_new( + gboolean return_value); + +/** + * core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_new_error: + * @code: error code. + * @message: error message. + * @details: (allow-none): error details or %NULL. + * + * Creates a new error response to + * HostIntegrationCoreApi.taskQueueIsBackgroundThread. + * + * Returns: a new + * #CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse + */ +CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse* +core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_new_error( + const gchar* code, const gchar* message, FlValue* details); + /** * CoreTestsPigeonTestHostIntegrationCoreApiVTable: * @@ -3753,6 +3820,10 @@ typedef struct { CoreTestsPigeonTestAnotherEnum* another_enum, CoreTestsPigeonTestHostIntegrationCoreApiResponseHandle* response_handle, gpointer user_data); + CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse* ( + *default_is_main_thread)(gpointer user_data); + CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse* ( + *task_queue_is_background_thread)(gpointer user_data); void (*call_flutter_noop)( CoreTestsPigeonTestHostIntegrationCoreApiResponseHandle* response_handle, gpointer user_data); diff --git a/packages/pigeon/platform_tests/test_plugin/linux/test_plugin.cc b/packages/pigeon/platform_tests/test_plugin/linux/test_plugin.cc index 14938e9980f..6f72865481d 100644 --- a/packages/pigeon/platform_tests/test_plugin/linux/test_plugin.cc +++ b/packages/pigeon/platform_tests/test_plugin/linux/test_plugin.cc @@ -9,6 +9,7 @@ #include #include +#include #include "pigeon/core_tests.gen.h" #include "test_plugin_private.h" @@ -24,6 +25,8 @@ struct _TestPlugin { CoreTestsPigeonTestFlutterSmallApi* flutter_small_api_two; GCancellable* cancellable; + + std::thread::id main_thread_id; }; G_DEFINE_TYPE(TestPlugin, test_plugin, G_TYPE_OBJECT) @@ -789,6 +792,20 @@ static void echo_another_async_nullable_enum( response_handle, another_enum); } +static CoreTestsPigeonTestHostIntegrationCoreApiDefaultIsMainThreadResponse* +default_is_main_thread(gpointer user_data) { + TestPlugin* self = TEST_PLUGIN(user_data); + return core_tests_pigeon_test_host_integration_core_api_default_is_main_thread_response_new( + std::this_thread::get_id() == self->main_thread_id); +} + +static CoreTestsPigeonTestHostIntegrationCoreApiTaskQueueIsBackgroundThreadResponse* +task_queue_is_background_thread(gpointer user_data) { + TestPlugin* self = TEST_PLUGIN(user_data); + return core_tests_pigeon_test_host_integration_core_api_task_queue_is_background_thread_response_new( + std::this_thread::get_id() != self->main_thread_id); +} + static void noop_cb(GObject* object, GAsyncResult* result, gpointer user_data) { g_autoptr(CallbackData) data = static_cast(user_data); @@ -3280,6 +3297,8 @@ static CoreTestsPigeonTestHostIntegrationCoreApiVTable host_core_api_vtable = { .echo_async_nullable_class_map = echo_async_nullable_class_map, .echo_async_nullable_enum = echo_async_nullable_enum, .echo_another_async_nullable_enum = echo_another_async_nullable_enum, + .default_is_main_thread = default_is_main_thread, + .task_queue_is_background_thread = task_queue_is_background_thread, .call_flutter_noop = call_flutter_noop, .call_flutter_throw_error = call_flutter_throw_error, .call_flutter_throw_error_from_void = call_flutter_throw_error_from_void, @@ -3416,6 +3435,8 @@ static TestPlugin* test_plugin_new(FlBinaryMessenger* messenger) { self->flutter_small_api_two = core_tests_pigeon_test_flutter_small_api_new(messenger, "suffixTwo"); + self->main_thread_id = std::this_thread::get_id(); + return self; } diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift index 4647a66d1aa..b4c2173b388 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift @@ -967,6 +967,12 @@ protocol HostIntegrationCoreApi { /// Returns the passed enum, to test asynchronous serialization and deserialization. func echoAsyncNullable( _ anotherEnum: AnotherEnum?, completion: @escaping (Result) -> Void) + /// Returns true if the handler is run on a main thread, which should be + /// true since there is no TaskQueue annotation. + func defaultIsMainThread() throws -> Bool + /// Returns true if the handler is run on a non-main thread, which should be + /// true for any platform with TaskQueue support. + func taskQueueIsBackgroundThread() throws -> Bool func callFlutterNoop(completion: @escaping (Result) -> Void) func callFlutterThrowError(completion: @escaping (Result) -> Void) func callFlutterThrowErrorFromVoid(completion: @escaping (Result) -> Void) @@ -1089,6 +1095,11 @@ class HostIntegrationCoreApiSetup { messageChannelSuffix: String = "" ) { let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + #if os(iOS) + let taskQueue = binaryMessenger.makeBackgroundTaskQueue?() + #else + let taskQueue: FlutterTaskQueue? = nil + #endif /// A no-op function taking no arguments and returning no value, to sanity /// test basic calling. let noopChannel = FlutterBasicMessageChannel( @@ -3059,6 +3070,49 @@ class HostIntegrationCoreApiSetup { } else { echoAnotherAsyncNullableEnumChannel.setMessageHandler(nil) } + /// Returns true if the handler is run on a main thread, which should be + /// true since there is no TaskQueue annotation. + let defaultIsMainThreadChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.defaultIsMainThread\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + defaultIsMainThreadChannel.setMessageHandler { _, reply in + do { + let result = try api.defaultIsMainThread() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + defaultIsMainThreadChannel.setMessageHandler(nil) + } + /// Returns true if the handler is run on a non-main thread, which should be + /// true for any platform with TaskQueue support. + let taskQueueIsBackgroundThreadChannel = + taskQueue == nil + ? FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.taskQueueIsBackgroundThread\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec) + : FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.taskQueueIsBackgroundThread\(channelSuffix)", + binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue) + + if let api = api { + taskQueueIsBackgroundThreadChannel.setMessageHandler { _, reply in + do { + let result = try api.taskQueueIsBackgroundThread() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + taskQueueIsBackgroundThreadChannel.setMessageHandler(nil) + } let callFlutterNoopChannel = FlutterBasicMessageChannel( name: "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterNoop\(channelSuffix)", diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift index c23056111ed..295a6440875 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift @@ -513,6 +513,14 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { completion(.success(anotherEnum)) } + func defaultIsMainThread() -> Bool { + return Thread.isMainThread + } + + func taskQueueIsBackgroundThread() -> Bool { + return !Thread.isMainThread + } + func callFlutterNoop(completion: @escaping (Result) -> Void) { flutterAPI.noop { response in switch response { diff --git a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp index c47e3a16e2e..8fd4949f06e 100644 --- a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp +++ b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.cpp @@ -6049,6 +6049,61 @@ void HostIntegrationCoreApi::SetUp(flutter::BinaryMessenger* binary_messenger, channel.SetMessageHandler(nullptr); } } + { + BasicMessageChannel<> channel(binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests." + "HostIntegrationCoreApi.defaultIsMainThread" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + ErrorOr output = api->DefaultIsMainThread(); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } + { + BasicMessageChannel<> channel( + binary_messenger, + "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi." + "taskQueueIsBackgroundThread" + + prepended_suffix, + &GetCodec()); + if (api != nullptr) { + channel.SetMessageHandler( + [api](const EncodableValue& message, + const flutter::MessageReply& reply) { + try { + ErrorOr output = api->TaskQueueIsBackgroundThread(); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + wrapped.push_back(EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel.SetMessageHandler(nullptr); + } + } { BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.pigeon_integration_tests." diff --git a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h index 7f19e1a19cb..7455ab574be 100644 --- a/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h +++ b/packages/pigeon/platform_tests/test_plugin/windows/pigeon/core_tests.gen.h @@ -1156,6 +1156,12 @@ class HostIntegrationCoreApi { const AnotherEnum* another_enum, std::function> reply)> result) = 0; + // Returns true if the handler is run on a main thread, which should be + // true since there is no TaskQueue annotation. + virtual ErrorOr DefaultIsMainThread() = 0; + // Returns true if the handler is run on a non-main thread, which should be + // true for any platform with TaskQueue support. + virtual ErrorOr TaskQueueIsBackgroundThread() = 0; virtual void CallFlutterNoop( std::function reply)> result) = 0; virtual void CallFlutterThrowError( diff --git a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp index c6f69c0a8c0..470507a9024 100644 --- a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp +++ b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "pigeon/core_tests.gen.h" @@ -77,7 +78,8 @@ TestPlugin::TestPlugin(flutter::BinaryMessenger* binary_messenger, host_small_api_one_(std::move(host_small_api_one)), host_small_api_two_(std::move(host_small_api_two)), flutter_api_( - std::make_unique(binary_messenger)) {} + std::make_unique(binary_messenger)), + main_thread_id_(std::this_thread::get_id()) {} TestPlugin::~TestPlugin() {} @@ -711,6 +713,14 @@ void TestPlugin::EchoAnotherAsyncNullableEnum( : std::nullopt); } +ErrorOr TestPlugin::DefaultIsMainThread() { + return std::this_thread::get_id() == main_thread_id_; +} + +ErrorOr TestPlugin::TaskQueueIsBackgroundThread() { + return std::this_thread::get_id() != main_thread_id_; +} + void TestPlugin::CallFlutterNoop( std::function reply)> result) { flutter_api_->Noop([result]() { result(std::nullopt); }, diff --git a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h index cabbe665ed5..74fa192257e 100644 --- a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h +++ b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "pigeon/core_tests.gen.h" @@ -393,6 +394,8 @@ class TestPlugin : public flutter::Plugin, std::optional> reply)> result) override; + core_tests_pigeontest::ErrorOr DefaultIsMainThread() override; + core_tests_pigeontest::ErrorOr TaskQueueIsBackgroundThread() override; void CallFlutterNoop( std::function< void(std::optional reply)> @@ -679,6 +682,7 @@ class TestPlugin : public flutter::Plugin, flutter_small_api_two_; std::unique_ptr host_small_api_one_; std::unique_ptr host_small_api_two_; + std::thread::id main_thread_id_; }; } // namespace test_plugin diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index 3fb7a94a35e..6f85a9899b2 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. repository: https://github.com/flutter/packages/tree/main/packages/pigeon issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22 -version: 24.1.1 # This must match the version in lib/src/generator_tools.dart +version: 24.2.0 # This must match the version in lib/src/generator_tools.dart environment: sdk: ^3.4.0 diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 12b85a4973e..7e33c7b3fab 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -86,7 +86,6 @@ Future generateTestPigeons( // TODO(stuartmorgan): Make this dynamic rather than hard-coded. Or eliminate // it entirely; see https://github.com/flutter/flutter/issues/115169. const Set inputs = { - 'background_platform_channels', 'core_tests', 'enum', 'event_channel_tests', @@ -113,14 +112,14 @@ Future generateTestPigeons( _unsupportedFiles[input] ?? {}; final bool kotlinErrorClassGenerationTestFiles = - input == 'core_tests' || input == 'background_platform_channels'; + input == 'core_tests' || input == 'primitive'; final String kotlinErrorName = kotlinErrorClassGenerationTestFiles ? 'FlutterError' : '${pascalCaseName}Error'; final bool swiftErrorUseDefaultErrorName = - input == 'core_tests' || input == 'background_platform_channels'; + input == 'core_tests' || input == 'primitive'; final String? swiftErrorClassName = swiftErrorUseDefaultErrorName ? null : '${pascalCaseName}Error'; @@ -140,13 +139,13 @@ Future generateTestPigeons( : '$outputBase/android/src/main/kotlin/com/example/test_plugin/$pascalCaseName.gen.kt', kotlinPackage: 'com.example.test_plugin', kotlinErrorClassName: kotlinErrorName, - kotlinIncludeErrorClass: input != 'core_tests', + kotlinIncludeErrorClass: input != 'primitive', // iOS swiftOut: skipLanguages.contains(GeneratorLanguage.swift) ? null : '$outputBase/ios/Classes/$pascalCaseName.gen.swift', swiftErrorClassName: swiftErrorClassName, - swiftIncludeErrorClass: input != 'background_platform_channels', + swiftIncludeErrorClass: input != 'primitive', // Linux gobjectHeaderOut: skipLanguages.contains(GeneratorLanguage.gobject) ? null @@ -178,7 +177,7 @@ Future generateTestPigeons( ? null : '$outputBase/macos/Classes/$pascalCaseName.gen.swift', swiftErrorClassName: swiftErrorClassName, - swiftIncludeErrorClass: input != 'background_platform_channels', + swiftIncludeErrorClass: input != 'primitive', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', injectOverflowTypes: includeOverflow && input == 'core_tests',