|  | // Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  | // | 
|  | // Sample showing how to do calls from C into Dart through native ports. | 
|  | // | 
|  | // This sample does not use FFI callbacks to do the callbacks at all. Instead, | 
|  | // it sends a message to Dart through native ports, decodes the message in Dart | 
|  | // does a method call in Dart and sends the result back to C through a native | 
|  | // port. | 
|  | // | 
|  | // The disadvantage of this approach compared to `sample_async_callback.dart` | 
|  | // is that it requires more boilerplate, because it does not use the automatic | 
|  | // marshalling of data of the FFI. | 
|  | // | 
|  | // The advantage is that finalizers can be used when passing ownership of data | 
|  | // (buffers) from C to Dart. | 
|  |  | 
|  | import 'dart:ffi'; | 
|  | import 'dart:isolate'; | 
|  | import 'dart:typed_data'; | 
|  |  | 
|  | import 'package:expect/expect.dart'; | 
|  |  | 
|  | import '../dylib_utils.dart'; | 
|  |  | 
|  | var globalResult = 0; | 
|  | var numCallbacks1 = 0; | 
|  | var numCallbacks2 = 0; | 
|  |  | 
|  | main() async { | 
|  | print("Dart = Dart mutator thread executing Dart."); | 
|  | print("C Da = Dart mutator thread executing C."); | 
|  | print("C T1 = Some C thread executing C."); | 
|  | print("C T2 = Some C thread executing C."); | 
|  | print("C    = C T1 or C T2."); | 
|  | print("Dart: Setup."); | 
|  | Expect.isTrue(NativeApi.majorVersion == 1); | 
|  | Expect.isTrue(NativeApi.minorVersion >= 1); | 
|  | final initializeApi = dl.lookupFunction<IntPtr Function(Pointer<Void>), | 
|  | int Function(Pointer<Void>)>("InitDartApiDL"); | 
|  | Expect.isTrue(initializeApi(NativeApi.initializeApiDLData) == 0); | 
|  |  | 
|  | final interactiveCppRequests = ReceivePort()..listen(handleCppRequests); | 
|  | final int nativePort = interactiveCppRequests.sendPort.nativePort; | 
|  | registerSendPort(nativePort); | 
|  | print("Dart: Tell C to start worker threads."); | 
|  | startWorkSimulator2(); | 
|  |  | 
|  | // We need to yield control in order to be able to receive messages. | 
|  | while (numCallbacks2 < 3) { | 
|  | print("Dart: Yielding (able to receive messages on port)."); | 
|  | await asyncSleep(500); | 
|  | } | 
|  | print("Dart: Received expected number of callbacks."); | 
|  |  | 
|  | Expect.equals(2, numCallbacks1); | 
|  | Expect.equals(3, numCallbacks2); | 
|  | Expect.equals(14, globalResult); | 
|  |  | 
|  | print("Dart: Tell C to stop worker threads."); | 
|  | stopWorkSimulator2(); | 
|  | interactiveCppRequests.close(); | 
|  | print("Dart: Done."); | 
|  | } | 
|  |  | 
|  | int myCallback1(int a) { | 
|  | print("Dart:     myCallback1($a)."); | 
|  | numCallbacks1++; | 
|  | return a + 3; | 
|  | } | 
|  |  | 
|  | void myCallback2(int a) { | 
|  | print("Dart:     myCallback2($a)."); | 
|  | globalResult += a; | 
|  | numCallbacks2++; | 
|  | } | 
|  |  | 
|  | class CppRequest { | 
|  | final SendPort replyPort; | 
|  | final int pendingCall; | 
|  | final String method; | 
|  | final Uint8List data; | 
|  |  | 
|  | factory CppRequest.fromCppMessage(List message) { | 
|  | return CppRequest._(message[0], message[1], message[2], message[3]); | 
|  | } | 
|  |  | 
|  | CppRequest._(this.replyPort, this.pendingCall, this.method, this.data); | 
|  |  | 
|  | String toString() => 'CppRequest(method: $method, ${data.length} bytes)'; | 
|  | } | 
|  |  | 
|  | class CppResponse { | 
|  | final int pendingCall; | 
|  | final Uint8List data; | 
|  |  | 
|  | CppResponse(this.pendingCall, this.data); | 
|  |  | 
|  | List toCppMessage() => List.from([pendingCall, data], growable: false); | 
|  |  | 
|  | String toString() => 'CppResponse(message: ${data.length})'; | 
|  | } | 
|  |  | 
|  | void handleCppRequests(dynamic message) { | 
|  | final cppRequest = CppRequest.fromCppMessage(message); | 
|  | print('Dart:   Got message: $cppRequest'); | 
|  |  | 
|  | if (cppRequest.method == 'myCallback1') { | 
|  | // Use the data in any way you like. Here we just take the first byte as | 
|  | // the argument to the function. | 
|  | final int argument = cppRequest.data[0]; | 
|  | final int result = myCallback1(argument); | 
|  | final cppResponse = | 
|  | CppResponse(cppRequest.pendingCall, Uint8List.fromList([result])); | 
|  | print('Dart:   Responding: $cppResponse'); | 
|  | cppRequest.replyPort.send(cppResponse.toCppMessage()); | 
|  | } else if (cppRequest.method == 'myCallback2') { | 
|  | final int argument = cppRequest.data[0]; | 
|  | myCallback2(argument); | 
|  | } | 
|  | } | 
|  |  | 
|  | final dl = dlopenPlatformSpecific("ffi_test_functions"); | 
|  |  | 
|  | final registerSendPort = dl.lookupFunction<Void Function(Int64 sendPort), | 
|  | void Function(int sendPort)>('RegisterSendPort'); | 
|  |  | 
|  | final startWorkSimulator2 = | 
|  | dl.lookupFunction<Void Function(), void Function()>('StartWorkSimulator2'); | 
|  |  | 
|  | final stopWorkSimulator2 = | 
|  | dl.lookupFunction<Void Function(), void Function()>('StopWorkSimulator2'); | 
|  |  | 
|  | Future asyncSleep(int ms) { | 
|  | return new Future.delayed(Duration(milliseconds: ms), () => true); | 
|  | } |