Replace HeapFree with CoTaskMemAlloc (#144)
diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml
index e47bf66..a28e334 100644
--- a/.github/workflows/test-package.yml
+++ b/.github/workflows/test-package.yml
@@ -23,7 +23,7 @@
sdk: [dev]
steps:
- uses: actions/checkout@v2
- - uses: dart-lang/setup-dart@v1.0
+ - uses: dart-lang/setup-dart@v1
with:
sdk: ${{ matrix.sdk }}
- id: install
@@ -47,10 +47,10 @@
matrix:
# Add macos-latest and/or windows-latest if relevant for this package.
os: [ubuntu-latest]
- sdk: [2.12.0, dev]
+ sdk: [2.17.0, dev]
steps:
- uses: actions/checkout@v2
- - uses: dart-lang/setup-dart@v1.0
+ - uses: dart-lang/setup-dart@v1
with:
sdk: ${{ matrix.sdk }}
- id: install
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad5be6d..fd2be4d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## 2.0.0
+
+Switch Windows memory allocation to use `CoTaskMemAlloc` and `CoTaskMemFree`,
+which will enable support for `NativeFinalizer`. This release requires Dart
+2.17.0 or greater.
+
## 1.2.1
Revert added common C integer types as ABI-specific integers.
@@ -7,7 +13,7 @@
## 1.2.0 (retracted)
-This release requires Dart `2.16.0` or greater.
+This release requires Dart `2.16.0` or greater.
## 1.2.0-dev.0
@@ -15,7 +21,7 @@
types will make their way into `dart:ffi` in 2.17 and be deprecated from this
package. Having them in this package enables using them in Dart 2.16.
-This pre-release requires Dart `2.16.0-118.0.dev` or greater.
+This pre-release requires Dart `2.16.0-118.0.dev` or greater.
## 1.1.2
diff --git a/lib/src/allocation.dart b/lib/src/allocation.dart
index 49babe8..91c8411 100644
--- a/lib/src/allocation.dart
+++ b/lib/src/allocation.dart
@@ -5,9 +5,9 @@
import 'dart:ffi';
import 'dart:io';
-// Note that kernel32.dll is the correct name in both 32-bit and 64-bit.
+// Note that ole32.dll is the correct name in both 32-bit and 64-bit.
final DynamicLibrary stdlib = Platform.isWindows
- ? DynamicLibrary.open('kernel32.dll')
+ ? DynamicLibrary.open('ole32.dll')
: DynamicLibrary.process();
typedef PosixMallocNative = Pointer Function(IntPtr);
@@ -25,24 +25,16 @@
final PosixFree posixFree =
stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
-typedef WinGetProcessHeapFn = Pointer Function();
-final WinGetProcessHeapFn winGetProcessHeap = stdlib
- .lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
-final Pointer processHeap = winGetProcessHeap();
+typedef WinCoTaskMemAllocNative = Pointer Function(Size cb);
+typedef WinCoTaskMemAlloc = Pointer Function(int cb);
+final WinCoTaskMemAlloc winCoTaskMemAlloc =
+ stdlib.lookupFunction<WinCoTaskMemAllocNative, WinCoTaskMemAlloc>(
+ 'CoTaskMemAlloc');
-typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
-typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
-final WinHeapAlloc winHeapAlloc =
- stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('HeapAlloc');
-
-typedef WinHeapFreeNative = Int32 Function(
- Pointer heap, Uint32 flags, Pointer memory);
-typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory);
-final WinHeapFree winHeapFree =
- stdlib.lookupFunction<WinHeapFreeNative, WinHeapFree>('HeapFree');
-
-// ignore: constant_identifier_names
-const int HEAP_ZERO_MEMORY = 8;
+typedef WinCoTaskMemFreeNative = Void Function(Pointer pv);
+typedef WinCoTaskMemFree = void Function(Pointer pv);
+final WinCoTaskMemFree winCoTaskMemFree = stdlib
+ .lookupFunction<WinCoTaskMemFreeNative, WinCoTaskMemFree>('CoTaskMemFree');
/// Manages memory on the native heap.
///
@@ -50,14 +42,14 @@
/// for zero-initialized memory on allocation.
///
/// For POSIX-based systems, this uses `malloc` and `free`. On Windows, it uses
-/// `HeapAlloc` and `HeapFree` against the default public heap.
+/// `CoTaskMemAlloc`.
class _MallocAllocator implements Allocator {
const _MallocAllocator();
/// Allocates [byteCount] bytes of of unitialized memory on the native heap.
///
/// For POSIX-based systems, this uses `malloc`. On Windows, it uses
- /// `HeapAlloc` against the default public heap.
+ /// `CoTaskMemAlloc`.
///
/// Throws an [ArgumentError] if the number of bytes or alignment cannot be
/// satisfied.
@@ -66,7 +58,7 @@
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
Pointer<T> result;
if (Platform.isWindows) {
- result = winHeapAlloc(processHeap, /*flags=*/ 0, byteCount).cast();
+ result = winCoTaskMemAlloc(byteCount).cast();
} else {
result = posixMalloc(byteCount).cast();
}
@@ -78,19 +70,13 @@
/// Releases memory allocated on the native heap.
///
- /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree`
- /// against the default public heap. It may only be used against pointers
- /// allocated in a manner equivalent to [allocate].
- ///
- /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be
- /// freed.
- ///
+ /// For POSIX-based systems, this uses `free`. On Windows, it uses
+ /// `CoTaskMemFree`. It may only be used against pointers allocated in a
+ /// manner equivalent to [allocate].
@override
void free(Pointer pointer) {
if (Platform.isWindows) {
- if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) {
- throw ArgumentError('Could not free $pointer.');
- }
+ winCoTaskMemFree(pointer);
} else {
posixFree(pointer);
}
@@ -103,7 +89,7 @@
/// zero-initialized memory allocation.
///
/// For POSIX-based systems, this uses `malloc` and `free`. On Windows, it uses
-/// `HeapAlloc` and `HeapFree` against the default public heap.
+/// `CoTaskMemAlloc` and `CoTaskMemFree`.
const Allocator malloc = _MallocAllocator();
/// Manages memory on the native heap.
@@ -111,16 +97,28 @@
/// Initializes newly allocated memory to zero.
///
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
-/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default
-/// public heap.
+/// `CoTaskMemAlloc` and `CoTaskMemFree`.
class _CallocAllocator implements Allocator {
const _CallocAllocator();
+ /// Fills a block of memory with a specified value.
+ void _fillMemory(Pointer destination, int length, int fill) {
+ final ptr = destination.cast<Uint8>();
+ for (var i = 0; i < length; i++) {
+ ptr[i] = fill;
+ }
+ }
+
+ /// Fills a block of memory with zeros.
+ ///
+ void _zeroMemory(Pointer destination, int length) =>
+ _fillMemory(destination, length, 0);
+
/// Allocates [byteCount] bytes of zero-initialized of memory on the native
/// heap.
///
/// For POSIX-based systems, this uses `malloc`. On Windows, it uses
- /// `HeapAlloc` against the default public heap.
+ /// `CoTaskMemAlloc`.
///
/// Throws an [ArgumentError] if the number of bytes or alignment cannot be
/// satisfied.
@@ -129,8 +127,8 @@
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
Pointer<T> result;
if (Platform.isWindows) {
- result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount)
- .cast();
+ result = winCoTaskMemAlloc(byteCount).cast();
+ _zeroMemory(result, byteCount);
} else {
result = posixCalloc(byteCount, 1).cast();
}
@@ -142,19 +140,13 @@
/// Releases memory allocated on the native heap.
///
- /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree`
- /// against the default public heap. It may only be used against pointers
- /// allocated in a manner equivalent to [allocate].
- ///
- /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be
- /// freed.
- ///
+ /// For POSIX-based systems, this uses `free`. On Windows, it uses
+ /// `CoTaskMemFree`. It may only be used against pointers allocated in a
+ /// manner equivalent to [allocate].
@override
void free(Pointer pointer) {
if (Platform.isWindows) {
- if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) {
- throw ArgumentError('Could not free $pointer.');
- }
+ winCoTaskMemFree(pointer);
} else {
posixFree(pointer);
}
@@ -167,6 +159,5 @@
/// memory allocation.
///
/// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
-/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default
-/// public heap.
+/// `CoTaskMemAlloc` and `CoTaskMemFree`.
const Allocator calloc = _CallocAllocator();
diff --git a/pubspec.yaml b/pubspec.yaml
index f2f91d5..5fb789f 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,11 +1,11 @@
name: ffi
-version: 1.2.1
+version: 2.0.0
description: Utilities for working with Foreign Function Interface (FFI) code.
repository: https://github.com/dart-lang/ffi
environment:
- sdk: '>=2.12.0 <3.0.0'
+ sdk: '>=2.17.0 <3.0.0'
dev_dependencies:
- test: ^1.16.0
- lints: ^1.0.0
+ test: ^1.21.1
+ lints: ^2.0.0
diff --git a/test/allocation_test.dart b/test/allocation_test.dart
new file mode 100644
index 0000000..7bb685a
--- /dev/null
+++ b/test/allocation_test.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2021, 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.
+
+import 'dart:ffi';
+import 'dart:math' show Random;
+
+import 'package:ffi/ffi.dart';
+import 'package:test/test.dart';
+
+const testRuns = 1000;
+
+void main() async {
+ test('calloc', () {
+ // Tests that calloc successfully zeroes out memory.
+ for (var i = 0; i < testRuns; i++) {
+ final allocBytes = Random().nextInt(1000);
+ final mem = calloc<Uint8>(allocBytes);
+ expect(mem.asTypedList(allocBytes).where(((element) => element != 0)),
+ isEmpty);
+ calloc.free(mem);
+ }
+ });
+}