tryCollect method
collectUnused, answering null when it was refused because sync had not caught up, the one refusal a later pass can overcome; a pass that does not look for another reason answers 0, as collectUnused does.
Passes run one at a time, and one asked for while another waits to start joins it. A pass checks sync and scans local storage without holding up cuts, and takes their turn only to delete.
Visible so a test can tell a pass sync refused from one that answered.
Implementation
@visibleForTesting
Future<int?> tryCollect(CryptoContext context) {
final waiting = _waitingPass;
if (waiting != null) return waiting;
final pass = _collecting.then((_) {
_waitingPass = null;
return _collectUnused(context);
});
_waitingPass = pass;
_collecting = pass.then((_) {}, onError: (_) {});
return pass;
}