// 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. // // Sample illustrating resource management with an explicit pool. import 'dart:async'; import 'dart:ffi'; import 'package:expect/expect.dart'; import 'pool.dart'; import 'utf8_helpers.dart'; import '../dylib_utils.dart'; main() async { final ffiTestDynamicLibrary = dlopenPlatformSpecific("ffi_test_dynamic_library"); final MemMove = ffiTestDynamicLibrary.lookupFunction< Void Function(Pointer, Pointer, IntPtr), void Function(Pointer, Pointer, int)>("MemMove"); // To ensure resources are freed, wrap them in a [using] call. using((Pool pool) { final p = pool(2); p[0] = 24; MemMove(p.elementAt(1).cast(), p.cast(), sizeOf()); print(p[1]); Expect.equals(24, p[1]); }); // Resources are freed also when abnormal control flow occurs. try { using((Pool pool) { final p = pool(2); p[0] = 25; MemMove(p.elementAt(1).cast(), p.cast(), 8); print(p[1]); Expect.equals(25, p[1]); throw Exception("Some random exception"); }); // `calloc.free(p)` has been called. } on Exception catch (e) { print("Caught exception: $e"); } // In a pool multiple resources can be allocated, which will all be freed // at the end of the scope. using((Pool pool) { final p = pool(2); final p2 = pool(2); p[0] = 1; p[1] = 2; MemMove(p2.cast(), p.cast(), 2 * sizeOf()); Expect.equals(1, p2[0]); Expect.equals(2, p2[1]); }); // If the resource allocation happens in a different scope, then one either // needs to pass the pool to that scope. f1(Pool pool) { return pool(2); } using((Pool pool) { final p = f1(pool); final p2 = f1(pool); p[0] = 1; p[1] = 2; MemMove(p2.cast(), p.cast(), 2 * sizeOf()); Expect.equals(1, p2[0]); Expect.equals(2, p2[1]); }); // Using Strings. using((Pool pool) { final p = "Hello world!".toUtf8(pool); print(p.contents()); }); final allocateResource = ffiTestDynamicLibrary.lookupFunction< Pointer Function(), Pointer Function()>("AllocateResource"); final useResource = ffiTestDynamicLibrary.lookupFunction< Void Function(Pointer), void Function(Pointer)>("UseResource"); final releaseResource = ffiTestDynamicLibrary.lookupFunction< Void Function(Pointer), void Function(Pointer)>("ReleaseResource"); // Using an FFI call to release a resource. using((Pool pool) { final r = pool.using(allocateResource(), releaseResource); useResource(r); }); // Using an FFI call to release a resource with abnormal control flow. try { using((Pool pool) { final r = pool.using(allocateResource(), releaseResource); useResource(r); throw Exception("Some random exception"); }); // Resource has been freed. } on Exception catch (e) { print("Caught exception: $e"); } /// [using] waits with releasing its resources until after [Future]s /// complete. List freed = []; freeInt(int i) { freed.add(i); } Future myFutureInt = using((Pool pool) { return Future.microtask(() { pool.using(1, freeInt); return 1; }); }); Expect.isTrue(freed.isEmpty); await myFutureInt; Expect.equals(1, freed.single); } /// Represents some opaque resource being managed by a library. class SomeResource extends Opaque {}