From fb082e9e8f4a9bebdf7bb7dd9efd340f6d784da8 Mon Sep 17 00:00:00 2001 From: Donghyun Kim <temeddix@gmail.com> Date: Fri, 13 Sep 2024 02:03:42 +0900 Subject: [PATCH] Use leaf calls on possible platforms --- flutter_package/lib/src/interface_os.dart | 67 +------ flutter_package/lib/src/load_os.dart | 208 ++++++++++++++++++++-- 2 files changed, 206 insertions(+), 69 deletions(-) diff --git a/flutter_package/lib/src/interface_os.dart b/flutter_package/lib/src/interface_os.dart index c4a62121..a58ced68 100644 --- a/flutter_package/lib/src/interface_os.dart +++ b/flutter_package/lib/src/interface_os.dart @@ -15,12 +15,9 @@ void setCompiledLibPathReal(String path) { Future<void> prepareInterfaceReal( AssignRustSignal assignRustSignal, ) async { - // Load the native library. - loadRustLibrary(); - /// This should be called once at startup /// to enable `allo_isolate` to send data from the Rust side. - storeDartPostCObjectReal(NativeApi.postCObject); + rustLibrary.storeDartPostCObject(NativeApi.postCObject); // Prepare ports for communication over isolates. final rustSignalPort = ReceivePort(); @@ -50,67 +47,21 @@ Future<void> prepareInterfaceReal( }); // Make Rust prepare its isolate to send data to Dart. - prepareIsolateReal(rustSignalPort.sendPort.nativePort); + rustLibrary.prepareIsolate(rustSignalPort.sendPort.nativePort); } -@Native<Void Function()>( - isLeaf: true, - symbol: 'start_rust_logic_extern', -) -external void startRustLogicReal(); - -@Native<Void Function()>( - isLeaf: true, - symbol: 'stop_rust_logic_extern', -) -external void stopRustLogicReal(); +void startRustLogicReal() { + rustLibrary.startRustLogic(); +} -typedef SendDartSignalReal = Void Function( - Int32, - Pointer<Uint8>, - UintPtr, - Pointer<Uint8>, - UintPtr, -); -@Native<SendDartSignalReal>( - isLeaf: true, - symbol: 'send_dart_signal_extern', -) -external void sendDartSignalExtern( - int messageId, - Pointer<Uint8> messageBytesAddress, - int messageBytesLength, - Pointer<Uint8> binaryAddress, - int binaryLength, -); +void stopRustLogicReal() { + rustLibrary.stopRustLogic(); +} void sendDartSignalReal( int messageId, Uint8List messageBytes, Uint8List binary, ) { - sendDartSignalExtern( - messageId, - messageBytes.address, - messageBytes.length, - binary.address, - binary.length, - ); + rustLibrary.sendDartSignal(messageId, messageBytes, binary); } - -@Native<Void Function(Int64)>( - isLeaf: true, - symbol: 'prepare_isolate_extern', -) -external void prepareIsolateReal( - int port, -); - -typedef InnerFunction = Int8 Function(Int64, Pointer<Dart_CObject>); -@Native<Void Function(Pointer<NativeFunction<InnerFunction>>)>( - isLeaf: true, - symbol: 'store_dart_post_cobject', -) -external void storeDartPostCObjectReal( - Pointer<NativeFunction<InnerFunction>> postCObject, -); diff --git a/flutter_package/lib/src/load_os.dart b/flutter_package/lib/src/load_os.dart index 2672ba5f..4b5bc157 100644 --- a/flutter_package/lib/src/load_os.dart +++ b/flutter_package/lib/src/load_os.dart @@ -1,5 +1,7 @@ import 'dart:io' as io; import 'dart:ffi'; +import 'dart:typed_data'; +import 'package:ffi/ffi.dart'; String? dynamicLibPath; @@ -7,25 +9,209 @@ void setDynamicLibPath(String path) { dynamicLibPath = path; } -void loadRustLibrary() { +RustLibrary loadRustLibrary() { // Use provided dynamic library path if possible. + // Otherewise, use the default path. final path = dynamicLibPath; + DynamicLibrary lib; if (path != null) { - DynamicLibrary.open(path); - } - - // Otherewise, use the default path. - if (io.Platform.isLinux) { - DynamicLibrary.open('libhub.so'); + lib = DynamicLibrary.open(path); + } else if (io.Platform.isLinux) { + lib = DynamicLibrary.open('libhub.so'); } else if (io.Platform.isAndroid) { - DynamicLibrary.open('libhub.so'); + lib = DynamicLibrary.open('libhub.so'); } else if (io.Platform.isWindows) { - DynamicLibrary.open('hub.dll'); + lib = DynamicLibrary.open('hub.dll'); } else if (io.Platform.isIOS) { - DynamicLibrary.open('rinf.framework/rinf'); + lib = DynamicLibrary.open('rinf.framework/rinf'); } else if (io.Platform.isMacOS) { - DynamicLibrary.open('rinf.framework/rinf'); + lib = DynamicLibrary.open('rinf.framework/rinf'); } else { throw UnsupportedError('This operating system is not supported.'); } + + if (io.Platform.isAndroid) { + // On Android, native library symbols are loaded in local space + // because of Flutter's `RTLD_LOCAL` behavior. + // Therefore we cannot use the efficient `RustLibraryNew`. + // - https://github.com/dart-lang/native/issues/923 + return RustLibraryOld(lib); + } else { + // Native library symbols are loaded in global space + // thanks to Flutter's `RTLD_GLOBAL` behavior. + return RustLibraryNew(); + } +} + +// The central interface for calling native function. + +final rustLibrary = loadRustLibrary(); + +// Common type aliases. +// This is for better readability of the code. + +typedef PostCObjectInner = Int8 Function(Int64, Pointer<Dart_CObject>); +typedef PostCObjectFn = NativeFunction<PostCObjectInner>; + +// Direct access to global function symbols loaded in the process. +// These are available only if the native library is +// loaded into global process with `RTLD_GLOBAL` configuration. + +@Native<Void Function()>( + isLeaf: true, + symbol: 'start_rust_logic_extern', +) +external void startRustLogicExtern(); + +@Native<Void Function()>( + isLeaf: true, + symbol: 'stop_rust_logic_extern', +) +external void stopRustLogicExtern(); + +typedef SendDartSignalExtern = Void Function( + Int32, + Pointer<Uint8>, + UintPtr, + Pointer<Uint8>, + UintPtr, +); +@Native<SendDartSignalExtern>( + isLeaf: true, + symbol: 'send_dart_signal_extern', +) +external void sendDartSignalExtern( + int messageId, + Pointer<Uint8> messageBytesAddress, + int messageBytesLength, + Pointer<Uint8> binaryAddress, + int binaryLength, +); + +@Native<Void Function(Int64)>( + isLeaf: true, + symbol: 'prepare_isolate_extern', +) +external void prepareIsolateExtern( + int port, +); + +@Native<Void Function(Pointer<PostCObjectFn>)>( + isLeaf: true, + symbol: 'store_dart_post_cobject', +) +external void storeDartPostCObjectExtern( + Pointer<PostCObjectFn> postCObject, +); + +/// Abstract class for unifying the interface +/// for calling native functions. +abstract class RustLibrary { + void startRustLogic(); + void stopRustLogic(); + void sendDartSignal( + int messageId, + Uint8List messageBytes, + Uint8List binary, + ); + void prepareIsolate(int port); + void storeDartPostCObject(Pointer<PostCObjectFn> postCObject); +} + +/// Class for global native library symbols loaded with `RTLD_GLOBAL`. +/// This is the efficient and ideal way to call native code. +class RustLibraryNew extends RustLibrary { + void startRustLogic() { + startRustLogicExtern(); + } + + void stopRustLogic() { + stopRustLogicExtern(); + } + + void sendDartSignal( + int messageId, + Uint8List messageBytes, + Uint8List binary, + ) { + sendDartSignalExtern( + messageId, + messageBytes.address, + messageBytes.length, + binary.address, + binary.length, + ); + } + + void prepareIsolate(int port) { + prepareIsolateExtern(port); + } + + void storeDartPostCObject(Pointer<PostCObjectFn> postCObject) { + storeDartPostCObjectExtern(postCObject); + } +} + +/// Class for local native library symbols loaded with `RTLD_LOCAL`. +/// This is relatively inefficient because `malloc.allocate` is required. +/// `@Native` attributes can only used on global native symbols. +class RustLibraryOld extends RustLibrary { + final DynamicLibrary lib; + RustLibraryOld(this.lib); + + void storeDartPostCObject(Pointer<PostCObjectFn> postCObject) { + final rustFunction = lib.lookupFunction< + Pointer Function(Pointer<PostCObjectFn>), + Pointer Function(Pointer<PostCObjectFn>)>( + 'store_dart_post_cobject', + ); + rustFunction(postCObject); + } + + void startRustLogic() { + final rustFunction = lib.lookupFunction<Void Function(), void Function()>( + 'start_rust_logic_extern', + ); + rustFunction(); + } + + void stopRustLogic() { + final rustFunction = lib.lookupFunction<Void Function(), void Function()>( + 'stop_rust_logic_extern', + ); + rustFunction(); + } + + void sendDartSignal(int messageId, Uint8List messageBytes, Uint8List binary) { + final Pointer<Uint8> messageMemory = malloc.allocate(messageBytes.length); + messageMemory.asTypedList(messageBytes.length).setAll(0, messageBytes); + + final Pointer<Uint8> binaryMemory = malloc.allocate(binary.length); + binaryMemory.asTypedList(binary.length).setAll(0, binary); + + final rustFunction = lib.lookupFunction< + Void Function(Int32, Pointer<Uint8>, UintPtr, Pointer<Uint8>, UintPtr), + void Function(int, Pointer<Uint8>, int, Pointer<Uint8>, int)>( + 'send_dart_signal_extern', + ); + + rustFunction( + messageId, + messageMemory, + messageBytes.length, + binaryMemory, + binary.length, + ); + + malloc.free(messageMemory); + malloc.free(binaryMemory); + } + + void prepareIsolate(int port) { + final rustFunction = + lib.lookupFunction<Void Function(Int64), void Function(int)>( + 'prepare_isolate_extern', + ); + rustFunction(port); + } }