[pigeon] main actor async flutter api (#12729)

adds main actor annotation to async flutter api methods in swift.

fixes https://github.com/flutter/flutter/issues/192199
diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index 9c9a718..4d64c91 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 29.0.1
+
+* [swift] Annotates async `FlutterApi` methods with `@MainActor`.
+
 ## 29.0.0
 
 * [swift] [kotlin] Adds experimental support for FFI and JNI.
diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift
index 0e6ed89..5f0af5f 100644
--- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift
+++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift
@@ -373,7 +373,7 @@
 
 /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift.
 protocol MessageFlutterApiProtocol {
-  func flutterMethod(aString aStringArg: String?) async throws -> String
+  @MainActor func flutterMethod(aString aStringArg: String?) async throws -> String
 }
 class MessageFlutterApi: MessageFlutterApiProtocol {
   private let binaryMessenger: FlutterBinaryMessenger
@@ -385,7 +385,7 @@
   var codec: MessagesPigeonCodec {
     return MessagesPigeonCodec.shared
   }
-  func flutterMethod(aString aStringArg: String?) async throws -> String {
+  @MainActor func flutterMethod(aString aStringArg: String?) async throws -> String {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_example_package.MessageFlutterApi.flutterMethod\(messageChannelSuffix)"
diff --git a/packages/pigeon/lib/src/generator_tools.dart b/packages/pigeon/lib/src/generator_tools.dart
index 3aa7095..3d2d8bb 100644
--- a/packages/pigeon/lib/src/generator_tools.dart
+++ b/packages/pigeon/lib/src/generator_tools.dart
@@ -16,7 +16,7 @@
 /// The current version of pigeon.
 ///
 /// This must match the version in pubspec.yaml.
-const String pigeonVersion = '29.0.0';
+const String pigeonVersion = '29.0.1';
 
 /// Default plugin package name.
 const String defaultPluginPackageName = 'dev.flutter.pigeon';
diff --git a/packages/pigeon/lib/src/swift/swift_generator.dart b/packages/pigeon/lib/src/swift/swift_generator.dart
index cd1c83c..c744a05 100644
--- a/packages/pigeon/lib/src/swift/swift_generator.dart
+++ b/packages/pigeon/lib/src/swift/swift_generator.dart
@@ -1263,6 +1263,7 @@
             errorTypeName: _getErrorClassName(generatorOptions),
             isAsynchronous: true,
             isAsynchronousCallback: func.isAsynchronousCallback,
+            isMainActor: !func.isAsynchronousCallback,
             swiftFunction: func.swiftFunction,
             getParameterName: _getSafeArgumentName,
           ),
@@ -2614,6 +2615,7 @@
       errorTypeName: _getErrorClassName(generatorOptions),
       isAsynchronous: isAsynchronous,
       isAsynchronousCallback: isAsynchronousCallback,
+      isMainActor: isAsynchronous && !isAsynchronousCallback,
       swiftFunction: swiftFunction,
       getParameterName: _getSafeArgumentName,
     );
@@ -3963,6 +3965,7 @@
   bool isAsynchronous = false,
   bool ffiUserApi = false,
   bool isAsynchronousCallback = false,
+  bool isMainActor = false,
   String? swiftFunction,
   bool ffiBridgeApi = false,
   _SwiftFunctionComponents? components,
@@ -4026,8 +4029,9 @@
   }
 
   if (isAsynchronous && !isAsynchronousCallback) {
+    final mainActorPrefix = isMainActor ? '@MainActor ' : '';
     final returnTypeSuffix = returnType.isVoid ? '' : ' -> $returnTypeString';
-    return 'func $methodName($parameterSignature) async throws$returnTypeSuffix';
+    return '${mainActorPrefix}func ${components.name}($parameterSignature) async throws$returnTypeSuffix';
   }
 
   if (isAsynchronous) {
diff --git a/packages/pigeon/pigeons/core_tests.dart b/packages/pigeon/pigeons/core_tests.dart
index e7ff2cc..09af71b 100644
--- a/packages/pigeon/pigeons/core_tests.dart
+++ b/packages/pigeon/pigeons/core_tests.dart
@@ -1159,6 +1159,9 @@
 
   @async
   void callFlutterCallbackThrowErrorFromVoid();
+
+  @async
+  bool callFlutterIsAsyncFlutterApiOnRoot();
 }
 
 /// A Flutter API using callback-based asynchronous methods (@asyncCallback).
@@ -1447,9 +1450,6 @@
   AnotherEnum? echoAnotherNullableEnum(AnotherEnum? anotherEnum);
 
   // ========== Async tests ==========
-  // These are minimal since async FlutterApi only changes Dart generation.
-  // Currently they aren't integration tested, but having them here ensures
-  // analysis coverage.
 
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic asynchronous calling.
@@ -1461,6 +1461,10 @@
   @ObjCSelector('echoAsyncString:')
   @SwiftFunction('echoAsync(_:)')
   String echoAsyncString(String aString);
+
+  /// Returns true if the async FlutterApi method is run on the root isolate.
+  @async
+  bool isAsyncFlutterApiOnRoot();
 }
 
 /// A Host API using callback-based asynchronous methods (@asyncCallback).
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 ac77242..0f19d40 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
@@ -1134,6 +1134,31 @@
     flutterCallbackApi.throwErrorFromVoid(result);
   }
 
+  @Override
+  public void callFlutterIsAsyncFlutterApiOnRoot(@NonNull Result<Boolean> result) {
+    if (Looper.myLooper() != Looper.getMainLooper()) {
+      result.success(false);
+      return;
+    }
+    assert flutterApi != null;
+    flutterApi.isAsyncFlutterApiOnRoot(
+        new Result<Boolean>() {
+          @Override
+          public void success(Boolean res) {
+            if (Looper.myLooper() != Looper.getMainLooper()) {
+              result.success(false);
+              return;
+            }
+            result.success(res);
+          }
+
+          @Override
+          public void error(@NonNull Throwable error) {
+            result.error(error);
+          }
+        });
+  }
+
   public @NonNull CoreTests.UnusedClass testIfUnusedClassIsGenerated() {
     return new CoreTests.UnusedClass();
   }
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 98961f9..484eb44 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
@@ -4046,6 +4046,8 @@
 
     void callFlutterCallbackThrowErrorFromVoid(@NonNull VoidResult result);
 
+    void callFlutterIsAsyncFlutterApiOnRoot(@NonNull Result<Boolean> result);
+
     /** The codec used by HostIntegrationCoreApi. */
     static @NonNull MessageCodec<Object> getCodec() {
       return PigeonCodec.INSTANCE;
@@ -8845,6 +8847,36 @@
           channel.setMessageHandler(null);
         }
       }
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger,
+                "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterIsAsyncFlutterApiOnRoot"
+                    + messageChannelSuffix,
+                getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                ArrayList<Object> wrapped = new ArrayList<>();
+                Result<Boolean> resultCallback =
+                    new Result<Boolean>() {
+                      public void success(Boolean result) {
+                        wrapped.add(0, result);
+                        reply.reply(wrapped);
+                      }
+
+                      public void error(Throwable error) {
+                        ArrayList<Object> wrappedError = wrapError(error);
+                        reply.reply(wrappedError);
+                      }
+                    };
+
+                api.callFlutterIsAsyncFlutterApiOnRoot(resultCallback);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
     }
   }
 
@@ -10624,6 +10656,39 @@
             }
           });
     }
