Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Actually it's very easy to avoid data-structure related deadlocks:

- avoid holding two locks simultaneously: that guarantees no deadlocks.

- if you have to hold several locks, always acquire them in the same order in all scenarios.

The "avoid holding multiple locks" rule also harmonizes very well with "minimize the durations/sizes of critical regions". That is, hold a lock over the minimum number of machine instructions necessary. That reduces lock contention, promoting better concurrency.

Of course, we don't get anything free and easy. Not holding multiple locks means that you can't lock in some consistent condition across multiple structures to do an atomic update. Any time you let go of a lock to go do something elsewhere and re-acquire the lock, the "world has changed". If the code hangs on to any previous assumptions about the state (e.g. cached info in local variables), there will be a bug.



"avoid holding two locks simultaneously" only guarantees no deadlocks if locks are your only form of inter-thread-blocking. If you have synchronization points or channels/pipes or anything else, that needs to be expanded to include "avoid using a channel while you hold a lock" and similar for every blocking primitive.


That's right; avoid doing anything that could block while holding a lock: acquiring another lock, waiting on a semaphore, and so on.

Note how condition variables have an aspect of this built-in: the wait operation gives up the mutex.


- Or if you have to hold several locks, grab them all atomically and if even one grab fails, release them all and try again later.

- If you really need all the locks right now, steal the ones you don't own from another thread, and make sure all threads can deal with lock theft.

It's not always easy to sort out the best way to deal with deadlocks. Each approach has significant tradeoffs.


Multiple locks are not always in the same scope. Method Foo::Update acquires a lock, then without releasing the lock calls into another class/object where Bar::Commit also acquires its own lock.


Yep. That's certainly one of the tradeoffs.


How would you do this in Go? If not Go, what language has the capability?


"...shared values are passed around on channels and, in fact, never actively shared by separate threads of execution. Only one goroutine has access to the value at any given time. Data races cannot occur, by design." [...if you stick to this style of programming...]




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: