Skip to content

Commit e5bb28b

Browse files
dcharkesCommit Queue
authored and
Commit Queue
committed
[vm/ffi] Pointer.asTypedList finalizer
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]>
1 parent e98d824 commit e5bb28b

14 files changed

+433
-53
lines changed

runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc

+37
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <stdlib.h>
99
#include <sys/types.h>
1010
#include <csignal>
11+
#include <cstdlib>
1112

1213
#include "platform/globals.h"
1314
#include "platform/memory_sanitizer.h"
@@ -1316,4 +1317,40 @@ DART_EXPORT bool IsNull(Dart_Handle object) {
13161317
return Dart_IsNull(object);
13171318
}
13181319

1320+
namespace {
1321+
struct RefCountedResource {
1322+
void* resource;
1323+
intptr_t refcount;
1324+
};
1325+
} // namespace
1326+
1327+
// We only ever have one ref counted resource in our test, use global lock.
1328+
std::mutex ref_counted_resource_mutex;
1329+
1330+
DART_EXPORT RefCountedResource* AllocateRefcountedResource() {
1331+
auto peer =
1332+
static_cast<RefCountedResource*>(malloc(sizeof(RefCountedResource)));
1333+
auto resource = malloc(128);
1334+
peer->resource = resource;
1335+
peer->refcount = 0; // We're not going to count the reference here.
1336+
return peer;
1337+
}
1338+
1339+
DART_EXPORT void IncreaseRefcount(RefCountedResource* peer) {
1340+
ref_counted_resource_mutex.lock();
1341+
peer->refcount++;
1342+
ref_counted_resource_mutex.unlock();
1343+
}
1344+
1345+
// And delete if zero.
1346+
DART_EXPORT void DecreaseRefcount(RefCountedResource* peer) {
1347+
ref_counted_resource_mutex.lock();
1348+
peer->refcount--;
1349+
if (peer->refcount <= 0) {
1350+
free(peer->resource);
1351+
free(peer);
1352+
}
1353+
ref_counted_resource_mutex.unlock();
1354+
}
1355+
13191356
} // namespace dart

runtime/lib/ffi.cc

+23
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,27 @@ DEFINE_FFI_NATIVE_ENTRY(FinalizerEntry_SetExternalSize,
189189
}
190190
};
191191

192+
namespace {
193+
struct AsTypedListFinalizerData {
194+
void (*callback)(void*);
195+
void* token;
196+
};
197+
} // namespace
198+
199+
DEFINE_FFI_NATIVE_ENTRY(Pointer_asTypedListFinalizerAllocateData, void*, ()) {
200+
return malloc(sizeof(AsTypedListFinalizerData));
201+
};
202+
203+
void AsTypedListFinalizerCallback(void* peer) {
204+
const auto* data = reinterpret_cast<AsTypedListFinalizerData*>(peer);
205+
data->callback(data->token);
206+
free(peer);
207+
}
208+
209+
DEFINE_FFI_NATIVE_ENTRY(Pointer_asTypedListFinalizerCallbackPointer,
210+
void*,
211+
()) {
212+
return reinterpret_cast<void*>(&AsTypedListFinalizerCallback);
213+
};
214+
192215
} // namespace dart

runtime/tools/ffi/sdk_lib_ffi_generator.dart

+55-17
Original file line numberDiff line numberDiff line change
@@ -39,30 +39,51 @@ main(List<String> arguments) {
3939
final args = argParser().parse(arguments);
4040
Uri path = Uri.parse(args['path']);
4141

42-
generate(path, "ffi.g.dart", generatePublicExtension);
43-
generate(path, "ffi_patch.g.dart", generatePatchExtension);
42+
update(Uri.file('sdk/lib/ffi/ffi.dart'), generatePublicExtension);
43+
update(Uri.file('sdk/lib/_internal/vm/lib/ffi_patch.dart'),
44+
generatePatchExtension);
4445
}
4546

