Use lock addresses to enforce uniform locking order. Avoid deadlock
git.kernel.org
git.kernel.org
The problem is that this strategy is only applicable in a very-very limited context, in most cases it's useless because the number of lockable resources changes dynamically and/or it's not feasible/scalable to get all locks up to N+1.
Therefore, for large scale systems, locks are an anti-pattern.
Indeed, this extends quite naturally to locks that are not related from modules that are not aware of each other.
I repeat, just brilliant.
It fails you if you have a monadic rather than applicative requirement on the computation; that is, if intermediate computations can require you to take more locks that you couldn't have predicted needing in advance. In that case, you're going to need a transactional-memory system that can abort and retry computations, and that is powerful but hard to get right.
It's not that limited. Rule is that, if you want to acquire locks N and M > N, you have to do it in the order N,M. That is sufficient to guarantee that the thread holding the highest numbered lock can make progress.
Main limitation, AFAIK, is that you do not always know which locks you need. Does your concurrent hash map use a lock? Can you tell whether library call X uses one? Which one? It a.so can degrade performance a bit. For example, your memory allocator may occasionally use a lock, but most of the time, it will give you memory out of a thread-local pool.
This is one of those things that seems baffling at first, but then once you see the rationale it's "why didn't I think of that!?" obvious. I'm guessing the one who submitted this experienced such a moment.
Ownership semantics make it safer (can't leak the internal object), but considering how unsafe and dependent on proper usage locks tend to be, I wonder why I've never seen that idea in e.g. Python or Ruby, it would make the right thing easier to do either way, even if there would remain a possibility of leaking the internal object (and even then, I CPython might be able to check the lockee's refcount on lock/unlock, possibly optionally).
Does it solve the case here? The two locks ordered by the address check are otherwise unordered, i.e., one is not inherently owned by the other. One could imagine the same data structure in a Rust program: a vector of runqueues, all on equal footing, and the programmer wants to lock an arbitrary pair (i, j) of them. This would have to look something like:
{
let mut guard1 = locks[i].lock().unwrap();
let mut guard2 = locks[j].lock().unwrap();
do_work(*guard1, *guard2);
}
And you have the same problem as this post addresses: we need a total order over indices (e.g., ensure i < j) to prevent deadlock.Absolutely brilliant! Hats-off to the author of this snippet.