Skip to content

Commit

Permalink
Merge pull request #425 from cunarist/leaf-call-when-possible
Browse files Browse the repository at this point in the history
Use leaf calls when possible to reduce memory copy
  • Loading branch information
temeddix authored Sep 12, 2024
2 parents 69d9a90 + 75a21a9 commit 3709130
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 71 deletions.
2 changes: 1 addition & 1 deletion flutter_package/example/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ buildscript {
}

dependencies {
classpath 'com.android.tools.build:gradle:7.3.0'
classpath 'com.android.tools.build:gradle:8.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
2 changes: 1 addition & 1 deletion flutter_package/example/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import UIKit
import Flutter

@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
Expand Down
2 changes: 1 addition & 1 deletion flutter_package/example/macos/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Cocoa
import FlutterMacOS

@NSApplicationMain
@main
class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
Expand Down
60 changes: 7 additions & 53 deletions flutter_package/lib/src/interface_os.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:ffi';
import 'dart:typed_data';
import 'load_os.dart';
import 'package:ffi/ffi.dart';
import 'dart:async';
import 'dart:isolate';
import 'interface.dart';
Expand All @@ -18,14 +17,7 @@ Future<void> prepareInterfaceReal(
) async {
/// This should be called once at startup
/// to enable `allo_isolate` to send data from the Rust side.
final rustFunction = rustLibrary.lookupFunction<
Pointer Function(
Pointer<NativeFunction<Int8 Function(Int64, Pointer<Dart_CObject>)>>,
),
Pointer Function(
Pointer<NativeFunction<Int8 Function(Int64, Pointer<Dart_CObject>)>>,
)>('store_dart_post_cobject');
rustFunction(NativeApi.postCObject);
rustLibrary.storeDartPostCObject(NativeApi.postCObject);

// Prepare ports for communication over isolates.
final rustSignalPort = ReceivePort();
Expand Down Expand Up @@ -55,59 +47,21 @@ Future<void> prepareInterfaceReal(
});

// Make Rust prepare its isolate to send data to Dart.
prepareIsolateReal(rustSignalPort.sendPort.nativePort);
rustLibrary.prepareIsolate(rustSignalPort.sendPort.nativePort);
}

void startRustLogicReal() {
final rustFunction =
rustLibrary.lookupFunction<Void Function(), void Function()>(
'start_rust_logic_extern',
);
rustFunction();
rustLibrary.startRustLogic();
}

void stopRustLogicReal() {
final rustFunction =
rustLibrary.lookupFunction<Void Function(), void Function()>(
'stop_rust_logic_extern',
);
rustFunction();
rustLibrary.stopRustLogic();
}

