withLock
Runs block while holding the mutex for key.
Callers with equal keys run block one at a time; callers with other keys are unaffected.
Cancellation: waiting for the key's mutex is cancellable and rethrows; cancellation inside block releases the mutex and rethrows. Entry cleanup runs in a NonCancellable context, so a cancelled caller never leaks its entry.