Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using pool.dart from dart-lang sample #8

Merged
merged 1 commit into from
Mar 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 21 additions & 15 deletions lib/src/logic_conf_linux.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'linux/hidraw.dart';
import 'linux/libudev.dart';
import 'logic_conf_interface.dart';
import 'linux/libc.dart';
import 'util/pool.dart';

class LogicConfLinux extends LogicConfPlatform {
int _devDescriptor = -1;
Expand All @@ -26,9 +27,10 @@ class LogicConfLinux extends LogicConfPlatform {

var enumerateContext = _libudev.udev_enumerate_new(udevContext);
try {
var hidrawStr = 'hidraw'.toNativeUtf8();
_libudev.udev_enumerate_add_match_subsystem(enumerateContext, hidrawStr.cast());
malloc.free(hidrawStr);
using((Pool pool) {
var nativeUtf8 = 'hidraw'.toNativeUtf8(allocator: pool);
return _libudev.udev_enumerate_add_match_subsystem(enumerateContext, nativeUtf8.cast());
});
_libudev.udev_enumerate_scan_devices(enumerateContext);

var deviceList = _libudev.udev_enumerate_get_list_entry(enumerateContext);
Expand Down Expand Up @@ -70,19 +72,21 @@ class LogicConfLinux extends LogicConfPlatform {
}

Map<String, dynamic>? _getSysAttributes(Pointer<udev_device> rawDevice) {
var hidStr = 'hid'.toNativeUtf8();
// Do not unref parent
var parentDevice = _libudev.udev_device_get_parent_with_subsystem_devtype(rawDevice, hidStr.cast(), nullptr);
malloc.free(hidStr);
var parentDevice = using((Pool pool) {
var nativeUtf8 = 'hid'.toNativeUtf8(allocator: pool);
return _libudev.udev_device_get_parent_with_subsystem_devtype(rawDevice, nativeUtf8.cast(), nullptr);
});
if (parentDevice == nullptr) {
return null;
}

var path = _libudev.udev_device_get_devnode(parentDevice);

var ueventStr = 'uevent'.toNativeUtf8();
var sysAttributeValuesPtr = _libudev.udev_device_get_sysattr_value(parentDevice, ueventStr.cast());
malloc.free(ueventStr);
var sysAttributeValuesPtr = using((Pool pool) {
var nativeUtf8 = 'uevent'.toNativeUtf8(allocator: pool);
return _libudev.udev_device_get_sysattr_value(parentDevice, nativeUtf8.cast());
});
var sysAttributeValues = sysAttributeValuesPtr.cast<Utf8>().toDartString();
for (var attributeValue in sysAttributeValues.split('\n')) {
if (!attributeValue.contains('=')) continue;
Expand All @@ -105,9 +109,10 @@ class LogicConfLinux extends LogicConfPlatform {
}

Uint8List? _readReportDescriptor(String sysfsPath) {
var reportDescPathStr = '$sysfsPath/device/report_descriptor'.toNativeUtf8();
var reportDescriptor = _libc.open2(reportDescPathStr.cast(), O_RDONLY);
malloc.free(reportDescPathStr);
var reportDescriptor = using((Pool pool) {
var nativeUtf8 = '$sysfsPath/device/report_descriptor'.toNativeUtf8(allocator: pool);
return _libc.open2(nativeUtf8.cast(), O_RDONLY);
});
if (reportDescriptor < 0) {
// TODO strerror()
print('open error');
Expand Down Expand Up @@ -219,9 +224,10 @@ class LogicConfLinux extends LogicConfPlatform {

@override
bool openDevice(String path) {
var nativeUtf8 = path.toNativeUtf8();
var descriptor = _libc.open2(nativeUtf8.cast(), O_RDWR);
malloc.free(nativeUtf8);
var descriptor = using((Pool pool) {
var nativeUtf8 = path.toNativeUtf8(allocator: pool);
return _libc.open2(nativeUtf8.cast(), O_RDWR);
});

if (descriptor < 0) {
// TODO strerror()
Expand Down
8 changes: 5 additions & 3 deletions lib/src/logic_conf_macos.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'logic_conf_interface.dart';
import 'macos/constants.dart';
import 'macos/corefoundation.dart';
import 'macos/iokit.dart';
import 'util/pool.dart';

class LogicConfMacos extends LogicConfPlatform {
final _cf = CoreFoundation(DynamicLibrary.open('/System/Library/Frameworks/CoreFoundation.framework/Resources/BridgeSupport/CoreFoundation.dylib'));
Expand All @@ -25,9 +26,10 @@ class LogicConfMacos extends LogicConfPlatform {

@override
bool openDevice(String path) {
var nativeUtf8 = path.toNativeUtf8();
_entryPtr = _io.IORegistryEntryFromPath(kIOMasterPortDefault, nativeUtf8.cast());
malloc.free(nativeUtf8);
_entryPtr = using((Pool pool) {
var nativeUtf8 = path.toNativeUtf8(allocator: pool);
return _io.IORegistryEntryFromPath(kIOMasterPortDefault, nativeUtf8.cast());
});

if (_entryPtr == nullptr) {
print('IORegistryEntryFromPath error');
Expand Down
31 changes: 17 additions & 14 deletions lib/src/logic_conf_windows.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:ffi/ffi.dart';
import 'package:win32/win32.dart';

import 'logic_conf_interface.dart';
import 'util/pool.dart';
import 'windows/hidsdi.dart' as hid;
import 'windows/setupapi.dart' as sp;

Expand Down Expand Up @@ -59,9 +60,10 @@ class LogicConfWindows extends LogicConfPlatform {
// FIXME Utf16.decode
var devicePath = utf8.decode(deviceInterfaceDetailDataPtr.getDevicePathData(requiredSizePtr.value));

var nativeUtf16 = devicePath.toNativeUtf16();
devHandle = CreateFile(nativeUtf16, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, NULL);
malloc.free(nativeUtf16);
devHandle = using((Pool pool) {
var nativeUtf16 = devicePath.toNativeUtf16(allocator: pool);
return CreateFile(nativeUtf16, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, NULL);
});

if (devHandle == INVALID_HANDLE_VALUE) {
print('CreateFile error ${GetLastError()}');
Expand Down Expand Up @@ -128,17 +130,18 @@ class LogicConfWindows extends LogicConfPlatform {

@override
bool openDevice(String path) {
var nativeUtf16 = path.toNativeUtf16();
_devHandle = CreateFile(
nativeUtf16,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
0,
NULL,
);
malloc.free(nativeUtf16);
_devHandle = using((Pool pool) {
var nativeUtf16 = path.toNativeUtf16(allocator: pool);
return CreateFile(
nativeUtf16,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr,
OPEN_EXISTING,
0,
NULL,
);
});

return _devHandle != INVALID_HANDLE_VALUE;
}
Expand Down
171 changes: 171 additions & 0 deletions lib/src/util/pool.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// Copyright (c) 2019, 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.
//
// Explicit pool used for managing resources.

import "dart:async";
import 'dart:ffi';

import 'package:ffi/ffi.dart';

/// An [Allocator] which frees all allocations at the same time.
///
/// The pool allows you to allocate heap memory, but ignores calls to [free].
/// Instead you call [releaseAll] to release all the allocations at the same
/// time.
///
/// Also allows other resources to be associated with the pool, through the
/// [using] method, to have a release function called for them when the pool is
/// released.
///
/// An [Allocator] can be provided to do the actual allocation and freeing.
/// Defaults to using [calloc].
class Pool implements Allocator {
/// The [Allocator] used for allocation and freeing.
final Allocator _wrappedAllocator;

/// Native memory under management by this [Pool].
final List<Pointer<NativeType>> _managedMemoryPointers = [];

/// Callbacks for releasing native resources under management by this [Pool].
final List<Function()> _managedResourceReleaseCallbacks = [];

bool _inUse = true;

/// Creates a pool of allocations.
///
/// The [allocator] is used to do the actual allocation and freeing of
/// memory. It defaults to using [calloc].
Pool([Allocator allocator = calloc]) : _wrappedAllocator = allocator;

/// Allocates memory and includes it in the pool.
///
/// Uses the allocator provided to the [Pool] constructor to do the
/// allocation.
///
/// Throws an [ArgumentError] if the number of bytes or alignment cannot be
/// satisfied.
@override
Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
_ensureInUse();
final p = _wrappedAllocator.allocate<T>(byteCount, alignment: alignment);
_managedMemoryPointers.add(p);
return p;
}

/// Registers [resource] in this pool.
///
/// Executes [releaseCallback] on [releaseAll].
T using<T>(T resource, Function(T) releaseCallback) {
_ensureInUse();
releaseCallback = Zone.current.bindUnaryCallback(releaseCallback);
_managedResourceReleaseCallbacks.add(() => releaseCallback(resource));
return resource;
}

/// Registers [releaseResourceCallback] to be executed on [releaseAll].
void onReleaseAll(Function() releaseResourceCallback) {
_managedResourceReleaseCallbacks.add(releaseResourceCallback);
}

/// Releases all resources that this [Pool] manages.
///
/// If [reuse] is `true`, the pool can be used again after resources
/// have been released. If not, the default, then the [allocate]
/// and [using] methods must not be called after a call to `releaseAll`.
void releaseAll({bool reuse = false}) {
if (!reuse) {
_inUse = false;
}
while (_managedResourceReleaseCallbacks.isNotEmpty) {
_managedResourceReleaseCallbacks.removeLast()();
}
for (final p in _managedMemoryPointers) {
_wrappedAllocator.free(p);
}
_managedMemoryPointers.clear();
}

/// Does nothing, invoke [releaseAll] instead.
@override
void free(Pointer<NativeType> pointer) {}

void _ensureInUse() {
if (!_inUse) {
throw StateError(
"Pool no longer in use, `releaseAll(reuse: false)` was called.");
}
}
}

/// Runs [computation] with a new [Pool], and releases all allocations at the end.
///
/// If [R] is a [Future], all allocations are released when the future completes.
///
/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_
/// cleaned up.
R using<R>(R Function(Pool) computation,
[Allocator wrappedAllocator = calloc]) {
final pool = Pool(wrappedAllocator);
bool isAsync = false;
try {
final result = computation(pool);
if (result is Future) {
isAsync = true;
return (result.whenComplete(pool.releaseAll) as R);
}
return result;
} finally {
if (!isAsync) {
pool.releaseAll();
}
}
}

/// Creates a zoned [Pool] to manage native resources.
///
/// The pool is availabe through [zonePool].
///
/// If the isolate is shut down, through `Isolate.kill()`, resources are _not_ cleaned up.
R withZonePool<R>(R Function() computation,
[Allocator wrappedAllocator = calloc]) {
final pool = Pool(wrappedAllocator);
var poolHolder = [pool];
bool isAsync = false;
try {
return runZoned(() {
final result = computation();
if (result is Future) {
isAsync = true;
result.whenComplete(pool.releaseAll);
}
return result;
}, zoneValues: {#_pool: poolHolder});
} finally {
if (!isAsync) {
pool.releaseAll();
poolHolder.remove(pool);
}
}
}

/// A zone-specific [Pool].
///
/// Asynchronous computations can share a [Pool]. Use [withZonePool] to create
/// a new zone with a fresh [Pool], and that pool will then be released
/// automatically when the function passed to [withZonePool] completes.
/// All code inside that zone can use `zonePool` to access the pool.
///
/// The current pool must not be accessed by code which is not running inside
/// a zone created by [withZonePool].
Pool get zonePool {
final List<Pool>? poolHolder = Zone.current[#_pool];
if (poolHolder == null) {
throw StateError("Not inside a zone created by `usePool`");
}
if (!poolHolder.isEmpty) {
return poolHolder.single;
}
throw StateError("Pool as already been cleared with releaseAll.");
}