Skip to content

Commit

Permalink
Use leaf calls on possible platforms
Browse files Browse the repository at this point in the history
  • Loading branch information
temeddix committed Sep 12, 2024
1 parent 9b8d063 commit fb082e9
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 69 deletions.
67 changes: 9 additions & 58 deletions flutter_package/lib/src/interface_os.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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,
);
208 changes: 197 additions & 11 deletions flutter_package/lib/src/load_os.dart
Original file line number Diff line number Diff line change
@@ -1,31 +1,217 @@
import 'dart:io' as io;
import 'dart:ffi';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';

String? dynamicLibPath;

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

0 comments on commit fb082e9

Please sign in to comment.