Add DisposableAsyncLazy<T>
class to simplify guaranteed disposal of values
#953
Labels
DisposableAsyncLazy<T>
class to simplify guaranteed disposal of values
#953
Is your feature request related to a problem? Please describe.
When the
T
inAsyncLazy<T>
implementsIDisposable
, ensuring that theT
value is disposed of requires very carefully written code. Doing this properly requires:AsyncLazy<T>
Describe the solution you'd like
Declare a new
DisposableAsyncLazy<T> : IDisposable, IAsyncDisposable
class.Its disposal would perform the steps above.
Dispose()
would block on completion of the value factory and disposal where applicable.DisposeAsync()
would return aValueTask
that completes when those same steps have completed.The
T
would not be required to implement any interface, but upon disposal of theDisposableAsyncLazy<T>
, if the instance ofT
implementsIAsyncDisposable
(either BCL or vs-threading variety) then the value will be disposed if created. Otherwise if the instance ofT
implementsIDisposable
that interface will be used to dispose of the value. Note thatT
itself may not derive from these interfaces but if the concrete type that may derive fromT
does, we should dispose of the value as described.When
T
does not implement any recognized disposable interface,DisposableAsyncLazy<T>
may still be an appropriate choice when it is desirableThe value factory will take a
CancellationToken
which is only canceled upon disposal of theDisposableAsyncLazy<T>
instance.Although the
GetValue
andGetValueAsync
methods will also take aCancellationToken
, these will not propagate in any way to the value factory. This way the value factory can honor theCancellationToken
given to it even after side effects may have been applied that would be unsafe to cancel afterward because it can assume cancellation means the overall object graph is being disposed of (e.g. shutdown).The value factory will never be invoked more than once.
Describe alternatives you've considered
Add disposable functionality to
AsyncLazy<T>
itself. This would mean implementingIDisposable
and/orIAsyncDisposable
on the existing class, leading to a great many existing users getting flagged for not disposing of a value that may not need to be disposed of. Unfortunately, we cannot makeAsyncLazy<T>
only implementIDisposable
whenT
itself implements it.We considered making the value factory cancellable multiple times (like #952 proposes) but considered that that would tempt the value factory to ignore the token after side effects have been applied to avoid repeat execution, but ignoring cancellation can slow down app shutdown. When value disposal is a requirement then, we want to focus on just that being the cancellation cause and therefore always something the factory should honor.
Additional context
This design was hashed out with @matteo-prosperi and @SealSlicer.
We also discussed #952 and decided that the two styles of value factory cancellation would be best kept distinct across the two classes.
The text was updated successfully, but these errors were encountered: