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

Commit

Permalink
Started handling messages from background isolates for iOS (#35174)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaaclarke authored Sep 8, 2022
1 parent 55c4a39 commit 2be73ba
Show file tree
Hide file tree
Showing 29 changed files with 564 additions and 24 deletions.
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;

/// 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

0 comments on commit 2be73ba

Please sign in to comment.