Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

flutter/dart: How to use async callback with Dart FFI?

My app's backend is written in C++ and the frontend in Dart/flutter. I'd love to have the backend notify frontend whenever data is ready. This requires implementing an async callback scheme between Dart and C++.

Environment

$ flutter doctor -v
[✓] Flutter (Channel stable, 1.20.1, on Mac OS X 10.15.5 19F101, locale
    en-CN)
    • Flutter version 1.20.1 at /Applications/Android/flutter
    • Framework revision 2ae34518b8 (2 days ago), 2020-08-05 19:53:19 -0700
    • Engine revision c8e3b94853
    • Dart version 2.9.0
    • Pub download mirror https://pub.flutter-io.cn
    • Flutter download mirror https://storage.flutter-io.cn

 
[✓] Android toolchain - develop for Android devices (Android SDK version
    30.0.1)
    • Android SDK at /Applications/Android/sdk
    • Platform android-30, build-tools 30.0.1
    • ANDROID_HOME = /Applications/Android/sdk
    • ANDROID_SDK_ROOT = /Applications/Android/sdk
    • Java binary at: /Applications/Android
      Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build
      1.8.0_242-release-1644-b3-6222593)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 11.6)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 11.6, Build version 11E708
    • CocoaPods version 1.8.4

[✓] Android Studio (version 4.0)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 48.0.2
    • Dart plugin version 193.7361
    • Java version OpenJDK Runtime Environment (build
      1.8.0_242-release-1644-b3-6222593)

[✓] VS Code (version 1.47.3)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.8.1

 
[!] Connected device                          
    ! No devices available

What I did

I looked at the github example from the Dart team.

To adapt the sample code to a flutter project, I did the following

  • Create a FFI plugin project
  • Replace the main function with the sample function
  • Remove unimportant dependencies such as expect
  • Create native library using the referenced C++ code such as dart-sdk-master/runtime/bin/ffi_test/ffi_test_functions.cc

Problems

However, I couldn't get the sample to run.

Dart version

The first problem I had was that the dart version was not supported when I run the sample directly. I'm on latest stable channel, and also tried dev channel. The error says

Error: The specified language version is too high. The highest supported language version is 2.9.

Native compilation

The information on the native-side implementation is missing in the example. So I'm left with lots of compiler errors.