+
+    /** Returns true if the async FlutterApi method is run on the root isolate. */
+    public void isAsyncFlutterApiOnRoot(@NonNull Result<Boolean> result) {
+      final String channelName =
+          "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.isAsyncFlutterApiOnRoot"
+              + messageChannelSuffix;
+      BasicMessageChannel<Object> channel =
+          new BasicMessageChannel<>(binaryMessenger, channelName, getCodec());
+      channel.send(
+          null,
+          channelReply -> {
+            if (channelReply instanceof List) {
+              List<Object> listReply = (List<Object>) channelReply;
+              if (listReply.size() > 1) {
+                result.error(
+                    new FlutterError(
+                        (String) listReply.get(0), (String) listReply.get(1), listReply.get(2)));
+              } else if (listReply.get(0) == null) {
+                result.error(
+                    new FlutterError(
+                        "null-error",
+                        "Flutter api returned null value for non-null return value.",
+                        ""));
+              } else {
+                @SuppressWarnings("ConstantConditions")
+                Boolean output = (Boolean) listReply.get(0);
+                result.success(output);
+              }
+            } else {
+              result.error(createConnectionError(channelName));
+            }
+          });
+    }
   }
 
   /**
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/darwin/alternate_language_test_plugin/Sources/alternate_language_test_plugin/AlternateLanguageTestPlugin.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/darwin/alternate_language_test_plugin/Sources/alternate_language_test_plugin/AlternateLanguageTestPlugin.m
index d1df05a..c0caa4b 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/darwin/alternate_language_test_plugin/Sources/alternate_language_test_plugin/AlternateLanguageTestPlugin.m
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/darwin/alternate_language_test_plugin/Sources/alternate_language_test_plugin/AlternateLanguageTestPlugin.m
@@ -1247,6 +1247,21 @@
   [self.flutterCallbackAPI throwErrorFromVoidWithCompletion:completion];
 }
 
+- (void)callFlutterIsAsyncFlutterApiOnRootWithCompletion:
+    (void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion {
+  if (![NSThread isMainThread]) {
+    completion(@NO, nil);
+    return;
+  }
+  [self.flutterAPI isAsyncFlutterApiOnRootWithCompletion:^(NSNumber *result, FlutterError *error) {
+    if (![NSThread isMainThread]) {
+      completion(@NO, nil);
+      return;
+    }
+    completion(result, error);
+  }];
+}
+
 @end
 
 @interface AlternateLanguageTestAPIWithSuffix ()
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/darwin/alternate_language_test_plugin/Sources/alternate_language_test_plugin/CoreTests.gen.m b/packages/pigeon/platform_tests/alternate_language_test_plugin/darwin/alternate_language_test_plugin/Sources/alternate_language_test_plugin/CoreTests.gen.m
index dafdc72..ae93b34 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/darwin/alternate_language_test_plugin/Sources/alternate_language_test_plugin/CoreTests.gen.m
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/darwin/alternate_language_test_plugin/Sources/alternate_language_test_plugin/CoreTests.gen.m
@@ -5579,6 +5579,31 @@
       [channel setMessageHandler:nil];
     }
   }
+  {
+    FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+           initWithName:[NSString stringWithFormat:
+                                      @"%@%@",
+                                      @"dev.flutter.pigeon.pigeon_integration_tests."
+                                      @"HostIntegrationCoreApi.callFlutterIsAsyncFlutterApiOnRoot",
+                                      messageChannelSuffix]
+        binaryMessenger:binaryMessenger
+                  codec:FLTGetCoreTestsCodec()];
+    if (api) {
+      NSCAssert(
+          [api respondsToSelector:@selector(callFlutterIsAsyncFlutterApiOnRootWithCompletion:)],
+          @"FLTHostIntegrationCoreApi api (%@) doesn't respond to "
+          @"@selector(callFlutterIsAsyncFlutterApiOnRootWithCompletion:)",
+          api);
+      [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+        [api callFlutterIsAsyncFlutterApiOnRootWithCompletion:^(NSNumber *_Nullable output,
+                                                                FlutterError *_Nullable error) {
+          callback(wrapResult(output, error));
+        }];
+      }];
+    } else {
+      [channel setMessageHandler:nil];
+    }
+  }
 }
 @interface FLTFlutterCallbackCoreApi ()
 @property(nonatomic, strong) NSObject<FlutterBinaryMessenger> *binaryMessenger;
@@ -7192,6 +7217,33 @@
                    }
                  }];
 }
+- (void)isAsyncFlutterApiOnRootWithCompletion:(void (^)(NSNumber *_Nullable,
+                                                        FlutterError *_Nullable))completion {
+  NSString *channelName =
+      [NSString stringWithFormat:@"%@%@",
+                                 @"dev.flutter.pigeon.pigeon_integration_tests."
+                                 @"FlutterIntegrationCoreApi.isAsyncFlutterApiOnRoot",
+                                 _messageChannelSuffix];
+  FlutterBasicMessageChannel *channel =
+      [FlutterBasicMessageChannel messageChannelWithName:channelName
+                                         binaryMessenger:self.binaryMessenger
+                                                   codec:FLTGetCoreTestsCodec()];
+  [channel sendMessage:nil
+                 reply:^(NSArray<id> *reply) {
+                   if (reply != nil) {
+                     if (reply.count > 1) {
+                       completion(nil, [FlutterError errorWithCode:reply[0]
+                                                           message:reply[1]
+                                                           details:reply[2]]);
+                     } else {
+                       NSNumber *output = reply[0] == [NSNull null] ? nil : reply[0];
+                       completion(output, nil);
+                     }
+                   } else {
+                     completion(nil, createConnectionError(channelName));
+                   }
+                 }];
+}
 @end
 
 void SetUpFLTHostCallbackCoreApi(id<FlutterBinaryMessenger> binaryMessenger,
diff --git a/packages/pigeon/platform_tests/alternate_language_test_plugin/darwin/alternate_language_test_plugin/Sources/alternate_language_test_plugin/include/alternate_language_test_plugin/CoreTests.gen.h b/packages/pigeon/platform_tests/alternate_language_test_plugin/darwin/alternate_language_test_plugin/Sources/alternate_language_test_plugin/include/alternate_language_test_plugin/CoreTests.gen.h
index 1b1883c..375c361 100644
--- a/packages/pigeon/platform_tests/alternate_language_test_plugin/darwin/alternate_language_test_plugin/Sources/alternate_language_test_plugin/include/alternate_language_test_plugin/CoreTests.gen.h
+++ b/packages/pigeon/platform_tests/alternate_language_test_plugin/darwin/alternate_language_test_plugin/Sources/alternate_language_test_plugin/include/alternate_language_test_plugin/CoreTests.gen.h
@@ -939,6 +939,8 @@
                                                               FlutterError *_Nullable))completion;
 - (void)callFlutterCallbackThrowErrorFromVoidWithCompletion:
     (void (^)(FlutterError *_Nullable))completion;
+- (void)callFlutterIsAsyncFlutterApiOnRootWithCompletion:
+    (void (^)(NSNumber *_Nullable, FlutterError *_Nullable))completion;
 @end
 
 extern void SetUpFLTHostIntegrationCoreApi(id<FlutterBinaryMessenger> binaryMessenger,
@@ -1166,6 +1168,9 @@
 /// Returns the passed in generic Object asynchronously.
 - (void)echoAsyncString:(NSString *)aString
              completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion;
+/// Returns true if the async FlutterApi method is run on the root isolate.
+- (void)isAsyncFlutterApiOnRootWithCompletion:(void (^)(NSNumber *_Nullable,
+                                                        FlutterError *_Nullable))completion;
 @end
 
 /// A Host API using callback-based asynchronous methods (@asyncCallback).
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 24766d1..056bbfa 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
@@ -1681,6 +1681,12 @@
       expect(api.callFlutterNoop(), completes);
     });
 
+    testWidgets('async FlutterApi works on proper thread and isolate', (WidgetTester _) async {
+      final api = HostIntegrationCoreApi();
+
+      expect(await api.callFlutterIsAsyncFlutterApiOnRoot(), isTrue);
+    });
+
     testWidgets('callback basic void->void call works', (WidgetTester _) async {
       final api = HostIntegrationCoreApi();
 
@@ -2631,6 +2637,11 @@
   Future<String> echoAsyncString(String aString) async {
     return aString;
   }
+
+  @override
+  Future<bool> isAsyncFlutterApiOnRoot() async {
+    return RootIsolateToken.instance != null && ServicesBinding.rootIsolateToken != null;
+  }
 }
 
 class _SmallFlutterApi implements FlutterSmallApi {
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 ffc02cc..4f8e2d4 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
@@ -4353,6 +4353,25 @@
 
     _extractReplyValueOrThrow(pigeonVar_replyList, pigeonVar_channelName, isNullValid: true);
   }
+
+  Future<bool> callFlutterIsAsyncFlutterApiOnRoot() async {
+    final pigeonVar_channelName =
+        'dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterIsAsyncFlutterApiOnRoot$pigeonVar_messageChannelSuffix';
+    final pigeonVar_channel = BasicMessageChannel<Object?>(
+      pigeonVar_channelName,
+      pigeonChannelCodec,
+      binaryMessenger: pigeonVar_binaryMessenger,
+    );
+    final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
+    final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
+
+    final Object? pigeonVar_replyValue = _extractReplyValueOrThrow(
+      pigeonVar_replyList,
+      pigeonVar_channelName,
+      isNullValid: false,
+    );
+    return pigeonVar_replyValue! as bool;
+  }
 }
 
 /// A Flutter API using callback-based asynchronous methods (@asyncCallback).
@@ -4647,6 +4666,9 @@
   /// Returns the passed in generic Object asynchronously.
   Future<String> echoAsyncString(String aString);
 
+  /// Returns true if the async FlutterApi method is run on the root isolate.
+  Future<bool> isAsyncFlutterApiOnRoot();
+
   static void setUp(
     FlutterIntegrationCoreApi? api, {
     BinaryMessenger? binaryMessenger,
@@ -5979,6 +6001,29 @@
         });
       }
     }
+    {
+      final pigeonVar_channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.isAsyncFlutterApiOnRoot$messageChannelSuffix',
+        pigeonChannelCodec,
+        binaryMessenger: binaryMessenger,
+      );
+      if (api == null) {
+        pigeonVar_channel.setMessageHandler(null);
+      } else {
+        pigeonVar_channel.setMessageHandler((Object? message) async {
+          try {
+            final bool output = await api.isAsyncFlutterApiOnRoot();
+            return wrapResponse(result: output);
+          } on PlatformException catch (e) {
+            return wrapResponse(error: e);
+          } catch (e) {
+            return wrapResponse(
+              error: PlatformException(code: 'error', message: e.toString()),
+            );
+          }
+        });
+      }
+    }
   }
 }
 
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 54afb51..dbca311 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
@@ -1559,6 +1559,8 @@
 
   suspend fun callFlutterCallbackThrowErrorFromVoid()
 
+  suspend fun callFlutterIsAsyncFlutterApiOnRoot(): Boolean
+
   companion object {
     /** The codec used by HostIntegrationCoreApi. */
     val codec: MessageCodec<Any?> by lazy { CoreTestsPigeonCodec() }
@@ -5388,6 +5390,28 @@
           channel.setMessageHandler(null)
         }
       }
+      run {
+        val channel =
+            BasicMessageChannel<Any?>(
+                binaryMessenger,
+                "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterIsAsyncFlutterApiOnRoot$separatedMessageChannelSuffix",
+                codec)
+        if (api != null) {
+          channel.setMessageHandler { _, reply ->
+            CoroutineScope(Dispatchers.Main).launch {
+              val wrapped: List<Any?> =
+                  try {
+                    listOf(api.callFlutterIsAsyncFlutterApiOnRoot())
+                  } catch (exception: Throwable) {
+                    CoreTestsPigeonUtils.wrapError(exception)
+                  }
+              reply.reply(wrapped)
+            }
+          }
+        } else {
+          channel.setMessageHandler(null)
+        }
+      }
     }
   }
 }
@@ -6832,6 +6856,33 @@
       }
     }
   }
