Provides base types and utilities for managing the loading and disposal of resources, for example when working with packages that do so via ffi such as flutter_soloud.
Set-up
Just add following dependencies to your pubspec.yaml:
dependencies:
disposable_resource_management: ^4.0.0
Usage
AsyncDisposableMixin (or DisposableMixin in scenarios where disposal is
synchronous) can be used to streamline the implementation of disposable types:
class SomeFFIWrapper with AsyncDisposableMixin {
final SomeAsyncFFIInteropService _ffi;
final int _handle;
SomeFFIWrapper._(int handle, SomeAsyncFFIInteropService ffi)
: _handle = handle,
_ffi = ffi;
Future<void> doSomething() {
// Since AsyncDisposableMixin implements the isDisposed property for us, we
// can use it for checks like this to make sure objects are not used after
// disposal.
if (isDisposed) {
throw StateError('Connot use disposed resource.');
}
return _ffi.doSomethingWithResource(_handle);
}
/// Thanks to [AsyncDisposableMixin] the object will not throw if accidentally
/// disposed multiple times, as the [onDisposeAsync] logic will only be run
/// the first time [disposeAsync] is called.
@protected
@override
Future<void> onDisposeAsync() =>
_ffi.asynchronouslyReleaseUnmanagedResource(_handle);
static Future<SomeFFIWrapper> create(SomeAsyncFFIInteropService ffi) async {
final handle = await ffi.asynchronouslyObtainUnmanagedResource();
return SomeFFIWrapper._(handle, ffi);
}
}
AsyncResourceManager (or ResourceManager in scenarios where obtaining +
releasing the resource is synchronous) can then be used to manage loading and
disposal of a resource via tokens, similar to how reference counters work in
languages like C++:
class SomeService with AsyncDisposableMixin {
AsyncResourceToken<SomeFFIWrapper> token;
SomeService(this.token);
// ...
@protected
@override
Future<void> onDisposeAsync() => token.disposeAsync();
}
void main() async {
final ffi = SomeAsyncFFIInteropService();
final resourceManager = AsyncResourceManager<SomeFFIWrapper>(
loadResource: () => SomeFFIWrapper.create(ffi),
releaseResource: (wrapper) => wrapper.disposeAsync(),
);
// The resource gets obtained on the first obtainToken() call.
final service1 = SomeService(await resourceManager.obtainToken());
final service2 = SomeService(await resourceManager.obtainToken());
// The resource will not be released yet since service2 still has an
// un-disposed token.
await service1.disposeAsync();
// The resource is now released when service2's token gets disposed.
await service2.disposeAsync();
// This obtains the resource again
final token1 = await resourceManager.obtainToken();
// We can also propagate the token to get another token.
// This is done synchronously, since propagation isn't allowed for disposed
// tokens and therefore does not require loading the resource as it is
// guaranteed to already be loaded.
final token2 = token1.propagate();
// The resource will not be released yet because the propagated token2 still
// is not disposed.
await token1.disposeAsync();
// The resource gets released again when all tokens for the resource are
// disposed.
await token2.disposeAsync();
}
Libraries
- disposable_resource_management
- Provides base types and utilities for managing the loading and disposal of resources.