/// Sends bytes to Rust.
Future<void> sendDartSignalReal(
void sendDartSignalReal(
int messageId,
Uint8List messageBytes,
Uint8List binary,
) async {
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 = rustLibrary.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 prepareIsolateReal(int port) {
final rustFunction =
rustLibrary.lookupFunction<Void Function(Int64), void Function(int)>(
'prepare_isolate_extern',
);
rustFunction(port);
) {
rustLibrary.sendDartSignal(messageId, messageBytes, binary);
}
227 changes: 215 additions & 12 deletions flutter_package/lib/src/load_os.dart
Original file line number Diff line number Diff line change
@@ -1,32 +1,235 @@
import 'dart:io' as io;
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';

String? dynamicLibPath;
final rustLibrary = loadRustLibrary();

void setDynamicLibPath(String path) {
dynamicLibPath = path;
}

DynamicLibrary loadRustLibrary() {
RustLibrary loadRustLibrary() {
// Use provided dynamic library path if possible.
// Otherewise, use the default path.
final path = dynamicLibPath;
DynamicLibrary lib;
if (path != null) {
return DynamicLibrary.open(path);
}

// Otherewise, use the default path.
if (io.Platform.isLinux) {
return DynamicLibrary.open('libhub.so');
lib = DynamicLibrary.open(path);
} else if (io.Platform.isLinux) {
lib = DynamicLibrary.open('libhub.so');
} else if (io.Platform.isAndroid) {
return DynamicLibrary.open('libhub.so');
lib = DynamicLibrary.open('libhub.so');
} else if (io.Platform.isWindows) {
return DynamicLibrary.open('hub.dll');
lib = DynamicLibrary.open('hub.dll');
} else if (io.Platform.isIOS) {
return DynamicLibrary.open('rinf.framework/rinf');
lib = DynamicLibrary.open('rinf.framework/rinf');
} else if (io.Platform.isMacOS) {
return 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 PostCObjectPtr = Pointer<NativeFunction<PostCObjectInner>>;
typedef SendDartSignalExtern = Void Function(
Int32,
Pointer<Uint8>,
UintPtr,
Pointer<Uint8>,
UintPtr,
);
typedef SendDartSignalWrap = void Function(
int,
Pointer<Uint8>,
int,
Pointer<Uint8>,
int,
);

// Direct access to global function symbols loaded in the process.
// These are available only if the native library is
// loaded into global space 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();

@Native<Void Function(Int64)>(
isLeaf: true,
symbol: 'prepare_isolate_extern',
)
external void prepareIsolateExtern(
int port,
);

@Native<Void Function(PostCObjectPtr)>(
isLeaf: true,
symbol: 'store_dart_post_cobject',
)
external void storeDartPostCObjectExtern(
PostCObjectPtr postCObject,
);

@Native<SendDartSignalExtern>(
isLeaf: true,
symbol: 'send_dart_signal_extern',
)
external void sendDartSignalExtern(
int messageId,
Pointer<Uint8> messageBytesAddress,
int messageBytesLength,
Pointer<Uint8> binaryAddress,
int binaryLength,
);

/// Abstract class for unifying the interface
/// for calling native functions.
abstract class RustLibrary {
void startRustLogic();
void stopRustLogic();
void prepareIsolate(int port);
void storeDartPostCObject(PostCObjectPtr postCObject);
void sendDartSignal(
int messageId,
Uint8List messageBytes,
Uint8List binary,
);
}

/// Class for global native library symbols loaded with `RTLD_GLOBAL`.
/// This is the efficient and ideal way to call native code.
/// `@Native` decorator with `isLeaf` parameter
/// that enables the `Uint8List.address` syntax
/// can only used on global native symbols.
/// - https://github.com/dart-lang/sdk/issues/44589
class RustLibraryNew extends RustLibrary {
void startRustLogic() {
startRustLogicExtern();
}

void stopRustLogic() {
stopRustLogicExtern();
}

void prepareIsolate(int port) {
prepareIsolateExtern(port);
}

void storeDartPostCObject(PostCObjectPtr postCObject) {
storeDartPostCObjectExtern(postCObject);
}

void sendDartSignal(
int messageId,
Uint8List messageBytes,
Uint8List binary,
) {
sendDartSignalExtern(
messageId,
messageBytes.address,
messageBytes.length,
binary.address,
binary.length,
);
}
}

/// Class for local native library symbols loaded with `RTLD_LOCAL`.
/// This is relatively inefficient because `malloc.allocate` is required.
class RustLibraryOld extends RustLibrary {
late DynamicLibrary lib;
late void Function() startRustLogicExtern;
late void Function() stopRustLogicExtern;
late void Function(int) prepareIsolateExtern;
late void Function(PostCObjectPtr) storeDartPostCObjectExtern;
late void Function(int, Pointer<Uint8>, int, Pointer<Uint8>, int)
sendDartSignalExtern;

RustLibraryOld(DynamicLibrary lib) {
this.lib = lib;
this.startRustLogicExtern =
lib.lookupFunction<Void Function(), void Function()>(
'start_rust_logic_extern',
);
this.stopRustLogicExtern =
lib.lookupFunction<Void Function(), void Function()>(
'stop_rust_logic_extern',
);
this.prepareIsolateExtern =
lib.lookupFunction<Void Function(Int64), void Function(int)>(
'prepare_isolate_extern',
);
this.storeDartPostCObjectExtern = lib.lookupFunction<
Void Function(PostCObjectPtr), void Function(PostCObjectPtr)>(
'store_dart_post_cobject',
);
this.sendDartSignalExtern =
lib.lookupFunction<SendDartSignalExtern, SendDartSignalWrap>(
'send_dart_signal_extern',
);
}

void startRustLogic() {
startRustLogicExtern();
}

void stopRustLogic() {
stopRustLogicExtern();
}

void prepareIsolate(int port) {
prepareIsolateExtern(port);
}

void storeDartPostCObject(PostCObjectPtr postCObject) {
storeDartPostCObjectExtern(postCObject);
}

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);

sendDartSignalExtern(
messageId,
messageMemory,
messageBytes.length,
binaryMemory,
binary.length,
);

malloc.free(messageMemory);
malloc.free(binaryMemory);
}
}
4 changes: 2 additions & 2 deletions flutter_package/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ version: 6.15.0
repository: https://github.com/cunarist/rinf

environment:
sdk: ">=3.0.5 <4.0.0"
flutter: ">=3.3.0"
sdk: ">=3.5.0 <4.0.0"
flutter: ">=3.22.0"

dependencies:
flutter:
Expand Down

0 comments on commit 3709130

Please sign in to comment.