+  /** Returns true if the async FlutterApi method is run on the root isolate. */
+  suspend fun isAsyncFlutterApiOnRoot(): Boolean {
+    val separatedMessageChannelSuffix =
+        if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
+    return suspendCancellableCoroutine { continuation ->
+      val channelName =
+          "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.isAsyncFlutterApiOnRoot$separatedMessageChannelSuffix"
+      val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
+      channel.send(null) {
+        if (it is List<*>) {
+          if (it.size > 1) {
+            continuation.resumeWithException(
+                FlutterError(it[0] as String, it[1] as String, it[2] as String?))
+          } else if (it[0] == null) {
+            continuation.resumeWithException(
+                FlutterError(
+                    "null-error", "Flutter api returned null value for non-null return value.", ""))
+          } else {
+            val output = it[0] as Boolean
+            continuation.resume(output)
+          }
+        } else {
+          continuation.resumeWithException(CoreTestsPigeonUtils.createConnectionError(channelName))
+        }
+      }
+    }
+  }
 }
 /**
  * A Host API using callback-based asynchronous methods (@asyncCallback).
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 f08d52c..7884d2d 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
@@ -863,6 +863,17 @@
     return suspendFlutterApi { flutterCallbackApi!!.throwErrorFromVoid(it) }
   }
 
+  override suspend fun callFlutterIsAsyncFlutterApiOnRoot(): Boolean {
+    if (Looper.myLooper() != Looper.getMainLooper()) {
+      return false
+    }
+    val result = flutterApi!!.isAsyncFlutterApiOnRoot()
+    if (Looper.myLooper() != Looper.getMainLooper()) {
+      return false
+    }
+    return result
+  }
+
   fun testUnusedClassesGenerate(): UnusedClass {
     return UnusedClass()
   }
diff --git a/packages/pigeon/platform_tests/test_plugin/darwin/test_plugin/Sources/test_plugin/CoreTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/darwin/test_plugin/Sources/test_plugin/CoreTests.gen.swift
index d3c1aab..29d4262 100644
--- a/packages/pigeon/platform_tests/test_plugin/darwin/test_plugin/Sources/test_plugin/CoreTests.gen.swift
+++ b/packages/pigeon/platform_tests/test_plugin/darwin/test_plugin/Sources/test_plugin/CoreTests.gen.swift
@@ -1474,6 +1474,7 @@
   func callFlutterCallbackEcho(_ aString: String) async throws -> String
   func callFlutterCallbackThrowError() async throws -> Any?
   func callFlutterCallbackThrowErrorFromVoid() async throws
+  func callFlutterIsAsyncFlutterApiOnRoot() async throws -> Bool
 }
 
 /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
@@ -4757,6 +4758,24 @@
     } else {
       callFlutterCallbackThrowErrorFromVoidChannel.setMessageHandler(nil)
     }
+    let callFlutterIsAsyncFlutterApiOnRootChannel = FlutterBasicMessageChannel(
+      name:
+        "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi.callFlutterIsAsyncFlutterApiOnRoot\(channelSuffix)",
+      binaryMessenger: binaryMessenger, codec: codec)
+    if let api = api {
+      callFlutterIsAsyncFlutterApiOnRootChannel.setMessageHandler { _, reply in
+        Task { @MainActor in
+          do {
+            let result = try await api.callFlutterIsAsyncFlutterApiOnRoot()
+            reply(wrapResult(result))
+          } catch {
+            reply(wrapError(error))
+          }
+        }
+      }
+    } else {
+      callFlutterIsAsyncFlutterApiOnRootChannel.setMessageHandler(nil)
+    }
   }
 }
 
@@ -4877,128 +4896,141 @@
 protocol FlutterIntegrationCoreApiProtocol {
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic calling.
-  func noop() async throws
+  @MainActor func noop() async throws
   /// Responds with an error from an async function returning a value.
-  func throwError() async throws -> Any?
+  @MainActor func throwError() async throws -> Any?
   /// Responds with an error from an async void function.
-  func throwErrorFromVoid() async throws
+  @MainActor func throwErrorFromVoid() async throws
   /// Returns the passed object, to test serialization and deserialization.
-  func echo(_ everythingArg: AllTypes) async throws -> AllTypes
+  @MainActor func echo(_ everythingArg: AllTypes) async throws -> AllTypes
   /// Returns the passed object, to test serialization and deserialization.
-  func echoNullable(_ everythingArg: AllNullableTypes?) async throws -> AllNullableTypes?
+  @MainActor func echoNullable(_ everythingArg: AllNullableTypes?) async throws -> AllNullableTypes?
   /// Returns passed in arguments of multiple types.
   ///
   /// Tests multiple-arity FlutterApi handling.
-  func sendMultipleNullableTypes(
+  @MainActor func sendMultipleNullableTypes(
     aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?,
     aString aNullableStringArg: String?
   ) async throws -> AllNullableTypes
   /// Returns the passed object, to test serialization and deserialization.
-  func echoNullable(_ everythingArg: AllNullableTypesWithoutRecursion?) async throws
+  @MainActor func echoNullable(_ everythingArg: AllNullableTypesWithoutRecursion?) async throws
     -> AllNullableTypesWithoutRecursion?
   /// Returns passed in arguments of multiple types.
   ///
   /// Tests multiple-arity FlutterApi handling.
-  func sendMultipleNullableTypesWithoutRecursion(
+  @MainActor func sendMultipleNullableTypesWithoutRecursion(
     aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?,
     aString aNullableStringArg: String?
   ) async throws -> AllNullableTypesWithoutRecursion
   /// Returns the passed boolean, to test serialization and deserialization.
-  func echo(_ aBoolArg: Bool) async throws -> Bool
+  @MainActor func echo(_ aBoolArg: Bool) async throws -> Bool
   /// Returns the passed int, to test serialization and deserialization.
-  func echo(_ anIntArg: Int64) async throws -> Int64
+  @MainActor func echo(_ anIntArg: Int64) async throws -> Int64
   /// Returns the passed double, to test serialization and deserialization.
-  func echo(_ aDoubleArg: Double) async throws -> Double
+  @MainActor func echo(_ aDoubleArg: Double) async throws -> Double
   /// Returns the passed string, to test serialization and deserialization.
-  func echo(_ aStringArg: String) async throws -> String
+  @MainActor func echo(_ aStringArg: String) async throws -> String
   /// Returns the passed byte list, to test serialization and deserialization.
-  func echo(_ listArg: FlutterStandardTypedData) async throws -> FlutterStandardTypedData
+  @MainActor func echo(_ listArg: FlutterStandardTypedData) async throws -> FlutterStandardTypedData
   /// Returns the passed list, to test serialization and deserialization.
-  func echo(_ listArg: [Any?]) async throws -> [Any?]
+  @MainActor func echo(_ listArg: [Any?]) async throws -> [Any?]
   /// Returns the passed list, to test serialization and deserialization.
-  func echo(enumList enumListArg: [AnEnum?]) async throws -> [AnEnum?]
+  @MainActor func echo(enumList enumListArg: [AnEnum?]) async throws -> [AnEnum?]
   /// Returns the passed list, to test serialization and deserialization.
-  func echo(classList classListArg: [AllNullableTypes?]) async throws -> [AllNullableTypes?]
+  @MainActor func echo(classList classListArg: [AllNullableTypes?]) async throws
+    -> [AllNullableTypes?]
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNonNull(enumList enumListArg: [AnEnum]) async throws -> [AnEnum]
+  @MainActor func echoNonNull(enumList enumListArg: [AnEnum]) async throws -> [AnEnum]
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNonNull(classList classListArg: [AllNullableTypes]) async throws -> [AllNullableTypes]
+  @MainActor func echoNonNull(classList classListArg: [AllNullableTypes]) async throws
+    -> [AllNullableTypes]
   /// Returns the passed map, to test serialization and deserialization.
-  func echo(_ mapArg: [AnyHashable?: Any?]) async throws -> [AnyHashable?: Any?]
+  @MainActor func echo(_ mapArg: [AnyHashable?: Any?]) async throws -> [AnyHashable?: Any?]
   /// Returns the passed map, to test serialization and deserialization.
-  func echo(stringMap stringMapArg: [String?: String?]) async throws -> [String?: String?]
+  @MainActor func echo(stringMap stringMapArg: [String?: String?]) async throws -> [String?:
+    String?]
   /// Returns the passed map, to test serialization and deserialization.
-  func echo(intMap intMapArg: [Int64?: Int64?]) async throws -> [Int64?: Int64?]
+  @MainActor func echo(intMap intMapArg: [Int64?: Int64?]) async throws -> [Int64?: Int64?]
   /// Returns the passed map, to test serialization and deserialization.
-  func echo(enumMap enumMapArg: [AnEnum?: AnEnum?]) async throws -> [AnEnum?: AnEnum?]
+  @MainActor func echo(enumMap enumMapArg: [AnEnum?: AnEnum?]) async throws -> [AnEnum?: AnEnum?]
   /// Returns the passed map, to test serialization and deserialization.
-  func echo(classMap classMapArg: [Int64?: AllNullableTypes?]) async throws -> [Int64?:
+  @MainActor func echo(classMap classMapArg: [Int64?: AllNullableTypes?]) async throws -> [Int64?:
     AllNullableTypes?]
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNonNull(stringMap stringMapArg: [String: String]) async throws -> [String: String]
+  @MainActor func echoNonNull(stringMap stringMapArg: [String: String]) async throws -> [String:
+    String]
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNonNull(intMap intMapArg: [Int64: Int64]) async throws -> [Int64: Int64]
+  @MainActor func echoNonNull(intMap intMapArg: [Int64: Int64]) async throws -> [Int64: Int64]
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNonNull(enumMap enumMapArg: [AnEnum: AnEnum]) async throws -> [AnEnum: AnEnum]
+  @MainActor func echoNonNull(enumMap enumMapArg: [AnEnum: AnEnum]) async throws -> [AnEnum: AnEnum]
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNonNull(classMap classMapArg: [Int64: AllNullableTypes]) async throws -> [Int64:
-    AllNullableTypes]
+  @MainActor func echoNonNull(classMap classMapArg: [Int64: AllNullableTypes]) async throws
+    -> [Int64: AllNullableTypes]
   /// Returns the passed enum to test serialization and deserialization.
-  func echo(_ anEnumArg: AnEnum) async throws -> AnEnum
+  @MainActor func echo(_ anEnumArg: AnEnum) async throws -> AnEnum
   /// Returns the passed enum to test serialization and deserialization.
-  func echo(_ anotherEnumArg: AnotherEnum) async throws -> AnotherEnum
+  @MainActor func echo(_ anotherEnumArg: AnotherEnum) async throws -> AnotherEnum
   /// Returns the passed boolean, to test serialization and deserialization.
-  func echoNullable(_ aBoolArg: Bool?) async throws -> Bool?
+  @MainActor func echoNullable(_ aBoolArg: Bool?) async throws -> Bool?
   /// Returns the passed int, to test serialization and deserialization.
-  func echoNullable(_ anIntArg: Int64?) async throws -> Int64?
+  @MainActor func echoNullable(_ anIntArg: Int64?) async throws -> Int64?
   /// Returns the passed double, to test serialization and deserialization.
-  func echoNullable(_ aDoubleArg: Double?) async throws -> Double?
+  @MainActor func echoNullable(_ aDoubleArg: Double?) async throws -> Double?
   /// Returns the passed string, to test serialization and deserialization.
-  func echoNullable(_ aStringArg: String?) async throws -> String?
+  @MainActor func echoNullable(_ aStringArg: String?) async throws -> String?
   /// Returns the passed byte list, to test serialization and deserialization.
-  func echoNullable(_ listArg: FlutterStandardTypedData?) async throws -> FlutterStandardTypedData?
+  @MainActor func echoNullable(_ listArg: FlutterStandardTypedData?) async throws
+    -> FlutterStandardTypedData?
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNullable(_ listArg: [Any?]?) async throws -> [Any?]?
+  @MainActor func echoNullable(_ listArg: [Any?]?) async throws -> [Any?]?
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNullable(enumList enumListArg: [AnEnum?]?) async throws -> [AnEnum?]?
+  @MainActor func echoNullable(enumList enumListArg: [AnEnum?]?) async throws -> [AnEnum?]?
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNullable(classList classListArg: [AllNullableTypes?]?) async throws
+  @MainActor func echoNullable(classList classListArg: [AllNullableTypes?]?) async throws
     -> [AllNullableTypes?]?
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNullableNonNull(enumList enumListArg: [AnEnum]?) async throws -> [AnEnum]?
+  @MainActor func echoNullableNonNull(enumList enumListArg: [AnEnum]?) async throws -> [AnEnum]?
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNullableNonNull(classList classListArg: [AllNullableTypes]?) async throws
+  @MainActor func echoNullableNonNull(classList classListArg: [AllNullableTypes]?) async throws
     -> [AllNullableTypes]?
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullable(_ mapArg: [AnyHashable?: Any?]?) async throws -> [AnyHashable?: Any?]?
+  @MainActor func echoNullable(_ mapArg: [AnyHashable?: Any?]?) async throws -> [AnyHashable?:
+    Any?]?
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullable(stringMap stringMapArg: [String?: String?]?) async throws -> [String?: String?]?
+  @MainActor func echoNullable(stringMap stringMapArg: [String?: String?]?) async throws
+    -> [String?: String?]?
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullable(intMap intMapArg: [Int64?: Int64?]?) async throws -> [Int64?: Int64?]?
+  @MainActor func echoNullable(intMap intMapArg: [Int64?: Int64?]?) async throws -> [Int64?:
+    Int64?]?
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullable(enumMap enumMapArg: [AnEnum?: AnEnum?]?) async throws -> [AnEnum?: AnEnum?]?
+  @MainActor func echoNullable(enumMap enumMapArg: [AnEnum?: AnEnum?]?) async throws -> [AnEnum?:
+    AnEnum?]?
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullable(classMap classMapArg: [Int64?: AllNullableTypes?]?) async throws -> [Int64?:
-    AllNullableTypes?]?
+  @MainActor func echoNullable(classMap classMapArg: [Int64?: AllNullableTypes?]?) async throws
+    -> [Int64?: AllNullableTypes?]?
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullableNonNull(stringMap stringMapArg: [String: String]?) async throws -> [String:
-    String]?
+  @MainActor func echoNullableNonNull(stringMap stringMapArg: [String: String]?) async throws
+    -> [String: String]?
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullableNonNull(intMap intMapArg: [Int64: Int64]?) async throws -> [Int64: Int64]?
+  @MainActor func echoNullableNonNull(intMap intMapArg: [Int64: Int64]?) async throws -> [Int64:
+    Int64]?
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullableNonNull(enumMap enumMapArg: [AnEnum: AnEnum]?) async throws -> [AnEnum: AnEnum]?
+  @MainActor func echoNullableNonNull(enumMap enumMapArg: [AnEnum: AnEnum]?) async throws
+    -> [AnEnum: AnEnum]?
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullableNonNull(classMap classMapArg: [Int64: AllNullableTypes]?) async throws -> [Int64:
-    AllNullableTypes]?
+  @MainActor func echoNullableNonNull(classMap classMapArg: [Int64: AllNullableTypes]?) async throws
+    -> [Int64: AllNullableTypes]?
   /// Returns the passed enum to test serialization and deserialization.
-  func echoNullable(_ anEnumArg: AnEnum?) async throws -> AnEnum?
+  @MainActor func echoNullable(_ anEnumArg: AnEnum?) async throws -> AnEnum?
   /// Returns the passed enum to test serialization and deserialization.
-  func echoNullable(_ anotherEnumArg: AnotherEnum?) async throws -> AnotherEnum?
+  @MainActor func echoNullable(_ anotherEnumArg: AnotherEnum?) async throws -> AnotherEnum?
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic asynchronous calling.
-  func noopAsync() async throws
+  @MainActor func noopAsync() async throws
   /// Returns the passed in generic Object asynchronously.
-  func echoAsync(_ aStringArg: String) async throws -> String
+  @MainActor func echoAsync(_ aStringArg: String) async throws -> String
+  /// Returns true if the async FlutterApi method is run on the root isolate.
+  @MainActor func isAsyncFlutterApiOnRoot() async throws -> Bool
 }
 class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol {
   private let binaryMessenger: FlutterBinaryMessenger
@@ -5012,7 +5044,7 @@
   }
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic calling.
-  func noop() async throws {
+  @MainActor func noop() async throws {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.noop\(messageChannelSuffix)"
@@ -5035,7 +5067,7 @@
     }
   }
   /// Responds with an error from an async function returning a value.
-  func throwError() async throws -> Any? {
+  @MainActor func throwError() async throws -> Any? {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.throwError\(messageChannelSuffix)"
@@ -5059,7 +5091,7 @@
     }
   }
   /// Responds with an error from an async void function.
-  func throwErrorFromVoid() async throws {
+  @MainActor func throwErrorFromVoid() async throws {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.throwErrorFromVoid\(messageChannelSuffix)"
@@ -5082,7 +5114,7 @@
     }
   }
   /// Returns the passed object, to test serialization and deserialization.
-  func echo(_ everythingArg: AllTypes) async throws -> AllTypes {
+  @MainActor func echo(_ everythingArg: AllTypes) async throws -> AllTypes {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllTypes\(messageChannelSuffix)"
@@ -5111,7 +5143,8 @@
     }
   }
   /// Returns the passed object, to test serialization and deserialization.
-  func echoNullable(_ everythingArg: AllNullableTypes?) async throws -> AllNullableTypes? {
+  @MainActor func echoNullable(_ everythingArg: AllNullableTypes?) async throws -> AllNullableTypes?
+  {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypes\(messageChannelSuffix)"
@@ -5137,7 +5170,7 @@
   /// Returns passed in arguments of multiple types.
   ///
   /// Tests multiple-arity FlutterApi handling.
-  func sendMultipleNullableTypes(
+  @MainActor func sendMultipleNullableTypes(
     aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?,
     aString aNullableStringArg: String?
   ) async throws -> AllNullableTypes {
@@ -5170,7 +5203,7 @@
     }
   }
   /// Returns the passed object, to test serialization and deserialization.
-  func echoNullable(_ everythingArg: AllNullableTypesWithoutRecursion?) async throws
+  @MainActor func echoNullable(_ everythingArg: AllNullableTypesWithoutRecursion?) async throws
     -> AllNullableTypesWithoutRecursion?
   {
     return try await withCheckedThrowingContinuation { continuation in
@@ -5198,7 +5231,7 @@
   /// Returns passed in arguments of multiple types.
   ///
   /// Tests multiple-arity FlutterApi handling.
-  func sendMultipleNullableTypesWithoutRecursion(
+  @MainActor func sendMultipleNullableTypesWithoutRecursion(
     aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?,
     aString aNullableStringArg: String?
   ) async throws -> AllNullableTypesWithoutRecursion {
@@ -5231,7 +5264,7 @@
     }
   }
   /// Returns the passed boolean, to test serialization and deserialization.
-  func echo(_ aBoolArg: Bool) async throws -> Bool {
+  @MainActor func echo(_ aBoolArg: Bool) async throws -> Bool {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoBool\(messageChannelSuffix)"
@@ -5260,7 +5293,7 @@
     }
   }
   /// Returns the passed int, to test serialization and deserialization.
-  func echo(_ anIntArg: Int64) async throws -> Int64 {
+  @MainActor func echo(_ anIntArg: Int64) async throws -> Int64 {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoInt\(messageChannelSuffix)"
@@ -5289,7 +5322,7 @@
     }
   }
   /// Returns the passed double, to test serialization and deserialization.
-  func echo(_ aDoubleArg: Double) async throws -> Double {
+  @MainActor func echo(_ aDoubleArg: Double) async throws -> Double {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoDouble\(messageChannelSuffix)"
@@ -5318,7 +5351,7 @@
     }
   }
   /// Returns the passed string, to test serialization and deserialization.
-  func echo(_ aStringArg: String) async throws -> String {
+  @MainActor func echo(_ aStringArg: String) async throws -> String {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoString\(messageChannelSuffix)"
@@ -5347,7 +5380,8 @@
     }
   }
   /// Returns the passed byte list, to test serialization and deserialization.
-  func echo(_ listArg: FlutterStandardTypedData) async throws -> FlutterStandardTypedData {
+  @MainActor func echo(_ listArg: FlutterStandardTypedData) async throws -> FlutterStandardTypedData
+  {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoUint8List\(messageChannelSuffix)"
@@ -5376,7 +5410,7 @@
     }
   }
   /// Returns the passed list, to test serialization and deserialization.
-  func echo(_ listArg: [Any?]) async throws -> [Any?] {
+  @MainActor func echo(_ listArg: [Any?]) async throws -> [Any?] {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoList\(messageChannelSuffix)"
@@ -5405,7 +5439,7 @@
     }
   }
   /// Returns the passed list, to test serialization and deserialization.
-  func echo(enumList enumListArg: [AnEnum?]) async throws -> [AnEnum?] {
+  @MainActor func echo(enumList enumListArg: [AnEnum?]) async throws -> [AnEnum?] {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnumList\(messageChannelSuffix)"
@@ -5434,7 +5468,9 @@
     }
   }
   /// Returns the passed list, to test serialization and deserialization.
-  func echo(classList classListArg: [AllNullableTypes?]) async throws -> [AllNullableTypes?] {
+  @MainActor func echo(classList classListArg: [AllNullableTypes?]) async throws
+    -> [AllNullableTypes?]
+  {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoClassList\(messageChannelSuffix)"
@@ -5463,7 +5499,7 @@
     }
   }
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNonNull(enumList enumListArg: [AnEnum]) async throws -> [AnEnum] {
+  @MainActor func echoNonNull(enumList enumListArg: [AnEnum]) async throws -> [AnEnum] {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNonNullEnumList\(messageChannelSuffix)"
@@ -5492,7 +5528,9 @@
     }
   }
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNonNull(classList classListArg: [AllNullableTypes]) async throws -> [AllNullableTypes] {
+  @MainActor func echoNonNull(classList classListArg: [AllNullableTypes]) async throws
+    -> [AllNullableTypes]
+  {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNonNullClassList\(messageChannelSuffix)"
@@ -5521,7 +5559,7 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echo(_ mapArg: [AnyHashable?: Any?]) async throws -> [AnyHashable?: Any?] {
+  @MainActor func echo(_ mapArg: [AnyHashable?: Any?]) async throws -> [AnyHashable?: Any?] {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoMap\(messageChannelSuffix)"
@@ -5550,7 +5588,9 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echo(stringMap stringMapArg: [String?: String?]) async throws -> [String?: String?] {
+  @MainActor func echo(stringMap stringMapArg: [String?: String?]) async throws -> [String?:
+    String?]
+  {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoStringMap\(messageChannelSuffix)"
@@ -5579,7 +5619,7 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echo(intMap intMapArg: [Int64?: Int64?]) async throws -> [Int64?: Int64?] {
+  @MainActor func echo(intMap intMapArg: [Int64?: Int64?]) async throws -> [Int64?: Int64?] {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoIntMap\(messageChannelSuffix)"
@@ -5608,7 +5648,7 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echo(enumMap enumMapArg: [AnEnum?: AnEnum?]) async throws -> [AnEnum?: AnEnum?] {
+  @MainActor func echo(enumMap enumMapArg: [AnEnum?: AnEnum?]) async throws -> [AnEnum?: AnEnum?] {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnumMap\(messageChannelSuffix)"
@@ -5637,7 +5677,7 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echo(classMap classMapArg: [Int64?: AllNullableTypes?]) async throws -> [Int64?:
+  @MainActor func echo(classMap classMapArg: [Int64?: AllNullableTypes?]) async throws -> [Int64?:
     AllNullableTypes?]
   {
     return try await withCheckedThrowingContinuation { continuation in
@@ -5668,7 +5708,9 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNonNull(stringMap stringMapArg: [String: String]) async throws -> [String: String] {
+  @MainActor func echoNonNull(stringMap stringMapArg: [String: String]) async throws -> [String:
+    String]
+  {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNonNullStringMap\(messageChannelSuffix)"
@@ -5697,7 +5739,7 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNonNull(intMap intMapArg: [Int64: Int64]) async throws -> [Int64: Int64] {
+  @MainActor func echoNonNull(intMap intMapArg: [Int64: Int64]) async throws -> [Int64: Int64] {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNonNullIntMap\(messageChannelSuffix)"
@@ -5726,7 +5768,8 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNonNull(enumMap enumMapArg: [AnEnum: AnEnum]) async throws -> [AnEnum: AnEnum] {
+  @MainActor func echoNonNull(enumMap enumMapArg: [AnEnum: AnEnum]) async throws -> [AnEnum: AnEnum]
+  {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNonNullEnumMap\(messageChannelSuffix)"
@@ -5755,8 +5798,8 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNonNull(classMap classMapArg: [Int64: AllNullableTypes]) async throws -> [Int64:
-    AllNullableTypes]
+  @MainActor func echoNonNull(classMap classMapArg: [Int64: AllNullableTypes]) async throws
+    -> [Int64: AllNullableTypes]
   {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
@@ -5786,7 +5829,7 @@
     }
   }
   /// Returns the passed enum to test serialization and deserialization.
-  func echo(_ anEnumArg: AnEnum) async throws -> AnEnum {
+  @MainActor func echo(_ anEnumArg: AnEnum) async throws -> AnEnum {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnum\(messageChannelSuffix)"
@@ -5815,7 +5858,7 @@
     }
   }
   /// Returns the passed enum to test serialization and deserialization.
-  func echo(_ anotherEnumArg: AnotherEnum) async throws -> AnotherEnum {
+  @MainActor func echo(_ anotherEnumArg: AnotherEnum) async throws -> AnotherEnum {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAnotherEnum\(messageChannelSuffix)"
@@ -5844,7 +5887,7 @@
     }
   }
   /// Returns the passed boolean, to test serialization and deserialization.
-  func echoNullable(_ aBoolArg: Bool?) async throws -> Bool? {
+  @MainActor func echoNullable(_ aBoolArg: Bool?) async throws -> Bool? {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableBool\(messageChannelSuffix)"
@@ -5868,7 +5911,7 @@
     }
   }
   /// Returns the passed int, to test serialization and deserialization.
-  func echoNullable(_ anIntArg: Int64?) async throws -> Int64? {
+  @MainActor func echoNullable(_ anIntArg: Int64?) async throws -> Int64? {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableInt\(messageChannelSuffix)"
@@ -5892,7 +5935,7 @@
     }
   }
   /// Returns the passed double, to test serialization and deserialization.
-  func echoNullable(_ aDoubleArg: Double?) async throws -> Double? {
+  @MainActor func echoNullable(_ aDoubleArg: Double?) async throws -> Double? {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableDouble\(messageChannelSuffix)"
@@ -5916,7 +5959,7 @@
     }
   }
   /// Returns the passed string, to test serialization and deserialization.
-  func echoNullable(_ aStringArg: String?) async throws -> String? {
+  @MainActor func echoNullable(_ aStringArg: String?) async throws -> String? {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableString\(messageChannelSuffix)"
@@ -5940,7 +5983,8 @@
     }
   }
   /// Returns the passed byte list, to test serialization and deserialization.
-  func echoNullable(_ listArg: FlutterStandardTypedData?) async throws -> FlutterStandardTypedData?
+  @MainActor func echoNullable(_ listArg: FlutterStandardTypedData?) async throws
+    -> FlutterStandardTypedData?
   {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
@@ -5965,7 +6009,7 @@
     }
   }
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNullable(_ listArg: [Any?]?) async throws -> [Any?]? {
+  @MainActor func echoNullable(_ listArg: [Any?]?) async throws -> [Any?]? {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableList\(messageChannelSuffix)"
@@ -5989,7 +6033,7 @@
     }
   }
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNullable(enumList enumListArg: [AnEnum?]?) async throws -> [AnEnum?]? {
+  @MainActor func echoNullable(enumList enumListArg: [AnEnum?]?) async throws -> [AnEnum?]? {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnumList\(messageChannelSuffix)"
@@ -6013,7 +6057,7 @@
     }
   }
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNullable(classList classListArg: [AllNullableTypes?]?) async throws
+  @MainActor func echoNullable(classList classListArg: [AllNullableTypes?]?) async throws
     -> [AllNullableTypes?]?
   {
     return try await withCheckedThrowingContinuation { continuation in
@@ -6039,7 +6083,7 @@
     }
   }
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNullableNonNull(enumList enumListArg: [AnEnum]?) async throws -> [AnEnum]? {
+  @MainActor func echoNullableNonNull(enumList enumListArg: [AnEnum]?) async throws -> [AnEnum]? {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableNonNullEnumList\(messageChannelSuffix)"
@@ -6063,7 +6107,7 @@
     }
   }
   /// Returns the passed list, to test serialization and deserialization.
-  func echoNullableNonNull(classList classListArg: [AllNullableTypes]?) async throws
+  @MainActor func echoNullableNonNull(classList classListArg: [AllNullableTypes]?) async throws
     -> [AllNullableTypes]?
   {
     return try await withCheckedThrowingContinuation { continuation in
@@ -6089,7 +6133,9 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullable(_ mapArg: [AnyHashable?: Any?]?) async throws -> [AnyHashable?: Any?]? {
+  @MainActor func echoNullable(_ mapArg: [AnyHashable?: Any?]?) async throws -> [AnyHashable?:
+    Any?]?
+  {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableMap\(messageChannelSuffix)"
@@ -6113,7 +6159,8 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullable(stringMap stringMapArg: [String?: String?]?) async throws -> [String?: String?]?
+  @MainActor func echoNullable(stringMap stringMapArg: [String?: String?]?) async throws
+    -> [String?: String?]?
   {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
@@ -6138,7 +6185,9 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullable(intMap intMapArg: [Int64?: Int64?]?) async throws -> [Int64?: Int64?]? {
+  @MainActor func echoNullable(intMap intMapArg: [Int64?: Int64?]?) async throws -> [Int64?:
+    Int64?]?
+  {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableIntMap\(messageChannelSuffix)"
@@ -6162,7 +6211,9 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullable(enumMap enumMapArg: [AnEnum?: AnEnum?]?) async throws -> [AnEnum?: AnEnum?]? {
+  @MainActor func echoNullable(enumMap enumMapArg: [AnEnum?: AnEnum?]?) async throws -> [AnEnum?:
+    AnEnum?]?
+  {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnumMap\(messageChannelSuffix)"
@@ -6186,8 +6237,8 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullable(classMap classMapArg: [Int64?: AllNullableTypes?]?) async throws -> [Int64?:
-    AllNullableTypes?]?
+  @MainActor func echoNullable(classMap classMapArg: [Int64?: AllNullableTypes?]?) async throws
+    -> [Int64?: AllNullableTypes?]?
   {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
@@ -6212,8 +6263,8 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullableNonNull(stringMap stringMapArg: [String: String]?) async throws -> [String:
-    String]?
+  @MainActor func echoNullableNonNull(stringMap stringMapArg: [String: String]?) async throws
+    -> [String: String]?
   {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
@@ -6238,7 +6289,9 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullableNonNull(intMap intMapArg: [Int64: Int64]?) async throws -> [Int64: Int64]? {
+  @MainActor func echoNullableNonNull(intMap intMapArg: [Int64: Int64]?) async throws -> [Int64:
+    Int64]?
+  {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableNonNullIntMap\(messageChannelSuffix)"
@@ -6262,7 +6315,8 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullableNonNull(enumMap enumMapArg: [AnEnum: AnEnum]?) async throws -> [AnEnum: AnEnum]?
+  @MainActor func echoNullableNonNull(enumMap enumMapArg: [AnEnum: AnEnum]?) async throws
+    -> [AnEnum: AnEnum]?
   {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
@@ -6287,8 +6341,8 @@
     }
   }
   /// Returns the passed map, to test serialization and deserialization.
-  func echoNullableNonNull(classMap classMapArg: [Int64: AllNullableTypes]?) async throws -> [Int64:
-    AllNullableTypes]?
+  @MainActor func echoNullableNonNull(classMap classMapArg: [Int64: AllNullableTypes]?) async throws
+    -> [Int64: AllNullableTypes]?
   {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
@@ -6313,7 +6367,7 @@
     }
   }
   /// Returns the passed enum to test serialization and deserialization.
-  func echoNullable(_ anEnumArg: AnEnum?) async throws -> AnEnum? {
+  @MainActor func echoNullable(_ anEnumArg: AnEnum?) async throws -> AnEnum? {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnum\(messageChannelSuffix)"
@@ -6337,7 +6391,7 @@
     }
   }
   /// Returns the passed enum to test serialization and deserialization.
-  func echoNullable(_ anotherEnumArg: AnotherEnum?) async throws -> AnotherEnum? {
+  @MainActor func echoNullable(_ anotherEnumArg: AnotherEnum?) async throws -> AnotherEnum? {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAnotherNullableEnum\(messageChannelSuffix)"
@@ -6362,7 +6416,7 @@
   }
   /// A no-op function taking no arguments and returning no value, to sanity
   /// test basic asynchronous calling.
-  func noopAsync() async throws {
+  @MainActor func noopAsync() async throws {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.noopAsync\(messageChannelSuffix)"
@@ -6385,7 +6439,7 @@
     }
   }
   /// Returns the passed in generic Object asynchronously.
-  func echoAsync(_ aStringArg: String) async throws -> String {
+  @MainActor func echoAsync(_ aStringArg: String) async throws -> String {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAsyncString\(messageChannelSuffix)"
@@ -6413,6 +6467,35 @@
       }
     }
   }
+  /// Returns true if the async FlutterApi method is run on the root isolate.
+  @MainActor func isAsyncFlutterApiOnRoot() async throws -> Bool {
+    return try await withCheckedThrowingContinuation { continuation in
+      let channelName: String =
+        "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.isAsyncFlutterApiOnRoot\(messageChannelSuffix)"
+      let channel = FlutterBasicMessageChannel(
+        name: channelName, binaryMessenger: binaryMessenger, codec: codec)
+      channel.sendMessage(nil) { response in
+        guard let listResponse = response as? [Any?] else {
+          continuation.resume(throwing: createConnectionError(withChannelName: channelName))
+          return
+        }
+        if listResponse.count > 1 {
+          let code: String = listResponse[0] as! String
+          let message: String? = nilOrValue(listResponse[1])
+          let details: String? = nilOrValue(listResponse[2])
+          continuation.resume(throwing: PigeonError(code: code, message: message, details: details))
+        } else if listResponse[0] == nil {
+          continuation.resume(
+            throwing: PigeonError(
+              code: "null-error",
+              message: "Flutter api returned null value for non-null return value.", details: ""))
+        } else {
+          let result = listResponse[0] as! Bool
+          continuation.resume(returning: result)
+        }
+      }
+    }
+  }
 }
 /// A Host API using callback-based asynchronous methods (@asyncCallback).
 ///
@@ -6671,8 +6754,8 @@
 ///
 /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift.
 protocol FlutterSmallApiProtocol {
-  func echo(_ msgArg: TestMessage) async throws -> TestMessage
-  func echo(string aStringArg: String) async throws -> String
+  @MainActor func echo(_ msgArg: TestMessage) async throws -> TestMessage
+  @MainActor func echo(string aStringArg: String) async throws -> String
 }
 class FlutterSmallApi: FlutterSmallApiProtocol {
   private let binaryMessenger: FlutterBinaryMessenger
@@ -6684,7 +6767,7 @@
   var codec: CoreTestsPigeonCodec {
     return CoreTestsPigeonCodec.shared
   }
-  func echo(_ msgArg: TestMessage) async throws -> TestMessage {
+  @MainActor func echo(_ msgArg: TestMessage) async throws -> TestMessage {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterSmallApi.echoWrappedList\(messageChannelSuffix)"
@@ -6712,7 +6795,7 @@
       }
     }
   }
-  func echo(string aStringArg: String) async throws -> String {
+  @MainActor func echo(string aStringArg: String) async throws -> String {
     return try await withCheckedThrowingContinuation { continuation in
       let channelName: String =
         "dev.flutter.pigeon.pigeon_integration_tests.FlutterSmallApi.echoString\(messageChannelSuffix)"
diff --git a/packages/pigeon/platform_tests/test_plugin/darwin/test_plugin/Sources/test_plugin/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/darwin/test_plugin/Sources/test_plugin/TestPlugin.swift
index acaf194..7df4c52 100644
--- a/packages/pigeon/platform_tests/test_plugin/darwin/test_plugin/Sources/test_plugin/TestPlugin.swift
+++ b/packages/pigeon/platform_tests/test_plugin/darwin/test_plugin/Sources/test_plugin/TestPlugin.swift
@@ -826,6 +826,18 @@
     try await awaitFlutterApi { self.flutterCallbackAPI.throwErrorFromVoid(completion: $0) }
   }
 
+  @MainActor
+  func callFlutterIsAsyncFlutterApiOnRoot() async throws -> Bool {
+    guard Thread.isMainThread else {
+      return false
+    }
+    let result = try await self.flutterAPI.isAsyncFlutterApiOnRoot()
+    guard Thread.isMainThread else {
+      return false
+    }
+    return result
+  }
+
   func testUnusedClassesGenerate() -> UnusedClass {
     return UnusedClass()
   }
diff --git a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/DataClassMethodsTests.swift b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/DataClassMethodsTests.swift
index c9d64d9..ccfc186 100644
--- a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/DataClassMethodsTests.swift
+++ b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/DataClassMethodsTests.swift
@@ -7,6 +7,7 @@
 
 @testable import test_plugin
 
+@MainActor
 struct DataClassMethodsTests {
 
   private let everythingFull = AllNullableTypes(
diff --git a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/PrimitiveTests.swift b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/PrimitiveTests.swift
index f779618..c02f048 100644
--- a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/PrimitiveTests.swift
+++ b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/PrimitiveTests.swift
@@ -19,6 +19,7 @@
   func aStringIntMap(value: [String?: Int64?]) -> [String?: Int64?] { value }
 }
 
+@MainActor
 struct PrimitiveTests {
   let codec = FlutterStandardMessageCodec.sharedInstance()
 
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 f99b79d..49b3c9f 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
@@ -16697,6 +16697,81 @@
   return self;
 }
 
+G_DECLARE_FINAL_TYPE(
+    CoreTestsPigeonTestHostIntegrationCoreApiCallFlutterIsAsyncFlutterApiOnRootResponse,
+    core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_response,
+    CORE_TESTS_PIGEON_TEST,
+    HOST_INTEGRATION_CORE_API_CALL_FLUTTER_IS_ASYNC_FLUTTER_API_ON_ROOT_RESPONSE,
+    GObject)
+
+struct
+    _CoreTestsPigeonTestHostIntegrationCoreApiCallFlutterIsAsyncFlutterApiOnRootResponse {
+  GObject parent_instance;
+
+  FlValue* value;
+};
+
+G_DEFINE_TYPE(
+    CoreTestsPigeonTestHostIntegrationCoreApiCallFlutterIsAsyncFlutterApiOnRootResponse,
+    core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_response,
+    G_TYPE_OBJECT)
+
+static void
+core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_response_dispose(
+    GObject* object) {
+  CoreTestsPigeonTestHostIntegrationCoreApiCallFlutterIsAsyncFlutterApiOnRootResponse*
+      self =
+          CORE_TESTS_PIGEON_TEST_HOST_INTEGRATION_CORE_API_CALL_FLUTTER_IS_ASYNC_FLUTTER_API_ON_ROOT_RESPONSE(
+              object);
+  g_clear_pointer(&self->value, fl_value_unref);
+  G_OBJECT_CLASS(
+      core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_response_parent_class)
+      ->dispose(object);
+}
+
+static void
+core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_response_init(
+    CoreTestsPigeonTestHostIntegrationCoreApiCallFlutterIsAsyncFlutterApiOnRootResponse*
+        self) {}
+
+static void
+core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_response_class_init(
+    CoreTestsPigeonTestHostIntegrationCoreApiCallFlutterIsAsyncFlutterApiOnRootResponseClass*
+        klass) {
+  G_OBJECT_CLASS(klass)->dispose =
+      core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_response_dispose;
+}
+
+static CoreTestsPigeonTestHostIntegrationCoreApiCallFlutterIsAsyncFlutterApiOnRootResponse*
+core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_response_new(
+    gboolean return_value) {
+  CoreTestsPigeonTestHostIntegrationCoreApiCallFlutterIsAsyncFlutterApiOnRootResponse*
+      self = CORE_TESTS_PIGEON_TEST_HOST_INTEGRATION_CORE_API_CALL_FLUTTER_IS_ASYNC_FLUTTER_API_ON_ROOT_RESPONSE(
+          g_object_new(
+              core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_response_get_type(),
+              nullptr));
+  self->value = fl_value_new_list();
+  fl_value_append_take(self->value, fl_value_new_bool(return_value));
+  return self;
+}
+
+static CoreTestsPigeonTestHostIntegrationCoreApiCallFlutterIsAsyncFlutterApiOnRootResponse*
+core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_response_new_error(
+    const gchar* code, const gchar* message, FlValue* details) {
+  CoreTestsPigeonTestHostIntegrationCoreApiCallFlutterIsAsyncFlutterApiOnRootResponse*
+      self = CORE_TESTS_PIGEON_TEST_HOST_INTEGRATION_CORE_API_CALL_FLUTTER_IS_ASYNC_FLUTTER_API_ON_ROOT_RESPONSE(
+          g_object_new(
+              core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_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 _CoreTestsPigeonTestHostIntegrationCoreApi {
   GObject parent_instance;
 
@@ -20958,6 +21033,25 @@
                                                             self->user_data);
 }
 
+static void
+core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_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->call_flutter_is_async_flutter_api_on_root == nullptr) {
+    return;
+  }
+
+  g_autoptr(CoreTestsPigeonTestHostIntegrationCoreApiResponseHandle) handle =
+      core_tests_pigeon_test_host_integration_core_api_response_handle_new(
+          channel, response_handle);
+  self->vtable->call_flutter_is_async_flutter_api_on_root(handle,
+                                                          self->user_data);
+}
+
 void core_tests_pigeon_test_host_integration_core_api_set_method_handlers(
     FlBinaryMessenger* messenger, const gchar* suffix,
     const CoreTestsPigeonTestHostIntegrationCoreApiVTable* vtable,
@@ -23007,6 +23101,20 @@
       call_flutter_callback_throw_error_from_void_channel,
       core_tests_pigeon_test_host_integration_core_api_call_flutter_callback_throw_error_from_void_cb,
       g_object_ref(api_data), g_object_unref);
+  g_autofree gchar* call_flutter_is_async_flutter_api_on_root_channel_name =
+      g_strdup_printf(
+          "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+          "callFlutterIsAsyncFlutterApiOnRoot%s",
+          dot_suffix);
+  g_autoptr(FlBasicMessageChannel)
+      call_flutter_is_async_flutter_api_on_root_channel =
+          fl_basic_message_channel_new(
+              messenger, call_flutter_is_async_flutter_api_on_root_channel_name,
+              FL_MESSAGE_CODEC(codec));
+  fl_basic_message_channel_set_message_handler(
+      call_flutter_is_async_flutter_api_on_root_channel,
+      core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_cb,
+      g_object_ref(api_data), g_object_unref);
 }
 
 void core_tests_pigeon_test_host_integration_core_api_clear_method_handlers(
@@ -24740,6 +24848,19 @@
   fl_basic_message_channel_set_message_handler(
       call_flutter_callback_throw_error_from_void_channel, nullptr, nullptr,
       nullptr);
+  g_autofree gchar* call_flutter_is_async_flutter_api_on_root_channel_name =
+      g_strdup_printf(
+          "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+          "callFlutterIsAsyncFlutterApiOnRoot%s",
+          dot_suffix);
+  g_autoptr(FlBasicMessageChannel)
+      call_flutter_is_async_flutter_api_on_root_channel =
+          fl_basic_message_channel_new(
+              messenger, call_flutter_is_async_flutter_api_on_root_channel_name,
+              FL_MESSAGE_CODEC(codec));
+  fl_basic_message_channel_set_message_handler(
+      call_flutter_is_async_flutter_api_on_root_channel, nullptr, nullptr,
+      nullptr);
 }
 
 void core_tests_pigeon_test_host_integration_core_api_respond_noop_async(
@@ -27924,6 +28045,40 @@
   }
 }
 
+void core_tests_pigeon_test_host_integration_core_api_respond_call_flutter_is_async_flutter_api_on_root(
+    CoreTestsPigeonTestHostIntegrationCoreApiResponseHandle* response_handle,
+    gboolean return_value) {
+  g_autoptr(
+      CoreTestsPigeonTestHostIntegrationCoreApiCallFlutterIsAsyncFlutterApiOnRootResponse)
+      response =
+          core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_response_new(
+              return_value);
+  g_autoptr(GError) error = nullptr;
+  if (!fl_basic_message_channel_respond(response_handle->channel,
+                                        response_handle->response_handle,
+                                        response->value, &error)) {
+    g_warning("Failed to send response to %s.%s: %s", "HostIntegrationCoreApi",
+              "callFlutterIsAsyncFlutterApiOnRoot", error->message);
+  }
+}
+
+void core_tests_pigeon_test_host_integration_core_api_respond_error_call_flutter_is_async_flutter_api_on_root(
+    CoreTestsPigeonTestHostIntegrationCoreApiResponseHandle* response_handle,
+    const gchar* code, const gchar* message, FlValue* details) {
+  g_autoptr(
+      CoreTestsPigeonTestHostIntegrationCoreApiCallFlutterIsAsyncFlutterApiOnRootResponse)
+      response =
+          core_tests_pigeon_test_host_integration_core_api_call_flutter_is_async_flutter_api_on_root_response_new_error(
+              code, message, details);
+  g_autoptr(GError) error = nullptr;
+  if (!fl_basic_message_channel_respond(response_handle->channel,
+                                        response_handle->response_handle,
+                                        response->value, &error)) {
+    g_warning("Failed to send response to %s.%s: %s", "HostIntegrationCoreApi",
+              "callFlutterIsAsyncFlutterApiOnRoot", error->message);
+  }
+}
+
 struct _CoreTestsPigeonTestFlutterCallbackCoreApi {
   GObject parent_instance;
 
@@ -37323,6 +37478,174 @@
       response);
 }
 
+struct
+    _CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse {
+  GObject parent_instance;
+
+  FlValue* error;
+  FlValue* return_value;
+};
+
+G_DEFINE_TYPE(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse,
+    core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response,
+    G_TYPE_OBJECT)
+
+static void
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_dispose(
+    GObject* object) {
+  CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse* self =
+      CORE_TESTS_PIGEON_TEST_FLUTTER_INTEGRATION_CORE_API_IS_ASYNC_FLUTTER_API_ON_ROOT_RESPONSE(
+          object);
+  g_clear_pointer(&self->error, fl_value_unref);
+  g_clear_pointer(&self->return_value, fl_value_unref);
+  G_OBJECT_CLASS(
+      core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_parent_class)
+      ->dispose(object);
+}
+
+static void
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_init(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+        self) {}
+
+static void
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_class_init(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponseClass*
+        klass) {
+  G_OBJECT_CLASS(klass)->dispose =
+      core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_dispose;
+}
+
+static CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_new(
+    FlValue* response) {
+  CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse* self =
+      CORE_TESTS_PIGEON_TEST_FLUTTER_INTEGRATION_CORE_API_IS_ASYNC_FLUTTER_API_ON_ROOT_RESPONSE(
+          g_object_new(
+              core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_type(),
+              nullptr));
+  if (fl_value_get_length(response) > 1) {
+    self->error = fl_value_ref(response);
+  } else {
+    FlValue* value = fl_value_get_list_value(response, 0);
+    self->return_value = fl_value_ref(value);
+  }
+  return self;
+}
+
+gboolean
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_is_error(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+        self) {
+  g_return_val_if_fail(
+      CORE_TESTS_PIGEON_TEST_IS_FLUTTER_INTEGRATION_CORE_API_IS_ASYNC_FLUTTER_API_ON_ROOT_RESPONSE(
+          self),
+      FALSE);
+  return self->error != nullptr;
+}
+
+const gchar*
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_error_code(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+        self) {
+  g_return_val_if_fail(
+      CORE_TESTS_PIGEON_TEST_IS_FLUTTER_INTEGRATION_CORE_API_IS_ASYNC_FLUTTER_API_ON_ROOT_RESPONSE(
+          self),
+      nullptr);
+  g_assert(
+      core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_is_error(
+          self));
+  return fl_value_get_string(fl_value_get_list_value(self->error, 0));
+}
+
+const gchar*
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_error_message(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+        self) {
+  g_return_val_if_fail(
+      CORE_TESTS_PIGEON_TEST_IS_FLUTTER_INTEGRATION_CORE_API_IS_ASYNC_FLUTTER_API_ON_ROOT_RESPONSE(
+          self),
+      nullptr);
+  g_assert(
+      core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_is_error(
+          self));
+  return fl_value_get_string(fl_value_get_list_value(self->error, 1));
+}
+
+FlValue*
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_error_details(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+        self) {
+  g_return_val_if_fail(
+      CORE_TESTS_PIGEON_TEST_IS_FLUTTER_INTEGRATION_CORE_API_IS_ASYNC_FLUTTER_API_ON_ROOT_RESPONSE(
+          self),
+      nullptr);
+  g_assert(
+      core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_is_error(
+          self));
+  return fl_value_get_list_value(self->error, 2);
+}
+
+gboolean
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_return_value(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+        self) {
+  g_return_val_if_fail(
+      CORE_TESTS_PIGEON_TEST_IS_FLUTTER_INTEGRATION_CORE_API_IS_ASYNC_FLUTTER_API_ON_ROOT_RESPONSE(
+          self),
+      FALSE);
+  g_assert(
+      !core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_is_error(
+          self));
+  return fl_value_get_bool(self->return_value);
+}
+
+static void
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_cb(
+    GObject* object, GAsyncResult* result, gpointer user_data) {
+  GTask* task = G_TASK(user_data);
+  g_task_return_pointer(task, result, g_object_unref);
+}
+
+void core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root(
+    CoreTestsPigeonTestFlutterIntegrationCoreApi* self,
+    GCancellable* cancellable, GAsyncReadyCallback callback,
+    gpointer user_data) {
+  g_autoptr(FlValue) args = fl_value_new_list();
+  g_autofree gchar* channel_name = g_strdup_printf(
+      "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi."
+      "isAsyncFlutterApiOnRoot%s",
+      self->suffix);
+  g_autoptr(CoreTestsPigeonTestMessageCodec) codec =
+      core_tests_pigeon_test_message_codec_new();
+  FlBasicMessageChannel* channel = fl_basic_message_channel_new(
+      self->messenger, channel_name, FL_MESSAGE_CODEC(codec));
+  GTask* task = g_task_new(self, cancellable, callback, user_data);
+  g_task_set_task_data(task, channel, g_object_unref);
+  fl_basic_message_channel_send(
+      channel, args, cancellable,
+      core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_cb,
+      task);
+}
+
+CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_finish(
+    CoreTestsPigeonTestFlutterIntegrationCoreApi* self, GAsyncResult* result,
+    GError** error) {
+  g_autoptr(GTask) task = G_TASK(result);
+  GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, nullptr));
+  FlBasicMessageChannel* channel =
+      FL_BASIC_MESSAGE_CHANNEL(g_task_get_task_data(task));
+  g_autoptr(FlValue) response =
+      fl_basic_message_channel_send_finish(channel, r, error);
+  if (response == nullptr) {
+    return nullptr;
+  }
+  return core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_new(
+      response);
+}
+
 struct _CoreTestsPigeonTestHostCallbackCoreApiResponseHandle {
   GObject parent_instance;
 
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 1607a23..11d1438 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
@@ -4598,6 +4598,9 @@
   void (*call_flutter_callback_throw_error_from_void)(
       CoreTestsPigeonTestHostIntegrationCoreApiResponseHandle* response_handle,
       gpointer user_data);
+  void (*call_flutter_is_async_flutter_api_on_root)(
+      CoreTestsPigeonTestHostIntegrationCoreApiResponseHandle* response_handle,
+      gpointer user_data);
 } CoreTestsPigeonTestHostIntegrationCoreApiVTable;
 
 /**
@@ -6942,6 +6945,31 @@
     CoreTestsPigeonTestHostIntegrationCoreApiResponseHandle* response_handle,
     const gchar* code, const gchar* message, FlValue* details);
 
+/**
+ * core_tests_pigeon_test_host_integration_core_api_respond_call_flutter_is_async_flutter_api_on_root:
+ * @response_handle: a #CoreTestsPigeonTestHostIntegrationCoreApiResponseHandle.
+ * @return_value: location to write the value returned by this method.
+ *
+ * Responds to HostIntegrationCoreApi.callFlutterIsAsyncFlutterApiOnRoot.
+ */
+void core_tests_pigeon_test_host_integration_core_api_respond_call_flutter_is_async_flutter_api_on_root(
+    CoreTestsPigeonTestHostIntegrationCoreApiResponseHandle* response_handle,
+    gboolean return_value);
+
+/**
+ * core_tests_pigeon_test_host_integration_core_api_respond_error_call_flutter_is_async_flutter_api_on_root:
+ * @response_handle: a #CoreTestsPigeonTestHostIntegrationCoreApiResponseHandle.
+ * @code: error code.
+ * @message: error message.
+ * @details: (allow-none): error details or %NULL.
+ *
+ * Responds with an error to
+ * HostIntegrationCoreApi.callFlutterIsAsyncFlutterApiOnRoot.
+ */
+void core_tests_pigeon_test_host_integration_core_api_respond_error_call_flutter_is_async_flutter_api_on_root(
+    CoreTestsPigeonTestHostIntegrationCoreApiResponseHandle* response_handle,
+    const gchar* code, const gchar* message, FlValue* details);
+
 G_DECLARE_FINAL_TYPE(
     CoreTestsPigeonTestFlutterCallbackCoreApiNoopResponse,
     core_tests_pigeon_test_flutter_callback_core_api_noop_response,
@@ -11171,6 +11199,83 @@
     CoreTestsPigeonTestFlutterIntegrationCoreApiEchoAsyncStringResponse*
         response);
 