46-
void generate(Uri path, String fileName,
47-
Function(StringBuffer, Config, String) generator) {
47+
void update(Uri fileName, Function(StringBuffer, Config, String) generator) {
48+
final file = File.fromUri(fileName);
49+
if (!file.existsSync()) {
50+
print('$fileName does not exist, run from the root of the SDK.');
51+
return;
52+
}
53+
54+
final fileContents = file.readAsStringSync();
55+
final split1 = fileContents.split(header);
56+
if (split1.length != 2) {
57+
print('$fileName has unexpected contents.');
58+
print(split1.length);
59+
return;
60+
}
61+
final split2 = split1[1].split(footer);
62+
if (split2.length != 2) {
63+
print('$fileName has unexpected contents 2.');
64+
print(split2.length);
65+
return;
66+
}
67+
4868
final buffer = StringBuffer();
49-
generateHeader(buffer);
69+
buffer.write(split1[0]);
70+
buffer.write(header);
5071
configuration.forEach((Config c) => generator(buffer, c, "Pointer"));
5172
configuration.forEach((Config c) => generator(buffer, c, "Array"));
52-
generateFooter(buffer);
73+
buffer.write(footer);
74+
buffer.write(split2[1]);
5375

54-
final fullPath = path.resolve(fileName).path;
55-
File(fullPath).writeAsStringSync(buffer.toString());
56-
final fmtResult = Process.runSync(dartPath().path, ["format", fullPath]);
76+
file.writeAsStringSync(buffer.toString());
77+
final fmtResult =
78+
Process.runSync(dartPath().path, ["format", fileName.toFilePath()]);
5779
if (fmtResult.exitCode != 0) {
5880
throw Exception(
5981
"Formatting failed:\n${fmtResult.stdout}\n${fmtResult.stderr}\n");
6082
}
61-
print("Generated $fullPath.");
83+
print("Updated $fileName.");
6284
}
6385

64-
void generateHeader(StringBuffer buffer) {
65-
const header = """
86+
const header = """
6687
//
6788
// The following code is generated, do not edit by hand.
6889
//
@@ -71,6 +92,7 @@ void generateHeader(StringBuffer buffer) {
7192
7293
""";
7394

95+
void generateHeader(StringBuffer buffer) {
7496
buffer.write(header);
7597
}
7698

@@ -173,7 +195,15 @@ void generatePublicExtension(
173195
///
174196
/// The user has to ensure the memory range is accessible while using the
175197
/// returned list.
176-
$alignment external $typedListType asTypedList(int length);
198+
///
199+
/// If provided, [finalizer] will be run on the pointer once the typed list
200+
/// is GCed. If provided, [token] will be passed to [finalizer], otherwise
201+
/// the this pointer itself will be passed.
202+
$alignment external $typedListType asTypedList(
203+
int length, {
204+
@Since('3.1') Pointer<NativeFinalizerFunction>? finalizer,
205+
@Since('3.1') Pointer<Void>? token,
206+
});
177207
""";
178208

179209
if (container == "Pointer") {
@@ -228,12 +258,20 @@ void generatePatchExtension(
228258
? ""
229259
: """
230260
@patch
231-
$typedListType asTypedList(int length) {
261+
$typedListType asTypedList(
262+
int length, {
263+
Pointer<NativeFinalizerFunction>? finalizer,
264+
Pointer<Void>? token,
265+
}) {
232266
ArgumentError.checkNotNull(this, "Pointer<$nativeType>");
233267
ArgumentError.checkNotNull(length, "length");
234268
_checkExternalTypedDataLength(length, $elementSize);
235269
_checkPointerAlignment(address, $elementSize);
236-
return _asExternalTypedData$nativeType(this, length);
270+
final result = _asExternalTypedData$nativeType(this, length);
271+
if (finalizer != null) {
272+
_attachAsTypedListFinalizer(finalizer, result, token ?? this, $sizeTimes length);
273+
}
274+
return result;
237275
}
238276
""";
239277

@@ -279,13 +317,13 @@ extension ${nativeType}Array on Array<$nativeType> {
279317
}
280318
}
281319

282-
void generateFooter(StringBuffer buffer) {
283-
final footer = """
320+
final footer = """
284321
//
285322
// End of generated code.
286323
//
287324
""";
288325

326+
void generateFooter(StringBuffer buffer) {
289327
buffer.write(footer);
290328
}
291329

runtime/vm/bootstrap_natives.cc

+1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ void Bootstrap::SetupNativeResolver() {
127127
ASSERT(!library.IsNull());
128128
library.set_native_entry_resolver(resolver);
129129
library.set_native_entry_symbol_resolver(symbol_resolver);
130+
library.set_ffi_native_resolver(ffi_native_resolver);
130131

131132
library = Library::InternalLibrary();
132133
ASSERT(!library.IsNull());

runtime/vm/bootstrap_natives.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -466,15 +466,17 @@ namespace dart {
466466
V(VariableMirror_type, 2)
467467

468468
#define BOOTSTRAP_FFI_NATIVE_LIST(V) \
469-
V(FinalizerEntry_SetExternalSize, void, (Dart_Handle, intptr_t))
469+
V(FinalizerEntry_SetExternalSize, void, (Dart_Handle, intptr_t)) \
470+
V(Pointer_asTypedListFinalizerAllocateData, void*, ()) \
471+
V(Pointer_asTypedListFinalizerCallbackPointer, void*, ())
470472

471473
class BootstrapNatives : public AllStatic {
472474
public:
473475
static Dart_NativeFunction Lookup(Dart_Handle name,
474476
int argument_count,
475477
bool* auto_setup_scope);
476478

477-
// For use with @FfiNative.
479+
// For use with @Native.
478480
static void* LookupFfiNative(const char* name, uintptr_t argument_count);
479481

480482
static const uint8_t* Symbol(Dart_NativeFunction nf);

runtime/vm/kernel_loader.cc

+2-1
Original file line numberDiff line numberDiff line change
@@ -1207,7 +1207,8 @@ void KernelLoader::LoadLibraryImportsAndExports(Library* library,
12071207
if (!Api::IsFfiEnabled() &&
12081208
target_library.url() == Symbols::DartFfi().ptr() &&
12091209
library->url() != Symbols::DartCore().ptr() &&
1210-
library->url() != Symbols::DartInternal().ptr()) {
1210+
library->url() != Symbols::DartInternal().ptr() &&
1211+
library->url() != Symbols::DartFfi().ptr()) {
12111212
H.ReportError(
12121213
"import of dart:ffi is not supported in the current Dart runtime");
12131214
}

sdk/lib/_internal/vm/lib/ffi_native_finalizer_patch.dart

+42-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// All imports must be in all FFI patch files to not depend on the order
66
// the patches are applied.
77
import 'dart:_internal';
8+
import 'dart:ffi';
89
import 'dart:isolate';
910
import 'dart:typed_data';
1011

@@ -38,7 +39,7 @@ final class _NativeFinalizer extends FinalizerBase implements NativeFinalizer {
3839
}
3940

4041
void attach(
41-
Finalizable value,
42+
Object value,
4243
Pointer<Void> token, {
4344
Object? detach,
4445
int? externalSize,
@@ -102,3 +103,43 @@ final class _NativeFinalizer extends FinalizerBase implements NativeFinalizer {
102103
finalizer._removeEntries();
103104
}
104105
}
106+
107+
@Native<Pointer<NativeFinalizerFunction> Function()>(
108+
symbol: 'Pointer_asTypedListFinalizerCallbackPointer')
109+
external Pointer<NativeFinalizerFunction>
110+
_asTypedListFinalizerCallbackPointer();
111+
112+
final Pointer<NativeFinalizerFunction> _asTypedListFinalizerCallback =
113+
_asTypedListFinalizerCallbackPointer();
114+
115+
final _asTypedListFinalizer = _NativeFinalizer(_asTypedListFinalizerCallback);
116+
117+
final class _AsTypedListFinalizerData extends Struct {
118+
external Pointer<NativeFinalizerFunction> callback;
119+
external Pointer<Void> token;
120+
}
121+
122+
@patch
123+
void _attachAsTypedListFinalizer(
124+
Pointer<NativeFinalizerFunction> callback,
125+
Object typedList,
126+
Pointer pointer,
127+
int? externalSize,
128+
) {
129+
final data = _allocateData();
130+
data.ref.callback = callback;
131+
data.ref.token = pointer.cast();
132+
_asTypedListFinalizer.attach(
133+
typedList,
134+
data.cast(),
135+
externalSize: externalSize,
136+
);
137+
}
138+
139+
// Ensure we use the `malloc` that corresponds to the `free` used inside
140+
// `_asTypedListFinalizerCallback` in the VM.
141+
@Native<Pointer<_AsTypedListFinalizerData> Function()>(
142+
symbol: 'Pointer_asTypedListFinalizerAllocateData',
143+
isLeaf: true,
144+
)
145+
external Pointer<_AsTypedListFinalizerData> _allocateData();

0 commit comments

Comments
 (0)