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

Started handling messages from background isolates for iOS #35174

Merged
merged 19 commits into from
Sep 8, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,9 @@ FILE: ../../../flutter/lib/ui/window/platform_message_response.cc
FILE: ../../../flutter/lib/ui/window/platform_message_response.h
FILE: ../../../flutter/lib/ui/window/platform_message_response_dart.cc
FILE: ../../../flutter/lib/ui/window/platform_message_response_dart.h
FILE: ../../../flutter/lib/ui/window/platform_message_response_dart_port.cc
FILE: ../../../flutter/lib/ui/window/platform_message_response_dart_port.h
FILE: ../../../flutter/lib/ui/window/platform_message_response_dart_port_unittests.cc
FILE: ../../../flutter/lib/ui/window/platform_message_response_dart_unittests.cc
FILE: ../../../flutter/lib/ui/window/pointer_data.cc
FILE: ../../../flutter/lib/ui/window/pointer_data.h
Expand Down
4 changes: 4 additions & 0 deletions lib/ui/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ source_set("ui") {
"window/platform_message_response.h",
"window/platform_message_response_dart.cc",
"window/platform_message_response_dart.h",
"window/platform_message_response_dart_port.cc",
"window/platform_message_response_dart_port.h",
"window/pointer_data.cc",
"window/pointer_data.h",
"window/pointer_data_packet.cc",
Expand All @@ -140,6 +142,7 @@ source_set("ui") {

public_deps = [
"//flutter/flow",
"//flutter/shell/common:platform_message_handler",
"//flutter/third_party/txt",
]

Expand Down Expand Up @@ -230,6 +233,7 @@ if (enable_unittests) {
"painting/single_frame_codec_unittests.cc",
"semantics/semantics_update_builder_unittests.cc",
"window/platform_configuration_unittests.cc",
"window/platform_message_response_dart_port_unittests.cc",
"window/platform_message_response_dart_unittests.cc",
"window/pointer_data_packet_converter_unittests.cc",
]
Expand Down
3 changes: 3 additions & 0 deletions lib/ui/dart_ui.cc
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ typedef CanvasPath Path;
V(PlatformConfigurationNativeApi::ComputePlatformResolvedLocale, 1) \
V(PlatformConfigurationNativeApi::SendPlatformMessage, 3) \
V(PlatformConfigurationNativeApi::RespondToPlatformMessage, 2) \
V(PlatformConfigurationNativeApi::GetRootIsolateToken, 0) \
V(PlatformConfigurationNativeApi::RegisterBackgroundIsolate, 1) \
V(PlatformConfigurationNativeApi::SendPortPlatformMessage, 4) \
V(DartRuntimeHooks::Logger_PrintDebugString, 1) \
V(DartRuntimeHooks::Logger_PrintString, 1) \
V(DartRuntimeHooks::ScheduleMicrotask, 1) \
Expand Down
64 changes: 64 additions & 0 deletions lib/ui/fixtures/ui_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import 'dart:async';
import 'dart:typed_data';
import 'dart:ui';
import 'dart:isolate';
import 'dart:ffi';

void main() {}

Expand Down Expand Up @@ -234,6 +236,21 @@ void frameCallback(_Image, int) {
print('called back');
}

@pragma('vm:entry-point')
void platformMessagePortResponseTest() async {
ReceivePort receivePort = ReceivePort();
_callPlatformMessageResponseDartPort(receivePort.sendPort.nativePort);
List<dynamic> resultList = await receivePort.first;
int identifier = resultList[0] as int;
Uint8List? bytes = resultList[1] as Uint8List?;
ByteData result = ByteData.sublistView(bytes!);
if (result.lengthInBytes == 100) {
_finishCallResponse(true);
} else {
_finishCallResponse(false);
}
}

@pragma('vm:entry-point')
void platformMessageResponseTest() {
_callPlatformMessageResponseDart((ByteData? result) {
Expand All @@ -246,6 +263,7 @@ void platformMessageResponseTest() {
});
}

void _callPlatformMessageResponseDartPort(int port) native 'CallPlatformMessageResponseDartPort';
void _callPlatformMessageResponseDart(void Function(ByteData? result) callback) native 'CallPlatformMessageResponseDart';
void _finishCallResponse(bool didPass) native 'FinishCallResponse';

Expand Down Expand Up @@ -884,9 +902,55 @@ void hooksTests() {
expectEquals(result, true);
});

test('root isolate token', () async {
if (RootIsolateToken.instance == null) {
throw Exception('We should have a token on a root isolate.');
}
ReceivePort receivePort = ReceivePort();
Isolate.spawn(_backgroundRootIsolateTestMain, receivePort.sendPort);
bool didPass = await receivePort.first as bool;
if (!didPass) {
throw Exception('Background isolate found a root isolate id.');
}
});

test('send port message without registering', () async {
ReceivePort receivePort = ReceivePort();
Isolate.spawn(_backgroundIsolateSendWithoutRegistering, receivePort.sendPort);
bool didError = await receivePort.first as bool;
if (!didError) {
throw Exception('Expected an error when not registering a root isolate and sending port messages.');
}
});

_finish();
}

/// Sends `true` on [port] if the isolate executing the function is not a root
/// isolate.
void _backgroundRootIsolateTestMain(SendPort port) {
port.send(RootIsolateToken.instance == null);
}

/// Sends `true` on [port] if [PlatformDispatcher.sendPortPlatformMessage]
/// throws an exception without calling
/// [PlatformDispatcher.registerBackgroundIsolate].
void _backgroundIsolateSendWithoutRegistering(SendPort port) {
bool didError = false;
ReceivePort messagePort = ReceivePort();
try {
PlatformDispatcher.instance.sendPortPlatformMessage(
'foo',
null,
1,
messagePort.sendPort,
);
} catch (_) {
didError = true;
}
port.send(didError);
}

typedef _Callback<T> = void Function(T result);
typedef _Callbacker<T> = String? Function(_Callback<T?> callback);

Expand Down
59 changes: 58 additions & 1 deletion lib/ui/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@ const String _kFlutterKeyDataChannel = 'flutter/keydata';
ByteData? _wrapUnmodifiableByteData(ByteData? byteData) =>
byteData == null ? null : UnmodifiableByteDataView(byteData);

/// A token that represents a root isolate.
class RootIsolateToken {
RootIsolateToken._(this._token);

/// An enumeration representing the root isolate (0 if not a root isolate).
final int _token;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a nit, consider adding a doc comment here explaining what this is, even though it's not public.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


/// The token for the root isolate that is executing this Dart code. If this
/// Dart code is not executing on a root isolate [instance] will be null.
static final RootIsolateToken? instance = () {
final int token = __getRootIsolateToken();
return token == 0 ? null : RootIsolateToken._(token);
}();

@FfiNative<Int64 Function()>('PlatformConfigurationNativeApi::GetRootIsolateToken')
external static int __getRootIsolateToken();
}

/// Platform event dispatcher singleton.
///
/// The most basic interface to the host operating system's interface.
Expand Down Expand Up @@ -532,12 +550,51 @@ class PlatformDispatcher {
}
}

