| // 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. |
| // |
| // @dart = 2.9 |
| |
| import 'dart:async'; |
| import 'dart:ffi'; |
| |
| import 'package:ffi/ffi.dart'; |
| import 'package:test/test.dart'; |
| |
| // TODO(dacoharkes): After merging into `package:ffi`, roll package in DEPS |
| // and remove arena.dart. |
| import 'arena.dart'; |
| |
| void main() async { |
| test('sync', () async { |
| List<int> freed = []; |
| void freeInt(int i) { |
| freed.add(i); |
| } |
| |
| using((Arena arena) { |
| arena.using(1234, freeInt); |
| expect(freed.isEmpty, true); |
| }); |
| expect(freed, [1234]); |
| }); |
| |
| test('async', () async { |
| /// Calling [using] waits with releasing its resources until after |
| /// [Future]s complete. |
| List<int> freed = []; |
| void freeInt(int i) { |
| freed.add(i); |
| } |
| |
| Future<int> myFutureInt = using((Arena arena) { |
| return Future.microtask(() { |
| arena.using(1234, freeInt); |
| return 1; |
| }); |
| }); |
| |
| expect(freed.isEmpty, true); |
| await myFutureInt; |
| expect(freed, [1234]); |
| }); |
| |
| test('throw', () { |
| /// [using] waits with releasing its resources until after [Future]s |
| /// complete. |
| List<int> freed = []; |
| void freeInt(int i) { |
| freed.add(i); |
| } |
| |
| // Resources are freed also when abnormal control flow occurs. |
| var didThrow = false; |
| try { |
| using((Arena arena) { |
| arena.using(1234, freeInt); |
| expect(freed.isEmpty, true); |
| throw Exception('Exception 1'); |
| }); |
| } on Exception { |
| expect(freed.single, 1234); |
| didThrow = true; |
| } |
| expect(didThrow, true); |
| }); |
| |
| test( |
| 'allocate', |
| () { |
| final countingAllocator = CountingAllocator(); |
| // To ensure resources are freed, wrap them in a [using] call. |
| using((Arena arena) { |
| final p = arena<Int64>(2); |
| p[1] = p[0]; |
| }, countingAllocator); |
| expect(countingAllocator.freeCount, 1); |
| }, |
| ); |
| |
| test('allocate throw', () { |
| // Resources are freed also when abnormal control flow occurs. |
| bool didThrow = false; |
| final countingAllocator = CountingAllocator(); |
| try { |
| using((Arena arena) { |
| final p = arena<Int64>(2); |
| p[0] = 25; |
| throw Exception('Exception 2'); |
| }, countingAllocator); |
| } on Exception { |
| expect(countingAllocator.freeCount, 1); |
| didThrow = true; |
| } |
| expect(didThrow, true); |
| }); |
| |
| test('toNativeUtf8', () { |
| final countingAllocator = CountingAllocator(); |
| using((Arena arena) { |
| final p = 'Hello world!'.toNativeUtf8(allocator: arena); |
| expect(p.toDartString(), 'Hello world!'); |
| }, countingAllocator); |
| expect(countingAllocator.freeCount, 1); |
| }); |
| |
| test('zone', () async { |
| List<int> freed = []; |
| void freeInt(int i) { |
| freed.add(i); |
| } |
| |
| withZoneArena(() { |
| zoneArena.using(1234, freeInt); |
| expect(freed.isEmpty, true); |
| }); |
| expect(freed.length, 1); |
| expect(freed.single, 1234); |
| }); |
| |
| test('zone async', () async { |
| /// [using] waits with releasing its resources until after [Future]s |
| /// complete. |
| List<int> freed = []; |
| void freeInt(int i) { |
| freed.add(i); |
| } |
| |
| Future<int> myFutureInt = withZoneArena(() { |
| return Future.microtask(() { |
| zoneArena.using(1234, freeInt); |
| return 1; |
| }); |
| }); |
| |
| expect(freed.isEmpty, true); |
| await myFutureInt; |
| expect(freed.length, 1); |
| expect(freed.single, 1234); |
| }); |
| |
| test('zone throw', () { |
| /// [using] waits with releasing its resources until after [Future]s |
| /// complete. |
| List<int> freed = []; |
| void freeInt(int i) { |
| freed.add(i); |
| } |
| |
| // Resources are freed also when abnormal control flow occurs. |
| bool didThrow = false; |
| try { |
| withZoneArena(() { |
| zoneArena.using(1234, freeInt); |
| expect(freed.isEmpty, true); |
| throw Exception('Exception 3'); |
| }); |
| } on Exception { |
| expect(freed.single, 1234); |
| didThrow = true; |
| } |
| expect(didThrow, true); |
| expect(freed.single, 1234); |
| }); |
| |
| test('allocate during releaseAll', () { |
| final countingAllocator = CountingAllocator(); |
| final arena = Arena(countingAllocator); |
| |
| arena.using(arena<Uint8>(), (Pointer discard) { |
| arena<Uint8>(); |
| }); |
| |
| expect(countingAllocator.allocationCount, 1); |
| expect(countingAllocator.freeCount, 0); |
| |
| arena.releaseAll(reuse: true); |
| |
| expect(countingAllocator.allocationCount, 2); |
| expect(countingAllocator.freeCount, 2); |
| }); |
| } |
| |
| /// Keeps track of the number of allocates and frees for testing purposes. |
| class CountingAllocator implements Allocator { |
| final Allocator wrappedAllocator; |
| |
| int allocationCount = 0; |
| int freeCount = 0; |
| |
| CountingAllocator([this.wrappedAllocator = calloc]); |
| |
| @override |
| Pointer<T> allocate<T extends NativeType>(int byteCount, {int alignment}) { |
| allocationCount++; |
| return wrappedAllocator.allocate(byteCount, alignment: alignment); |
| } |
| |
| @override |
| void free(Pointer<NativeType> pointer) { |
| freeCount++; |
| return wrappedAllocator.free(pointer); |
| } |
| } |