+G_DECLARE_FINAL_TYPE(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse,
+    core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response,
+    CORE_TESTS_PIGEON_TEST,
+    FLUTTER_INTEGRATION_CORE_API_IS_ASYNC_FLUTTER_API_ON_ROOT_RESPONSE, GObject)
+
+/**
+ * core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_is_error:
+ * @response: a
+ * #CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse.
+ *
+ * Checks if a response to FlutterIntegrationCoreApi.isAsyncFlutterApiOnRoot is
+ * an error.
+ *
+ * Returns: a %TRUE if this response is an error.
+ */
+gboolean
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_is_error(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+        response);
+
+/**
+ * core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_error_code:
+ * @response: a
+ * #CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse.
+ *
+ * Get the error code for this response.
+ *
+ * Returns: an error code or %NULL if not an error.
+ */
+const gchar*
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_error_code(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+        response);
+
+/**
+ * core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_error_message:
+ * @response: a
+ * #CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse.
+ *
+ * Get the error message for this response.
+ *
+ * Returns: an error message.
+ */
+const gchar*
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_error_message(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+        response);
+
+/**
+ * core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_error_details:
+ * @response: a
+ * #CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse.
+ *
+ * Get the error details for this response.
+ *
+ * Returns: (allow-none): an error details or %NULL.
+ */
+FlValue*
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_error_details(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+        response);
+
+/**
+ * core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_return_value:
+ * @response: a
+ * #CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse.
+ *
+ * Get the return value for this response.
+ *
+ * Returns: a return value.
+ */
+gboolean
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_return_value(
+    CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+        response);
+
 /**
  * CoreTestsPigeonTestFlutterIntegrationCoreApi:
  *
@@ -13038,6 +13143,41 @@
     CoreTestsPigeonTestFlutterIntegrationCoreApi* api, GAsyncResult* result,
     GError** error);
 
+/**
+ * core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root:
+ * @api: a #CoreTestsPigeonTestFlutterIntegrationCoreApi.
+ * @cancellable: (allow-none): a #GCancellable or %NULL.
+ * @callback: (scope async): (allow-none): a #GAsyncReadyCallback to call when
+ * the call is complete or %NULL to ignore the response.
+ * @user_data: (closure): user data to pass to @callback.
+ *
+ * Returns true if the async FlutterApi method is run on the root isolate.
+ */
+void core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root(
+    CoreTestsPigeonTestFlutterIntegrationCoreApi* api,
+    GCancellable* cancellable, GAsyncReadyCallback callback,
+    gpointer user_data);
+
+/**
+ * core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_finish:
+ * @api: a #CoreTestsPigeonTestFlutterIntegrationCoreApi.
+ * @result: a #GAsyncResult.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Completes a
+ * core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root()
+ * call.
+ *
+ * Returns: a
+ * #CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse
+ * or %NULL on error.
+ */
+CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse*
+core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_finish(
+    CoreTestsPigeonTestFlutterIntegrationCoreApi* api, GAsyncResult* result,
+    GError** error);
+
 G_DECLARE_FINAL_TYPE(CoreTestsPigeonTestHostCallbackCoreApi,
                      core_tests_pigeon_test_host_callback_core_api,
                      CORE_TESTS_PIGEON_TEST, HOST_CALLBACK_CORE_API, GObject)
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 6b44586..8c42895 100644
--- a/packages/pigeon/platform_tests/test_plugin/linux/test_plugin.cc
+++ b/packages/pigeon/platform_tests/test_plugin/linux/test_plugin.cc
@@ -3418,6 +3418,53 @@
       callback_data_new(self, response_handle));
 }
 
