Skip to content

Commit 26ba2b8

Browse files
Merge PR #474: Cache FFI lookups in Flutter native bridge (rebase of #457)
Caches Flutter FFI symbol lookups (dlsym/lookupFunction) to run once per process via Dart static final lazy init instead of on every bridge call, plus fixes a voice-agent getHandle() init race. Original author: @Reef-7 (via sanchitmonga22 revival) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2 parents 49279b7 + 6911f23 commit 26ba2b8

8 files changed

Lines changed: 482 additions & 361 deletions

File tree

examples/ios/RunAnywhereAI/Package.resolved

Lines changed: 0 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_llm.dart

Lines changed: 13 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import 'package:ffi/ffi.dart';
2121

2222
import 'package:runanywhere/foundation/logging/sdk_logger.dart';
2323
import 'package:runanywhere/native/ffi_types.dart';
24+
import 'package:runanywhere/native/native_functions.dart';
2425
import 'package:runanywhere/native/platform_loader.dart';
2526

2627
/// LLM component bridge for C++ interop.
@@ -78,13 +79,9 @@ class DartBridgeLLM {
7879
}
7980

8081
try {
81-
final lib = PlatformLoader.loadCommons();
82-
final create = lib.lookupFunction<Int32 Function(Pointer<RacHandle>),
83-
int Function(Pointer<RacHandle>)>('rac_llm_component_create');
84-
8582
final handlePtr = calloc<RacHandle>();
8683
try {
87-
final result = create(handlePtr);
84+
final result = NativeFunctions.llmCreate(handlePtr);
8885

8986
if (result != RAC_SUCCESS) {
9087
throw StateError(
@@ -111,11 +108,7 @@ class DartBridgeLLM {
111108
if (_handle == null) return false;
112109

113110
try {
114-
final lib = PlatformLoader.loadCommons();
115-
final isLoadedFn = lib.lookupFunction<Int32 Function(RacHandle),
116-
int Function(RacHandle)>('rac_llm_component_is_loaded');
117-
118-
return isLoadedFn(_handle!) == RAC_TRUE;
111+
return NativeFunctions.llmIsLoaded(_handle!) == RAC_TRUE;
119112
} catch (e) {
120113
_logger.debug('isLoaded check failed: $e');
121114
return false;
@@ -130,11 +123,7 @@ class DartBridgeLLM {
130123
if (_handle == null) return false;
131124

132125
try {
133-
final lib = PlatformLoader.loadCommons();
134-
final supportsStreamingFn = lib.lookupFunction<Int32 Function(RacHandle),
135-
int Function(RacHandle)>('rac_llm_component_supports_streaming');
136-
137-
return supportsStreamingFn(_handle!) == RAC_TRUE;
126+
return NativeFunctions.llmSupportsStreaming(_handle!) == RAC_TRUE;
138127
} catch (e) {
139128
return false;
140129
}
@@ -161,16 +150,8 @@ class DartBridgeLLM {
161150
final namePtr = modelName.toNativeUtf8();
162151

163152
try {
164-
final lib = PlatformLoader.loadCommons();
165-
final loadModelFn = lib.lookupFunction<
166-
Int32 Function(
167-
RacHandle, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>),
168-
int Function(RacHandle, Pointer<Utf8>, Pointer<Utf8>,
169-
Pointer<Utf8>)>('rac_llm_component_load_model');
170-
171-
_logger.debug(
172-
'Calling rac_llm_component_load_model with handle: $_handle, path: $modelPath');
173-
final result = loadModelFn(handle, pathPtr, idPtr, namePtr);
153+
_logger.debug('Calling rac_llm_component_load_model with handle=$handle');
154+
final result = NativeFunctions.llmLoadModel(handle, pathPtr, idPtr, namePtr);
174155
_logger.debug(
175156
'rac_llm_component_load_model returned: $result (${RacResultCode.getMessage(result)})');
176157

@@ -194,11 +175,7 @@ class DartBridgeLLM {
194175
if (_handle == null) return;
195176

196177
try {
197-
final lib = PlatformLoader.loadCommons();
198-
final cleanupFn = lib.lookupFunction<Int32 Function(RacHandle),
199-
int Function(RacHandle)>('rac_llm_component_cleanup');
200-
201-
cleanupFn(_handle!);
178+
NativeFunctions.llmCleanup(_handle!);
202179
_loadedModelId = null;
203180
_logger.info('LLM model unloaded');
204181
} catch (e) {
@@ -211,11 +188,7 @@ class DartBridgeLLM {
211188
if (_handle == null) return;
212189

213190
try {
214-
final lib = PlatformLoader.loadCommons();
215-
final cancelFn = lib.lookupFunction<Int32 Function(RacHandle),
216-
int Function(RacHandle)>('rac_llm_component_cancel');
217-
218-
cancelFn(_handle!);
191+
NativeFunctions.llmCancel(_handle!);
219192
_logger.debug('LLM generation cancelled');
220193
} catch (e) {
221194
_logger.error('Failed to cancel generation: $e');
@@ -373,11 +346,7 @@ class DartBridgeLLM {
373346
void destroy() {
374347
if (_handle != null) {
375348
try {
376-
final lib = PlatformLoader.loadCommons();
377-
final destroyFn = lib.lookupFunction<Void Function(RacHandle),
378-
void Function(RacHandle)>('rac_llm_component_destroy');
379-
380-
destroyFn(_handle!);
349+
NativeFunctions.llmDestroy(_handle!);
381350
_handle = null;
382351
_loadedModelId = null;
383352
_logger.debug('LLM component destroyed');
@@ -486,7 +455,7 @@ void _streamingIsolateEntry(_StreamingIsolateParams params) {
486455
// Set systemPrompt if provided
487456
if (params.systemPrompt != null && params.systemPrompt!.isNotEmpty) {
488457
systemPromptPtr = params.systemPrompt!.toNativeUtf8();
489-
optionsPtr.ref.systemPrompt = systemPromptPtr!;
458+
optionsPtr.ref.systemPrompt = systemPromptPtr;
490459
} else {
491460
optionsPtr.ref.systemPrompt = nullptr;
492461
}
@@ -553,7 +522,7 @@ void _streamingIsolateEntry(_StreamingIsolateParams params) {
553522
calloc.free(promptPtr);
554523
calloc.free(optionsPtr);
555524
if (systemPromptPtr != null) {
556-
calloc.free(systemPromptPtr!);
525+
calloc.free(systemPromptPtr);
557526
}
558527
_isolateSendPort = null;
559528
}
@@ -622,7 +591,7 @@ _IsolateGenerationResult _generateInIsolate(
622591
// Set systemPrompt if provided
623592
if (systemPrompt != null && systemPrompt.isNotEmpty) {
624593
systemPromptPtr = systemPrompt.toNativeUtf8();
625-
optionsPtr.ref.systemPrompt = systemPromptPtr!;
594+
optionsPtr.ref.systemPrompt = systemPromptPtr;
626595
} else {
627596
optionsPtr.ref.systemPrompt = nullptr;
628597
}
@@ -658,7 +627,7 @@ _IsolateGenerationResult _generateInIsolate(
658627
calloc.free(optionsPtr);
659628
calloc.free(resultPtr);
660629
if (systemPromptPtr != null) {
661-
calloc.free(systemPromptPtr!);
630+
calloc.free(systemPromptPtr);
662631
}
663632
}
664633
}

sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dart

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'package:ffi/ffi.dart';
1212

1313
import 'package:runanywhere/foundation/logging/sdk_logger.dart';
1414
import 'package:runanywhere/native/ffi_types.dart';
15+
import 'package:runanywhere/native/native_functions.dart';
1516
import 'package:runanywhere/native/platform_loader.dart';
1617

1718
/// STT component bridge for C++ interop.
@@ -48,13 +49,9 @@ class DartBridgeSTT {
4849
}
4950

5051
try {
51-
final lib = PlatformLoader.loadCommons();
52-
final create = lib.lookupFunction<Int32 Function(Pointer<RacHandle>),
53-
int Function(Pointer<RacHandle>)>('rac_stt_component_create');
54-
5552
final handlePtr = calloc<RacHandle>();
5653
try {
57-
final result = create(handlePtr);
54+
final result = NativeFunctions.sttCreate(handlePtr);
5855

5956
if (result != RAC_SUCCESS) {
6057
throw StateError(
@@ -81,11 +78,7 @@ class DartBridgeSTT {
8178
if (_handle == null) return false;
8279

8380
try {
84-
final lib = PlatformLoader.loadCommons();
85-
final isLoadedFn = lib.lookupFunction<Int32 Function(RacHandle),
86-
int Function(RacHandle)>('rac_stt_component_is_loaded');
87-
88-
return isLoadedFn(_handle!) == RAC_TRUE;
81+
return NativeFunctions.sttIsLoaded(_handle!) == RAC_TRUE;
8982
} catch (e) {
9083
_logger.debug('isLoaded check failed: $e');
9184
return false;
@@ -100,11 +93,7 @@ class DartBridgeSTT {
10093
if (_handle == null) return false;
10194

10295
try {
103-
final lib = PlatformLoader.loadCommons();
104-
final supportsStreamingFn = lib.lookupFunction<Int32 Function(RacHandle),
105-
int Function(RacHandle)>('rac_stt_component_supports_streaming');
106-
107-
return supportsStreamingFn(_handle!) == RAC_TRUE;
96+
return NativeFunctions.sttSupportsStreaming(_handle!) == RAC_TRUE;
10897
} catch (e) {
10998
return false;
11099
}
@@ -131,14 +120,7 @@ class DartBridgeSTT {
131120
final namePtr = modelName.toNativeUtf8();
132121

133122
try {
134-
final lib = PlatformLoader.loadCommons();
135-
final loadModelFn = lib.lookupFunction<
136-
Int32 Function(
137-
RacHandle, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>),
138-
int Function(RacHandle, Pointer<Utf8>, Pointer<Utf8>,
139-
Pointer<Utf8>)>('rac_stt_component_load_model');
140-
141-
final result = loadModelFn(handle, pathPtr, idPtr, namePtr);
123+
final result = NativeFunctions.sttLoadModel(handle, pathPtr, idPtr, namePtr);
142124

143125
if (result != RAC_SUCCESS) {
144126
throw StateError(
@@ -160,11 +142,7 @@ class DartBridgeSTT {
160142
if (_handle == null) return;
161143

162144
try {
163-
final lib = PlatformLoader.loadCommons();
164-
final cleanupFn = lib.lookupFunction<Int32 Function(RacHandle),
165-
int Function(RacHandle)>('rac_stt_component_cleanup');
166-
167-
cleanupFn(_handle!);
145+
NativeFunctions.sttCleanup(_handle!);
168146
_loadedModelId = null;
169147
_logger.info('STT model unloaded');
170148
} catch (e) {
@@ -357,11 +335,7 @@ class DartBridgeSTT {
357335
void destroy() {
358336
if (_handle != null) {
359337
try {
360-
final lib = PlatformLoader.loadCommons();
361-
final destroyFn = lib.lookupFunction<Void Function(RacHandle),
362-
void Function(RacHandle)>('rac_stt_component_destroy');
363-
364-
destroyFn(_handle!);
338+
NativeFunctions.sttDestroy(_handle!);
365339
_handle = null;
366340
_loadedModelId = null;
367341
_logger.debug('STT component destroyed');

sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tts.dart

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'package:ffi/ffi.dart';
1212

1313
import 'package:runanywhere/foundation/logging/sdk_logger.dart';
1414
import 'package:runanywhere/native/ffi_types.dart';
15+
import 'package:runanywhere/native/native_functions.dart';
1516
import 'package:runanywhere/native/platform_loader.dart';
1617

1718
/// TTS component bridge for C++ interop.
@@ -48,13 +49,9 @@ class DartBridgeTTS {
4849
}
4950

5051
try {
51-
final lib = PlatformLoader.loadCommons();
52-
final create = lib.lookupFunction<Int32 Function(Pointer<RacHandle>),
53-
int Function(Pointer<RacHandle>)>('rac_tts_component_create');
54-
5552
final handlePtr = calloc<RacHandle>();
5653
try {
57-
final result = create(handlePtr);
54+
final result = NativeFunctions.ttsCreate(handlePtr);
5855

5956
if (result != RAC_SUCCESS) {
6057
throw StateError(
@@ -81,11 +78,7 @@ class DartBridgeTTS {
8178
if (_handle == null) return false;
8279

8380
try {
84-
final lib = PlatformLoader.loadCommons();
85-
final isLoadedFn = lib.lookupFunction<Int32 Function(RacHandle),
86-
int Function(RacHandle)>('rac_tts_component_is_loaded');
87-
88-
return isLoadedFn(_handle!) == RAC_TRUE;
81+
return NativeFunctions.ttsIsLoaded(_handle!) == RAC_TRUE;
8982
} catch (e) {
9083
_logger.debug('isLoaded check failed: $e');
9184
return false;
@@ -116,14 +109,7 @@ class DartBridgeTTS {
116109
final namePtr = voiceName.toNativeUtf8();
117110

118111
try {
119-
final lib = PlatformLoader.loadCommons();
120-
final loadVoiceFn = lib.lookupFunction<
121-
Int32 Function(
122-
RacHandle, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>),
123-
int Function(RacHandle, Pointer<Utf8>, Pointer<Utf8>,
124-
Pointer<Utf8>)>('rac_tts_component_load_voice');
125-
126-
final result = loadVoiceFn(handle, pathPtr, idPtr, namePtr);
112+
final result = NativeFunctions.ttsLoadVoice(handle, pathPtr, idPtr, namePtr);
127113

128114
if (result != RAC_SUCCESS) {
129115
throw StateError(
@@ -145,11 +131,7 @@ class DartBridgeTTS {
145131
if (_handle == null) return;
146132

147133
try {
148-
final lib = PlatformLoader.loadCommons();
149-
final cleanupFn = lib.lookupFunction<Int32 Function(RacHandle),
150-
int Function(RacHandle)>('rac_tts_component_cleanup');
151-
152-
cleanupFn(_handle!);
134+
NativeFunctions.ttsCleanup(_handle!);
153135
_loadedVoiceId = null;
154136
_logger.info('TTS voice unloaded');
155137
} catch (e) {
@@ -162,11 +144,7 @@ class DartBridgeTTS {
162144
if (_handle == null) return;
163145

164146
try {
165-
final lib = PlatformLoader.loadCommons();
166-
final stopFn = lib.lookupFunction<Int32 Function(RacHandle),
167-
int Function(RacHandle)>('rac_tts_component_stop');
168-
169-
stopFn(_handle!);
147+
NativeFunctions.ttsStop(_handle!);
170148
_logger.debug('TTS synthesis stopped');
171149
} catch (e) {
172150
_logger.error('Failed to stop TTS: $e');
@@ -335,11 +313,7 @@ class DartBridgeTTS {
335313
void destroy() {
336314
if (_handle != null) {
337315
try {
338-
final lib = PlatformLoader.loadCommons();
339-
final destroyFn = lib.lookupFunction<Void Function(RacHandle),
340-
void Function(RacHandle)>('rac_tts_component_destroy');
341-
342-
destroyFn(_handle!);
316+
NativeFunctions.ttsDestroy(_handle!);
343317
_handle = null;
344318
_loadedVoiceId = null;
345319
_logger.debug('TTS component destroyed');

0 commit comments

Comments
 (0)