tomWithFileLock<T> function
Runs body with exclusive access to lockFile among every isolate and
process using this guard, and releases it however body ends.
Waits synchronously, sleeping between attempts. That suits the kernel's own file-backed stores, which present a synchronous contract; code running on an isolate that must stay responsive while it waits — a UI isolate — wants tomWithFileLockAsync.
onBrokenLock is called when an abandoned guard had to be broken, since
that means some other writer died mid-mutation and its last write may not
have landed — worth saying out loud rather than silently recovering from.
It is a callback rather than a log call so this library keeps no dependency
on the logging module, which is itself one of its callers.
Throws a FileSystemException if the guard cannot be taken within
timeout. A caller that could not take the lock has not written, and must
find that out rather than believe it has persisted.
Implementation
T tomWithFileLock<T>(
File lockFile,
T Function() body, {
Duration staleAfter = tomFileLockStaleAfter,
Duration timeout = const Duration(seconds: 30),
void Function(String warning)? onBrokenLock,
}) {
final token = _newToken();
final deadline = DateTime.now().add(timeout);
while (!_attempt(lockFile, token, staleAfter, onBrokenLock)) {
_failIfPast(deadline, lockFile, timeout);
sleep(_retryInterval);
}
try {
return body();
} finally {
_release(lockFile, token);
}
}