String? _sendPlatformMessage(String name,PlatformMessageResponseCallback? callback, ByteData? data) =>
String? _sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data) =>
__sendPlatformMessage(name, callback, data);

@FfiNative<Handle Function(Handle, Handle, Handle)>('PlatformConfigurationNativeApi::SendPlatformMessage')
external static String? __sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data);

/// Sends a message to a platform-specific plugin via a [SendPort].
///
/// This operates similarly to [sendPlatformMessage] but is used when sending
/// messages from background isolates. The [port] parameter allows Flutter to
/// know which isolate to send the result to. The [name] parameter is the name
/// of the channel communication will happen on. The [data] parameter is the
/// payload of the message. The [identifier] parameter is a unique integer
/// assigned to the message.
void sendPortPlatformMessage(
String name,
ByteData? data,
int identifier,
SendPort port) {
final String? error =
_sendPortPlatformMessage(name, identifier, port.nativePort, data);
if (error != null) {
throw Exception(error);
}
}

String? _sendPortPlatformMessage(String name, int identifier, int port, ByteData? data) =>
__sendPortPlatformMessage(name, identifier, port, data);

@FfiNative<Handle Function(Handle, Handle, Handle, Handle)>('PlatformConfigurationNativeApi::SendPortPlatformMessage')
external static String? __sendPortPlatformMessage(String name, int identifier, int port, ByteData? data);