+static void is_async_flutter_api_on_root_cb(GObject* object,
+                                            GAsyncResult* result,
+                                            gpointer user_data) {
+  g_autoptr(CallbackData) data = static_cast<CallbackData*>(user_data);
+
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(
+      CoreTestsPigeonTestFlutterIntegrationCoreApiIsAsyncFlutterApiOnRootResponse)
+      response =
+          core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_finish(
+              CORE_TESTS_PIGEON_TEST_FLUTTER_INTEGRATION_CORE_API(object),
+              result, &error);
+  if (response == nullptr) {
+    core_tests_pigeon_test_host_integration_core_api_respond_error_call_flutter_is_async_flutter_api_on_root(
+        data->response_handle, "Internal Error", error->message, nullptr);
+    return;
+  }
+  if (core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_is_error(
+          response)) {
+    core_tests_pigeon_test_host_integration_core_api_respond_error_call_flutter_is_async_flutter_api_on_root(
+        data->response_handle,
+        core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_error_code(
+            response),
+        core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_error_message(
+            response),
+        core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_error_details(
+            response));
+    return;
+  }
+
+  core_tests_pigeon_test_host_integration_core_api_respond_call_flutter_is_async_flutter_api_on_root(
+      data->response_handle,
+      core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root_response_get_return_value(
+          response));
+}
+
+static void call_flutter_is_async_flutter_api_on_root(
+    CoreTestsPigeonTestHostIntegrationCoreApiResponseHandle* response_handle,
+    gpointer user_data) {
+  TestPlugin* self = TEST_PLUGIN(user_data);
+
+  core_tests_pigeon_test_flutter_integration_core_api_is_async_flutter_api_on_root(
+      self->flutter_core_api, self->cancellable,
+      is_async_flutter_api_on_root_cb,
+      callback_data_new(self, response_handle));
+}
+
 static void host_callback_noop(
     CoreTestsPigeonTestHostCallbackCoreApiResponseHandle* response_handle,
     gpointer user_data) {
@@ -3679,7 +3726,9 @@
     .call_flutter_callback_echo_string = call_flutter_callback_echo_string,
     .call_flutter_callback_throw_error = call_flutter_callback_throw_error,
     .call_flutter_callback_throw_error_from_void =
-        call_flutter_callback_throw_error_from_void};
+        call_flutter_callback_throw_error_from_void,
+    .call_flutter_is_async_flutter_api_on_root =
+        call_flutter_is_async_flutter_api_on_root};
 
 static void echo(const gchar* a_string,
                  CoreTestsPigeonTestHostSmallApiResponseHandle* response_handle,
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 9abc656..dce5eaa 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
@@ -9741,6 +9741,37 @@
       channel.SetMessageHandler(nullptr);
     }
   }
