dart-sdk/tests/ffi/isolate_local_function_callbacks_test.dart
Ryan Macnak 1a9acb9171 [vm, ffi] Make callbacks profiler-safe.
Delay changing Thread::vm_tag on callback entry and restore the tag early on callback return so that the profiler doesn't see the "running Dart" tag unless it can also see the fake return address marking the entry frame.

TEST=ffi/async_void_function_callbacks, ffi/function_callbacks_subtype, ffi/function_callbacks, ffi/isolate_local_function_callbacks
Bug: https://github.com/dart-lang/sdk/issues/52814
Change-Id: I40d80ec7c44063d078db0e211565e2d127c6b81e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/367460
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
2024-05-28 21:16:41 +00:00

207 lines
5.9 KiB
Dart

// Copyright (c) 2023, 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.
// Dart test program for testing dart:ffi async callbacks.
//
// VMOptions=
// VMOptions=--stacktrace-every=100
// VMOptions=--use-slow-path
// VMOptions=--use-slow-path --stacktrace-every=100
// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
// VMOptions=--test_il_serialization
// VMOptions=--profiler --profile_vm=true
// VMOptions=--profiler --profile_vm=false
// SharedObjects=ffi_test_functions
import 'dart:async';
import 'dart:ffi';
import 'dart:isolate';
import "package:expect/expect.dart";
import 'dylib_utils.dart';
main(args, message) async {
testNativeCallableStatic();
testNativeCallableClosure();
testNativeCallableDoubleCloseError();
testNativeCallableNestedCloseCallStatic();
testNativeCallableNestedCloseCallClosure();
testNativeCallableExceptionalReturnStatic();
testNativeCallableExceptionalReturnClosure();
await testNativeCallableDontKeepAliveStatic();
await testNativeCallableDontKeepAliveClosure();
testNativeCallableKeepAliveGetter();
print("All tests completed :)");
}
final ffiTestFunctions = dlopenPlatformSpecific("ffi_test_functions");
typedef TwoIntFnNativeType = Int32 Function(Pointer, Int32, Int32);
typedef TwoIntFnType = int Function(Pointer, int, int);
final callTwoIntFunction = ffiTestFunctions
.lookupFunction<TwoIntFnNativeType, TwoIntFnType>("CallTwoIntFunction");
typedef CallbackNativeType = Int32 Function(Int32, Int32);
int add(int a, int b) {
return a + b;
}
testNativeCallableStatic() {
final callback = NativeCallable<CallbackNativeType>.isolateLocal(add,
exceptionalReturn: 0);
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
callback.close();
}
testNativeCallableClosure() {
int c = 70000;
final callback = NativeCallable<CallbackNativeType>.isolateLocal(
(int a, int b) => a + b + c,
exceptionalReturn: 0);
Expect.equals(71234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
c = 80000;
Expect.equals(81234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
callback.close();
}
testNativeCallableDoubleCloseError() {
final callback = NativeCallable<CallbackNativeType>.isolateLocal(add,
exceptionalReturn: 0);
Expect.notEquals(nullptr, callback.nativeFunction);
callback.close();
Expect.throwsStateError(() {
final _ = callback.nativeFunction;
});
// Expect that these do not throw.
callback.close();
callback.keepIsolateAlive = true;
Expect.isFalse(callback.keepIsolateAlive);
}
late NativeCallable selfClosingStaticCallback;
int selfClosingStatic(int a, int b) {
selfClosingStaticCallback.close();
return a + b;
}
testNativeCallableNestedCloseCallStatic() {
selfClosingStaticCallback = NativeCallable<CallbackNativeType>.isolateLocal(
selfClosingStatic,
exceptionalReturn: 0);
Expect.equals(1234,
callTwoIntFunction(selfClosingStaticCallback.nativeFunction, 1000, 234));
// The callback is already closed.
Expect.throwsStateError(() {
final _ = selfClosingStaticCallback.nativeFunction;
});
}
testNativeCallableNestedCloseCallClosure() {
late NativeCallable callback;
callback = NativeCallable<CallbackNativeType>.isolateLocal((int a, int b) {
callback.close();
return a + b;
}, exceptionalReturn: 0);
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
// The callback is already closed.
Expect.throwsStateError(() {
final _ = callback.nativeFunction;
});
}
int throwerCallback(int a, int b) {
if (a != 1000) {
throw "Oh no!";
}
return a + b;
}
testNativeCallableExceptionalReturnStatic() {
final callback = NativeCallable<CallbackNativeType>.isolateLocal(
throwerCallback,
exceptionalReturn: 5678);
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
Expect.equals(5678, callTwoIntFunction(callback.nativeFunction, 0, 0));
callback.close();
}
testNativeCallableExceptionalReturnClosure() {
final callback =
NativeCallable<CallbackNativeType>.isolateLocal((int a, int b) {
if (a != 1000) {
throw "Oh no!";
}
return a + b;
}, exceptionalReturn: 5678);
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
Expect.equals(5678, callTwoIntFunction(callback.nativeFunction, 0, 0));
callback.close();
}
Future<void> testNativeCallableDontKeepAliveStatic() async {
final exitPort = ReceivePort();
await Isolate.spawn((_) async {
final callback = NativeCallable<CallbackNativeType>.isolateLocal(add,
exceptionalReturn: 0);
Expect.equals(1234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
callback.keepIsolateAlive = false;
}, null, onExit: exitPort.sendPort);
await exitPort.first;
exitPort.close();
}
Future<void> testNativeCallableDontKeepAliveClosure() async {
int c = 70000;
final exitPort = ReceivePort();
await Isolate.spawn((_) async {
final callback = NativeCallable<CallbackNativeType>.isolateLocal(
(int a, int b) => a + b + c,
exceptionalReturn: 0);
Expect.equals(
71234, callTwoIntFunction(callback.nativeFunction, 1000, 234));
callback.keepIsolateAlive = false;
}, null, onExit: exitPort.sendPort);
await exitPort.first;
exitPort.close();
}
testNativeCallableKeepAliveGetter() {
final callback = NativeCallable<CallbackNativeType>.isolateLocal(add,
exceptionalReturn: 0);
// Check that only the flag changes are counted by decrementing and
// incrementing a lot, and by different amounts.
for (int i = 0; i < 100; ++i) {
callback.keepIsolateAlive = false;
Expect.isFalse(callback.keepIsolateAlive);
}
for (int i = 0; i < 200; ++i) {
callback.keepIsolateAlive = true;
Expect.isTrue(callback.keepIsolateAlive);
}
callback.close();
}