/// Registers the current isolate with the isolate identified with by the
/// [token]. This is required if platform channels are to be used on a
/// background isolate.
void registerBackgroundIsolate(RootIsolateToken token) {
if (!Platform.isIOS) {
// Issue: https://github.com/flutter/flutter/issues/13937
throw UnimplementedError("Platform doesn't yet support platform channels on background isolates.");
}
__registerBackgroundIsolate(token._token);
}
@FfiNative<Void Function(Int64)>('PlatformConfigurationNativeApi::RegisterBackgroundIsolate')
external static void __registerBackgroundIsolate(int rootIsolateId);

/// Called whenever this platform dispatcher receives a message from a
/// platform-specific plugin.
///
Expand Down
30 changes: 30 additions & 0 deletions lib/ui/ui_dart_state.cc
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,20 @@ UIDartState* UIDartState::Current() {

void UIDartState::SetPlatformConfiguration(
std::unique_ptr<PlatformConfiguration> platform_configuration) {
FML_DCHECK(IsRootIsolate());
platform_configuration_ = std::move(platform_configuration);
if (platform_configuration_) {
platform_configuration_->client()->UpdateIsolateDescription(debug_name_,
main_port_);
}
}

void UIDartState::SetPlatformMessageHandler(
std::weak_ptr<PlatformMessageHandler> handler) {
FML_DCHECK(!IsRootIsolate());
platform_message_handler_ = handler;
}

const TaskRunners& UIDartState::GetTaskRunners() const {
return context_.task_runners;
}
Expand Down Expand Up @@ -214,4 +221,27 @@ bool UIDartState::enable_skparagraph() const {
return enable_skparagraph_;
}

Dart_Handle UIDartState::HandlePlatformMessage(
std::unique_ptr<PlatformMessage> message) {
if (platform_configuration_) {
platform_configuration_->client()->HandlePlatformMessage(
std::move(message));
} else {
std::shared_ptr<PlatformMessageHandler> handler =
platform_message_handler_.lock();
if (handler) {
handler->HandlePlatformMessage(std::move(message));
} else {
return tonic::ToDart(
"No platform channel handler registered for background isolate.");
}
}

return Dart_Null();
}

int64_t UIDartState::GetRootIsolateToken() const {
return IsRootIsolate() ? reinterpret_cast<int64_t>(this) : 0;
}

} // namespace flutter
11 changes: 11 additions & 0 deletions lib/ui/ui_dart_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "flutter/lib/ui/painting/image_decoder.h"
#include "flutter/lib/ui/snapshot_delegate.h"
#include "flutter/lib/ui/volatile_path_tracker.h"
#include "flutter/shell/common/platform_message_handler.h"
#include "third_party/dart/runtime/include/dart_api.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "third_party/tonic/dart_microtask_queue.h"
Expand All @@ -30,6 +31,7 @@ namespace flutter {
class FontSelector;
class ImageGeneratorRegistry;
class PlatformConfiguration;
class PlatformMessage;

class UIDartState : public tonic::DartState {
public:
Expand Down Expand Up @@ -106,6 +108,10 @@ class UIDartState : public tonic::DartState {
return platform_configuration_.get();
}

void SetPlatformMessageHandler(std::weak_ptr<PlatformMessageHandler> handler);

Dart_Handle HandlePlatformMessage(std::unique_ptr<PlatformMessage> message);

const TaskRunners& GetTaskRunners() const;

void ScheduleMicrotask(Dart_Handle handle);
Expand Down Expand Up @@ -153,6 +159,10 @@ class UIDartState : public tonic::DartState {
return unhandled_exception_callback_;
}

/// Returns a enumeration that that uniquely represents this root isolate.
/// Returns `0` if called from a non-root isolate.
int64_t GetRootIsolateToken() const;

protected:
UIDartState(TaskObserverAdd add_callback,
TaskObserverRemove remove_callback,
Expand Down Expand Up @@ -181,6 +191,7 @@ class UIDartState : public tonic::DartState {
const bool is_root_isolate_;
std::string debug_name_;
std::unique_ptr<PlatformConfiguration> platform_configuration_;
std::weak_ptr<PlatformMessageHandler> platform_message_handler_;
tonic::DartMicrotaskQueue microtask_queue_;
UnhandledExceptionCallback unhandled_exception_callback_;
LogMessageCallback log_message_callback_;
Expand Down
Loading