Founder Notes
I Was the Stranger
I asked for a concurrency guard to be made stronger. The stronger version would have deadlocked the system against itself on every single run, deterministically, and the reason is the same line of code that makes the guard work at all.
The setup is ordinary. A publishing pipeline writes to a handful of accounts, and I do not want two runs posting to the same account at the same time, so each account has a lock. Take it before you write, release it after.
The lock is a directory. Creating a directory either succeeds or fails atomically, which is a cheap way to get mutual exclusion without a database. Inside it, the holder writes its own process id. If creation fails, the lock is held, and the caller reads that id and asks the operating system a single question: is that process still alive?
Alive means someone is genuinely mid-write, so back off. Dead means the holder crashed while holding it, so break the lock and take it. That second branch is the whole reason for storing the id. Without it, one crash leaves an account locked forever.
Widening the guard
Two posts had gone out minutes apart when a spacing rule should have kept them further apart. I never reproduced the cause — I formed a hypothesis about a stale read, checked it, and it was wrong. What I had was a rare misbehaviour I could not demonstrate on demand.
So I reached for the blunter fix: rather than take the lock around each individual post, take it once at the start of the run and hold it until the run ends. Widen the critical section until the race has nowhere to live.
That change is correct in isolation. It is also the point where the system starts locking me out.
The refusal
With the lock held for the whole run, the code that publishes a single post still does what it always did: take the lock for that account. It finds the directory already there. It reads the process id inside. It asks the operating system whether that process is alive.
The id is its own.
The answer comes back yes, with perfect accuracy, and the code concludes exactly what it was written to conclude — someone else is mid-write, do not proceed. It refuses. It is now waiting for itself to finish something it has not started, and it will wait forever.
The liveness check is what makes this lock robust. Any holder that dies gets cleaned up automatically. Every failure mode recovers on its own. And that same check is precisely what blinds it to self-ownership, because the process doing the asking is always alive — it is the one asking. Every other holder can die and free the lock. I cannot. I am the only holder it will never let through.
The safety property and the blind spot are not two features that happen to interact. They are the same sentence read twice.
The question that was missing
A process id answers is someone holding this. It does not answer is that someone me. Exclusion needs both questions, and I had only ever needed the first, because until I widened the scope, nothing ever asked for a lock it already held.
The fix is boring, as fixes to this kind of thing usually are. Keep a count per lock inside the process. Taking a lock you already hold increments the count instead of consulting the id. Only the outermost release actually lets go. Re-entering becomes legal; a genuine second process is still refused exactly as before, because for anyone else the count is not there.
What I actually learned
I hardened a system against a failure I could not reproduce, and in the process created one I could reproduce on every run. That sounds worse than it was. The new bug was deterministic, so it surfaced in the first test and cost an afternoon. The original was rare, and rare is what makes a bug expensive.
But the part worth keeping is narrower than test your fixes.
Mutual exclusion assumes the parties are mutually distinct. That assumption is invisible while the scope is small, because a narrow critical section is never re-entered and the case never comes up. Widening the guard does not just make it stricter. It changes who it is guarding against, and if you widen it far enough, the party on the other side of the lock is you.
Any guard strong enough to keep everyone out is strong enough to keep you out. The interesting question is whether it can tell the difference — and a lock that identifies its owner by liveness alone structurally cannot.