From 7d21ea0f0c566bbce72430fb5f0db242d43724ee Mon Sep 17 00:00:00 2001 From: wangkun Date: Wed, 3 Mar 2021 16:41:10 +0800 Subject: [PATCH] using pool.dart from dart-lang sample Signed-off-by: wangkun --- lib/src/logic_conf_linux.dart | 36 ++++--- lib/src/logic_conf_macos.dart | 8 +- lib/src/logic_conf_windows.dart | 31 +++--- lib/src/util/pool.dart | 171 ++++++++++++++++++++++++++++++++ 4 files changed, 214 insertions(+), 32 deletions(-) create mode 100644 lib/src/util/pool.dart diff --git a/lib/src/logic_conf_linux.dart b/lib/src/logic_conf_linux.dart index 4e2c9f4..8e669e6 100644 --- a/lib/src/logic_conf_linux.dart +++ b/lib/src/logic_conf_linux.dart @@ -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; @@ -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); @@ -70,19 +72,21 @@ class LogicConfLinux extends LogicConfPlatform { } Map? _getSysAttributes(Pointer 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().toDartString(); for (var attributeValue in sysAttributeValues.split('\n')) { if (!attributeValue.contains('=')) continue; @@ -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'); @@ -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() diff --git a/lib/src/logic_conf_macos.dart b/lib/src/logic_conf_macos.dart index eaf1ec5..bc99806 100644 --- a/lib/src/logic_conf_macos.dart +++ b/lib/src/logic_conf_macos.dart @@ -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')); @@ -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'); diff --git a/lib/src/logic_conf_windows.dart b/lib/src/logic_conf_windows.dart index b9ea69e..c1a6254 100644 --- a/lib/src/logic_conf_windows.dart +++ b/lib/src/logic_conf_windows.dart @@ -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; @@ -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()}'); @@ -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; } diff --git a/lib/src/util/pool.dart b/lib/src/util/pool.dart new file mode 100644 index 0000000..7ad0e85 --- /dev/null +++ b/lib/src/util/pool.dart @@ -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> _managedMemoryPointers = []; + + /// Callbacks for releasing native resources under management by this [Pool]. + final List _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 allocate(int byteCount, {int? alignment}) { + _ensureInUse(); + final p = _wrappedAllocator.allocate(byteCount, alignment: alignment); + _managedMemoryPointers.add(p); + return p; + } + + /// Registers [resource] in this pool. + /// + /// Executes [releaseCallback] on [releaseAll]. + T using(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 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 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 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? 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."); +} \ No newline at end of file