+  {
+    BasicMessageChannel<> channel(
+        binary_messenger,
+        "dev.flutter.pigeon.pigeon_integration_tests.HostIntegrationCoreApi."
+        "callFlutterIsAsyncFlutterApiOnRoot" +
+            prepended_suffix,
+        &GetCodec());
+    if (api != nullptr) {
+      channel.SetMessageHandler(
+          [api](const EncodableValue& message,
+                const ::flutter::MessageReply<EncodableValue>& reply) {
+            try {
+              api->CallFlutterIsAsyncFlutterApiOnRoot(
+                  [reply](ErrorOr<bool>&& output) {
+                    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);
+    }
+  }
 }
 
 EncodableValue HostIntegrationCoreApi::WrapError(
@@ -11909,6 +11940,40 @@
       });
 }
 
+void FlutterIntegrationCoreApi::IsAsyncFlutterApiOnRoot(
+    std::function<void(bool)>&& on_success,
+    std::function<void(const FlutterError&)>&& on_error) {
+  const std::string channel_name =
+      "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi."
+      "isAsyncFlutterApiOnRoot" +
+      message_channel_suffix_;
+  BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec());
+  EncodableValue encoded_api_arguments = EncodableValue();
+  channel.Send(
+      encoded_api_arguments, [channel_name, on_success = std::move(on_success),
+                              on_error = std::move(on_error)](
+                                 const uint8_t* reply, size_t reply_size) {
+        std::unique_ptr<EncodableValue> response =
+            GetCodec().DecodeMessage(reply, reply_size);
+        const auto& encodable_return_value = *response;
+        const auto* list_return_value =
+            std::get_if<EncodableList>(&encodable_return_value);
+        if (list_return_value) {
+          if (list_return_value->size() > 1) {
+            on_error(
+                FlutterError(std::get<std::string>(list_return_value->at(0)),
+                             std::get<std::string>(list_return_value->at(1)),
+                             list_return_value->at(2)));
+          } else {
+            const auto& return_value = std::get<bool>(list_return_value->at(0));
+            on_success(return_value);
+          }
+        } else {
+          on_error(CreateConnectionError(channel_name));
+        }
+      });
+}
+
 /// The codec used by HostCallbackCoreApi.
 const ::flutter::StandardMessageCodec& HostCallbackCoreApi::GetCodec() {
   return ::flutter::StandardMessageCodec::GetInstance(
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 225dcb1..7fff45d 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
@@ -1495,6 +1495,8 @@
           result) = 0;
   virtual void CallFlutterCallbackThrowErrorFromVoid(
       std::function<void(std::optional<FlutterError> reply)> result) = 0;
+  virtual void CallFlutterIsAsyncFlutterApiOnRoot(
+      std::function<void(ErrorOr<bool> reply)> result) = 0;
 
   // The codec used by HostIntegrationCoreApi.
   static const ::flutter::StandardMessageCodec& GetCodec();
@@ -1792,6 +1794,10 @@
   void EchoAsyncString(const std::string& a_string,
                        std::function<void(const std::string&)>&& on_success,
                        std::function<void(const FlutterError&)>&& on_error);
+  // Returns true if the async FlutterApi method is run on the root isolate.
+  void IsAsyncFlutterApiOnRoot(
+      std::function<void(bool)>&& on_success,
+      std::function<void(const FlutterError&)>&& on_error);
 
  private:
   ::flutter::BinaryMessenger* binary_messenger_;
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 175fa05..c4136cc 100644
--- a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp
+++ b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.cpp
@@ -1359,6 +1359,13 @@
       [result](const FlutterError& error) { result(error); });
 }
 