Launching lib/main.dart on ONEPLUS A6000 in debug mode...
Running Gradle task 'assembleDebug'...

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:externalNativeBuildDebug'.
> Build command failed.
  Error while executing process /Applications/Android/sdk/cmake/3.6.4111459/bin/cmake with arguments {--build /path/to/ffiasync/example/android/app/.cxx/cmake/debug/armeabi-v7a --target ffiasync}
  [1/2] Building CXX object CMakeFiles/ffiasync.dir/path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc.o
  [2/2] Linking CXX shared library /path/to/ffiasync/example/build/app/intermediates/cmake/debug/obj/armeabi-v7a/libffiasync.so
  FAILED: : && /Applications/Android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++  --target=armv7-none-linux-androideabi16 --gcc-toolchain=/Applications/Android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64 --sysroot=/Applications/Android/sdk/ndk/21.3.6528147/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -fPIC -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -march=armv7-a -mthumb -Wformat -Werror=format-security   -O0 -fno-limit-debug-info  -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libgcc_real.a -Wl,--exclude-libs,libatomic.a -static-libstdc++ -Wl,--build-id -Wl,--fatal-warnings -Wl,--exclude-libs,libunwind.a -Wl,--no-undefined -Qunused-arguments -shared -Wl,-soname,libffiasync.so -o /path/to/ffiasync/example/build/app/intermediates/cmake/debug/obj/armeabi-v7a/libffiasync.so CMakeFiles/ffiasync.dir/path/to/ffiasync/lib/ffiasync.cpp.o CMakeFiles/ffiasync.dir/path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc.o  -latomic -lm && :
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:54: error: undefined reference to 'Dart_ExecuteInternalCommand'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:60: error: undefined reference to 'Dart_ExecuteInternalCommand'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:66: error: undefined reference to 'Dart_ExecuteInternalCommand'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:74: error: undefined reference to 'Dart_ExecuteInternalCommand'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:143: error: undefined reference to 'Dart_CurrentIsolate'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:183: error: undefined reference to 'ClobberAndCall'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:277: error: undefined reference to 'Dart_InitializeApiDL(void*)'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:288: error: undefined reference to 'Dart_DumpNativeStackTrace'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:289: error: undefined reference to 'Dart_PrepareToAbort'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_PostCObject_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_NewNativePort_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_PostCObject_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_CloseNativePort_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_PostCObject_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:791: error: undefined reference to 'Dart_NewPersistentHandle'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:793: error: undefined reference to 'Dart_HandleFromPersistent'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:794: error: undefined reference to 'Dart_DeletePersistentHandle'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:795: error: undefined reference to 'Dart_IsError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:796: error: undefined reference to 'Dart_PropagateError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:800: error: undefined reference to 'Dart_IsNull'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:802: error: undefined reference to 'Dart_NewWeakPersistentHandle'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:804: error: undefined reference to 'Dart_HandleFromWeakPersistent'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:809: error: undefined reference to 'Dart_DeleteWeakPersistentHandle'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:826: error: undefined reference to 'Dart_IsError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:829: error: undefined reference to 'Dart_PropagateError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:846: error: undefined reference to 'Dart_NewStringFromCString'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:847: error: undefined reference to 'Dart_IsError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:848: error: undefined reference to 'Dart_PropagateError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:850: error: undefined reference to 'Dart_NewInteger'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:851: error: undefined reference to 'Dart_IsError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:852: error: undefined reference to 'Dart_PropagateError'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:855: error: undefined reference to 'Dart_Invoke'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:874: error: undefined reference to 'Dart_NewStringFromCString'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:879: error: undefined reference to 'Dart_GetField'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:885: error: undefined reference to 'Dart_IntegerToInt64'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:895: error: undefined reference to 'Dart_EnterScope'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:900: error: undefined reference to 'Dart_ExitScope'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:905: error: undefined reference to 'Dart_True'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_NewPersistentHandle_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_HandleFromPersistent_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_SetPersistentHandle_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_DeletePersistentHandle_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_NewWeakPersistentHandle_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_HandleFromWeakPersistent_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_DeleteWeakPersistentHandle_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_NewPersistentHandle_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_HandleFromPersistent_DL'
  /path/to/ffiasync/lib/ffi_test_functions_vmspecific.cc:0: error: undefined reference to 'Dart_DeletePersistentHandle_DL'
  clang++: error: linker command failed with exit code 1 (use -v to see invocation)
  ninja: build stopped: subcommand failed.
  


* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 12s
Exception: Gradle task assembleDebug failed with exit code 1

Questions

Unable to get answers from Dart team, I have a few questions

  • Should I build Dart runtime from source to make the async callback work? The errors above seems to say I'm missing runtime support. Does that mean FFI async callback is not included in the official dart/flutter releases?
  • Is there a ready-to-run ffi async callback sample or documentation somewhere?

I'm afraid that with this little info, it would be too difficult to figure it out by trail-and-error.

like image 754
kakyo Avatar asked Aug 08 '20 01:08

kakyo


2 Answers

It might help to look at my new repository - I managed to have a JUCE (C++) Backend running its own thread that can call Flutter UI: JucyFluttering

like image 92
audiooffler Avatar answered Nov 19 '22 03:11

audiooffler


Agreed, it's not clear how you link against the Dart SDK executable(?)/shared library(?) while building your Android shared library or your iOS code. It compiles, but won't link.

Given that Dart is single-threaded and the async callback technique involves signalling the main Dart thread to indicate that it's time to call down to C to allow a callback to take place on that single, main thread, I've never seen much advantage over, say, polling from Dart (maybe from a timer) to see whether the response is ready. At 60fps, is there much point knowing that the response is ready half way through a 16ms inter-frame interval? The change isn't going to reflect until the next paint anyway. Sure, it doesn't feel very efficient to poll, but once every 16ms isn't that expensive.

like image 36
Richard Heap Avatar answered Nov 19 '22 03:11

Richard Heap