abandonable<T> method
Runs action and waits for it, but no longer than this job lives.
Throws Cancelled up front if the job is already cancelled or its rules
no longer hold. If a cancellation arrives while action is in flight, the
wait ends there with that Cancelled, and action runs on: its result is
dropped, and an error of its own goes to onError and on to the job's
answer. It ends the waiting, not the work: for anything that must
actually stop — a device, a download, a write — hand the cancellation to
it through onCancel and wait for it to finish with join.
final db = await ctx.abandonable(
Database.open,
discard: (db) => db.close(),
);
dispose runs whatever the outcome, discard unless the job ends
Done, and passing both is an ArgumentError. A value that did not reach
the body is released without a condition, at once.
See Stopping the operation on the cancellation page and A value that comes late on the cleanup rules page.
Implementation
@override
Future<T> abandonable<T>(
FutureOr<T> Function() action, {
FutureOr<void> Function(T value)? dispose,
FutureOr<void> Function(T value)? discard,
}) async {
throwIfFinished('run an abandonable action');
_oneCleanupOnly(dispose, discard);
check();
final result = action();
if (result is! Future<T>) {
// Work handed over with `unattended` waits for the value, so it goes
// on below like the body's: see `join`.
if (_owner.bodyEnded && !_inOwnFork) {
await _keepLate(dispose, discard, result);
return result;
}
// The action may have cancelled this job while it ran, and a value
// made after the mark is one the body must not see: `_race` answers
// that way for a future completing after the mark, and `join` for an
// action of its own. A member whose answer turns on whether the call
// happened to be synchronous is a member nobody can reason about.
//
// The cancellation alone, not `check()`: `abandonable` promises to end
// the waiting when the job is marked, and the rules of a domain are no
// part of that promise.
try {
throwIfCancelled();
} on Cancelled {
final disposer = dispose ?? discard;
if (disposer != null) {
await _dispose(disposer, result);
}
rethrow;
}
_keepOnStack(dispose, discard, result);
return result;
}
return _race(result, dispose, discard);
}