+void TestPlugin::CallFlutterIsAsyncFlutterApiOnRoot(
+    std::function<void(core_tests_pigeontest::ErrorOr<bool> reply)> result) {
+  flutter_api_->IsAsyncFlutterApiOnRoot(
+      [result](bool is_on_root) { result(is_on_root); },
+      [result](const FlutterError& error) { result(error); });
+}
+
 core_tests_pigeontest::UnusedClass TestPlugin::TestUnusedClassGenerates() {
   return core_tests_pigeontest::UnusedClass();
 }
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 3660528..b67da39 100644
--- a/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h
+++ b/packages/pigeon/platform_tests/test_plugin/windows/test_plugin.h
@@ -740,6 +740,9 @@
       std::function<
           void(std::optional<core_tests_pigeontest::FlutterError> reply)>
           result) override;
+  void CallFlutterIsAsyncFlutterApiOnRoot(
+      std::function<void(core_tests_pigeontest::ErrorOr<bool> reply)> result)
+      override;
   core_tests_pigeontest::UnusedClass TestUnusedClassGenerates();
 
  private:
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index a5fefad..69fb207 100644
--- a/packages/pigeon/pubspec.yaml
+++ b/packages/pigeon/pubspec.yaml
@@ -2,7 +2,8 @@
 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: 29.0.0 # This must match the version in lib/src/generator_tools.dart
