Skip to content

Commit

Permalink
[vm/ffi] Pointer.asTypedList finalizer
Browse files Browse the repository at this point in the history
TEST=tests/ffi/external_typed_data_finalizer_test.dart

Closes: #50507
CoreLibraryReviewExempt: #52261
Change-Id: I1a82dcca15961b28c0de64637970fe38a39286e5
Cq-Include-Trybots: luci.dart.try:vm-asan-linux-release-x64-try,vm-aot-asan-linux-release-x64-try,vm-ffi-android-debug-arm-try,vm-ffi-qemu-linux-release-arm-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-aot-win-debug-x64c-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-aot-mac-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/301001
Commit-Queue: Daco Harkes <[email protected]>
Reviewed-by: Slava Egorov <[email protected]>
  • Loading branch information
dcharkes authored and Commit Queue committed May 10, 2023
1 parent e98d824 commit e5bb28b
Show file tree
Hide file tree
Showing 14 changed files with 433 additions and 53 deletions.
37 changes: 37 additions & 0 deletions runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <stdlib.h>
#include <sys/types.h>
#include <csignal>
#include <cstdlib>

#include "platform/globals.h"
#include "platform/memory_sanitizer.h"
Expand Down Expand Up @@ -1316,4 +1317,40 @@ DART_EXPORT bool IsNull(Dart_Handle object) {
return Dart_IsNull(object);
}

namespace {
struct RefCountedResource {
void* resource;
intptr_t refcount;
};
} // namespace

// We only ever have one ref counted resource in our test, use global lock.
std::mutex ref_counted_resource_mutex;

DART_EXPORT RefCountedResource* AllocateRefcountedResource() {
auto peer =
static_cast<RefCountedResource*>(malloc(sizeof(RefCountedResource)));
auto resource = malloc(128);
peer->resource = resource;
peer->refcount = 0; // We're not going to count the reference here.
return peer;
}

DART_EXPORT void IncreaseRefcount(RefCountedResource* peer) {
ref_counted_resource_mutex.lock();
peer->refcount++;
ref_counted_resource_mutex.unlock();
}

// And delete if zero.
DART_EXPORT void DecreaseRefcount(RefCountedResource* peer) {
ref_counted_resource_mutex.lock();
peer->refcount--;
if (peer->refcount <= 0) {
free(peer->resource);
free(peer);
}
ref_counted_resource_mutex.unlock();
}

} // namespace dart
23 changes: 23 additions & 0 deletions runtime/lib/ffi.cc
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,27 @@ DEFINE_FFI_NATIVE_ENTRY(FinalizerEntry_SetExternalSize,
}
};

namespace {
struct AsTypedListFinalizerData {
void (*callback)(void*);
void* token;
};
} // namespace

DEFINE_FFI_NATIVE_ENTRY(Pointer_asTypedListFinalizerAllocateData, void*, ()) {
return malloc(sizeof(AsTypedListFinalizerData));
};

void AsTypedListFinalizerCallback(void* peer) {
const auto* data = reinterpret_cast<AsTypedListFinalizerData*>(peer);
data->callback(data->token);
free(peer);
}

DEFINE_FFI_NATIVE_ENTRY(Pointer_asTypedListFinalizerCallbackPointer,
void*,
()) {
return reinterpret_cast<void*>(&AsTypedListFinalizerCallback);
};

} // namespace dart
72 changes: 55 additions & 17 deletions runtime/tools/ffi/sdk_lib_ffi_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,30 +39,51 @@ main(List<String> arguments) {
final args = argParser().parse(arguments);
Uri path = Uri.parse(args['path']);

generate(path, "ffi.g.dart", generatePublicExtension);
generate(path, "ffi_patch.g.dart", generatePatchExtension);
update(Uri.file('sdk/lib/ffi/ffi.dart'), generatePublicExtension);
update(Uri.file('sdk/lib/_internal/vm/lib/ffi_patch.dart'),
generatePatchExtension);
}

