Flutter (Dart) ffi - Aplication freezes during processing external library methohd

463

Async Callbacks

The problem is that C routines called from dart are blocking and therefore congest the single existing dart isolate, consequently freezing the UI.

To work around this problem you have to open a port on the dart isolate through which your C routines can asynchronously send messages to the dart isolate. To signal to the dart compiler that this is a non-blocking operation, simply delay the completion of the function until a message on the designated port has been received.

Future<int> asyncData() async {
  var receiveData;
  bool receivedCallback = false;

  var receivePort = ReceivePort()..listen((data) {
    print('Received data from c');
    receiveData = data;
    receivedCallback = true;
  });
  var nativeSendPort = receivePort.sendPort.nativePort;

  nativeTriggerFunction(nativeSendPort);

  while(!receivedCallback) {
    await Future.delayed(Duration(milliseconds: 100));
  }

  receivePort.close();
  return receiveData;
}

In C, you need to create a trigger function which should ideally be as lightweight as possible, passing the port number to your C code and calling the actual function you want to execute on a different thread. The trigger function will finish almost instantly, allowing your dart thread to do other work and as soon as the newly created thread is done, it sends its result through the native port back to the dart isolate which can pick up where it left off.

void native_trigger_function(Dart_Port port) {
  pthread_t t;
  Dart_Port *args= malloc(sizeof(Dart_Port));

  pthread_create(&t, NULL, _native_function, args);
}

void *_native_function(void *args) {
  Dart_Port port = *(Dart_Port *)args;
  int rc = 0;

  // do some heavy work
  
  // send return code to dart
  Dart_CObject obj;
  obj.type = Dart_CObject_kInt32;
  obj.value.as_int32 = rc;
  Dart_PostCObject_DL(args_c.send_port, &obj);
  
  free(args);
  pthread_exit(NULL);
}

Note: This logic relies on the native dart api to work which can be found here. Before use, the interface needs to be attached to the current dart isolate which can be achieved by calling Dart_InitializeApiDL(dart_api_data) from C where dart_api_data is a void pointer which can be obtained from your dart code using the dart:ffi package through NativeApi.initializeApiData.

Share:
463
Miroslav Brandýský
Author by

Miroslav Brandýský

Updated on December 27, 2022

Comments

  • Miroslav Brandýský
    Miroslav Brandýský over 1 year

    I am using C library iperf3 to measure network. When I start network testing my aplication freezes and wait for results. I tried async and threads but any progress. Any advise? I'd like to run my test and asynchronously call another methods (at best, call this library again, but other methods). Is it possible?

    My network.dart

    final DynamicLibrary iperfLib = Platform.isAndroid
        ? DynamicLibrary.open("libiperf.so")
        : DynamicLibrary.process();
    
    typedef RunTestFunc = ffi.Pointer<ffi.Uint8> Function(
        ffi.Pointer<ffi.Uint8> context);
    typedef RunTest = ffi.Pointer<ffi.Uint8> Function(
        ffi.Pointer<ffi.Uint8> context);
    
    RunTest _run_test = iperfLib
        .lookup<ffi.NativeFunction<RunTestFunc>>('run_test')
        .asFunction<RunTest>();
    
    ffi.Pointer<ffi.Uint8> runTest(ffi.Pointer<ffi.Uint8> context) {
      return _run_test(context);
    }
    

    and iperf.c

    Iperf* run_test(Iperf* test) {
    
          __android_log_print( ANDROID_LOG_INFO, "DONE ", "server_hostname  %s", test->server_hostname );
         int cc = iperf_run_client( test ) ;
           __android_log_print( ANDROID_LOG_INFO, "DONE ", " %d",cc );
        iperf_free_test( test );
        return test
    }