+
+version: 29.0.1 # This must match the version in lib/src/generator_tools.dart
 
 environment:
   sdk: ^3.11.0
diff --git a/packages/pigeon/test/swift_generator_test.dart b/packages/pigeon/test/swift_generator_test.dart
index 6d6243f..bb507fe 100644
--- a/packages/pigeon/test/swift_generator_test.dart
+++ b/packages/pigeon/test/swift_generator_test.dart
@@ -837,10 +837,50 @@
     generator.generate(swiftOptions, root, sink, dartPackageName: DEFAULT_PACKAGE_NAME);
     final code = sink.toString();
     expect(code, contains('class Api'));
-    expect(code, matches('func doSomething.*Input.*async throws -> Output'));
+    expect(code, matches(r'@MainActor func doSomething.*Input.*async throws -> Output'));
     expect(code, isNot(contains('if (')));
   });
 
+  test('flutter api methods use @MainActor for async but not callback', () {
+    final root = Root(
+      apis: <Api>[
+        AstFlutterApi(
+          name: 'Api',
+          methods: <Method>[
+            Method(
+              name: 'asyncMethod',
+              location: ApiLocation.flutter,
+              parameters: <Parameter>[],
+              returnType: const TypeDeclaration.voidDeclaration(),
+              isAsynchronous: true,
+            ),
+            Method(
+              name: 'callbackMethod',
+              location: ApiLocation.flutter,
+              parameters: <Parameter>[],
+              returnType: const TypeDeclaration.voidDeclaration(),
+              isAsynchronous: true,
+              isAsynchronousCallback: true,
+            ),
+          ],
+        ),
+      ],
+      classes: <Class>[],
+      enums: <Enum>[],
+    );
+    final sink = StringBuffer();
+    const swiftOptions = InternalSwiftOptions(swiftOut: '');
+    const generator = SwiftGenerator();
+    generator.generate(swiftOptions, root, sink, dartPackageName: DEFAULT_PACKAGE_NAME);
+    final code = sink.toString();
+    expect(code, contains('@MainActor func asyncMethod() async throws'));
+    expect(
+      code,
+      contains('func callbackMethod(completion: @escaping (Result<Void, PigeonError>) -> Void)'),
+    );
+    expect(code, isNot(contains('@MainActor func callbackMethod')));
+  });
+
   test('gen one enum class', () {
     final anEnum = Enum(
       name: 'Enum1',