void generate(Uri path, String fileName,
Function(StringBuffer, Config, String) generator) {
void update(Uri fileName, Function(StringBuffer, Config, String) generator) {
final file = File.fromUri(fileName);
if (!file.existsSync()) {
print('$fileName does not exist, run from the root of the SDK.');
return;
}

final fileContents = file.readAsStringSync();
final split1 = fileContents.split(header);
if (split1.length != 2) {
print('$fileName has unexpected contents.');
print(split1.length);
return;
}
final split2 = split1[1].split(footer);
if (split2.length != 2) {
print('$fileName has unexpected contents 2.');
print(split2.length);
return;
}

final buffer = StringBuffer();
generateHeader(buffer);
buffer.write(split1[0]);
buffer.write(header);
configuration.forEach((Config c) => generator(buffer, c, "Pointer"));
configuration.forEach((Config c) => generator(buffer, c, "Array"));
generateFooter(buffer);
buffer.write(footer);
buffer.write(split2[1]);

final fullPath = path.resolve(fileName).path;
File(fullPath).writeAsStringSync(buffer.toString());
final fmtResult = Process.runSync(dartPath().path, ["format", fullPath]);
file.writeAsStringSync(buffer.toString());
final fmtResult =
Process.runSync(dartPath().path, ["format", fileName.toFilePath()]);
if (fmtResult.exitCode != 0) {
throw Exception(
"Formatting failed:\n${fmtResult.stdout}\n${fmtResult.stderr}\n");
}
print("Generated $fullPath.");
print("Updated $fileName.");
}

void generateHeader(StringBuffer buffer) {
const header = """
const header = """
//
// The following code is generated, do not edit by hand.
//
Expand All @@ -71,6 +92,7 @@ void generateHeader(StringBuffer buffer) {
""";

void generateHeader(StringBuffer buffer) {
buffer.write(header);
}

Expand Down Expand Up @@ -173,7 +195,15 @@ void generatePublicExtension(
///
/// The user has to ensure the memory range is accessible while using the
/// returned list.
$alignment external $typedListType asTypedList(int length);
///
/// If provided, [finalizer] will be run on the pointer once the typed list
/// is GCed. If provided, [token] will be passed to [finalizer], otherwise
/// the this pointer itself will be passed.
$alignment external $typedListType asTypedList(
int length, {
@Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
@Since('3.1') Pointer<Void>? token,
});
""";

if (container == "Pointer") {
Expand Down Expand Up @@ -228,12 +258,20 @@ void generatePatchExtension(
? ""
: """
@patch
$typedListType asTypedList(int length) {
$typedListType asTypedList(
int length, {
Pointer<NativeFinalizerFunction>? finalizer,
Pointer<Void>? token,
}) {
ArgumentError.checkNotNull(this, "Pointer<$nativeType>");
ArgumentError.checkNotNull(length, "length");
_checkExternalTypedDataLength(length, $elementSize);
_checkPointerAlignment(address, $elementSize);
return _asExternalTypedData$nativeType(this, length);
final result = _asExternalTypedData$nativeType(this, length);
if (finalizer != null) {
_attachAsTypedListFinalizer(finalizer, result, token ?? this, $sizeTimes length);
}
return result;
}
""";

Expand Down Expand Up @@ -279,13 +317,13 @@ extension ${nativeType}Array on Array<$nativeType> {
}
}

void generateFooter(StringBuffer buffer) {
final footer = """
final footer = """
//
// End of generated code.
//
""";

void generateFooter(StringBuffer buffer) {
buffer.write(footer);
}

Expand Down
1 change: 1 addition & 0 deletions runtime/vm/bootstrap_natives.cc
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ void Bootstrap::SetupNativeResolver() {
ASSERT(!library.IsNull());
library.set_native_entry_resolver(resolver);
library.set_native_entry_symbol_resolver(symbol_resolver);
library.set_ffi_native_resolver(ffi_native_resolver);

library = Library::InternalLibrary();
ASSERT(!library.IsNull());
Expand Down
6 changes: 4 additions & 2 deletions runtime/vm/bootstrap_natives.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,15 +466,17 @@ namespace dart {
V(VariableMirror_type, 2)

#define BOOTSTRAP_FFI_NATIVE_LIST(V) \
V(FinalizerEntry_SetExternalSize, void, (Dart_Handle, intptr_t))
V(FinalizerEntry_SetExternalSize, void, (Dart_Handle, intptr_t)) \
V(Pointer_asTypedListFinalizerAllocateData, void*, ()) \
V(Pointer_asTypedListFinalizerCallbackPointer, void*, ())

class BootstrapNatives : public AllStatic {
public:
static Dart_NativeFunction Lookup(Dart_Handle name,
int argument_count,
bool* auto_setup_scope);

// For use with @FfiNative.
// For use with @Native.
static void* LookupFfiNative(const char* name, uintptr_t argument_count);

static const uint8_t* Symbol(Dart_NativeFunction nf);
Expand Down
3 changes: 2 additions & 1 deletion runtime/vm/kernel_loader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1207,7 +1207,8 @@ void KernelLoader::LoadLibraryImportsAndExports(Library* library,
if (!Api::IsFfiEnabled() &&
target_library.url() == Symbols::DartFfi().ptr() &&
library->url() != Symbols::DartCore().ptr() &&
library->url() != Symbols::DartInternal().ptr()) {
library->url() != Symbols::DartInternal().ptr() &&
library->url() != Symbols::DartFfi().ptr()) {
H.ReportError(
"import of dart:ffi is not supported in the current Dart runtime");
}
Expand Down
43 changes: 42 additions & 1 deletion sdk/lib/_internal/vm/lib/ffi_native_finalizer_patch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// All imports must be in all FFI patch files to not depend on the order
// the patches are applied.
import 'dart:_internal';
import 'dart:ffi';
import 'dart:isolate';
import 'dart:typed_data';

Expand Down Expand Up @@ -38,7 +39,7 @@ final class _NativeFinalizer extends FinalizerBase implements NativeFinalizer {
}

void attach(
Finalizable value,
Object value,
Pointer<Void> token, {
Object? detach,
int? externalSize,
Expand Down Expand Up @@ -102,3 +103,43 @@ final class _NativeFinalizer extends FinalizerBase implements NativeFinalizer {
finalizer._removeEntries();
}
}

@Native<Pointer<NativeFinalizerFunction> Function()>(
symbol: 'Pointer_asTypedListFinalizerCallbackPointer')
external Pointer<NativeFinalizerFunction>
_asTypedListFinalizerCallbackPointer();

final Pointer<NativeFinalizerFunction> _asTypedListFinalizerCallback =
_asTypedListFinalizerCallbackPointer();

final _asTypedListFinalizer = _NativeFinalizer(_asTypedListFinalizerCallback);

final class _AsTypedListFinalizerData extends Struct {
external Pointer<NativeFinalizerFunction> callback;
external Pointer<Void> token;
}

@patch
void _attachAsTypedListFinalizer(
Pointer<NativeFinalizerFunction> callback,
Object typedList,
Pointer pointer,
int? externalSize,
) {
final data = _allocateData();
data.ref.callback = callback;
data.ref.token = pointer.cast();
_asTypedListFinalizer.attach(
typedList,
data.cast(),
externalSize: externalSize,
);
}

// Ensure we use the `malloc` that corresponds to the `free` used inside
// `_asTypedListFinalizerCallback` in the VM.
@Native<Pointer<_AsTypedListFinalizerData> Function()>(
symbol: 'Pointer_asTypedListFinalizerAllocateData',
isLeaf: true,
)
external Pointer<_AsTypedListFinalizerData> _allocateData();
Loading

0 comments on commit e5bb28b

Please sign in to comment.