pthreads: reader/writer locks, upgrading read lock to write lock

What else than a dead lock do you want in the following scenario?

  • thread 1 acquire read lock
  • thread 2 acquire read lock
  • thread 1 ask to upgrade lock to write
  • thread 2 ask to upgrade lock to write

So I'd just release the read lock, acquire the write lock and check again if I've to make the update or not.


The pthread library does not support this operation directly.

As a workaround, you can define a mutex to protect the lock:

  • To acquire the lock, first acquire the mutex, then the lock (read or write as needed), then release the mutex. (Never acquire the lock without holding the mutex.)
  • To release the lock, just release it (no mutex required here).
  • To upgrade the lock, acquire the mutex, release the read lock, acquire the write lock, then release the mutex.
  • To downgrade the lock, acquire the mutex, release the write lock, acquire the read lock, then release the mutex.

This way, no other thread can snatch the write lock while you are trying to upgrade it. However, your thread will block if other threads are holding the read lock when you try to upgrade.

Also, as mentioned above, if two threads are trying to upgrade the same lock at the same time you will encounter a deadlock:

  • T1 and T2 both hold a read lock.
  • T1 wishes to upgrade, acquires the mutex, releases the read lock and tries to acquire the write lock. This is blocked by T2.
  • T2 wishes to upgrade, tries to acquire the mutex and is blocked by T1.

Takeaway from my CS lectures: Deadlocks cannot be reliably avoided. For every strategy proposed, there is at least one use case in which the strategy is impractical. The only thing you can do is detect deadlock conditions (i.e. if a call fails with EDEADLK) and make sure your code is prepared to deal with that situation. (How to recover depends heavily on your code.)

Downgrading in this manner is not prone to deadlocks¹, although a downgrade and a concurrent upgrade can deadlock. If only one of your threads upgrades that lock in this manner (and other threads get a write lock immediately if needed), there is also no risk of deadlock¹.

As others have said, acquiring a write lock immediately when you might need it would be an alternative which is not prone to deadlocking¹, but might unnecessarily prevent other read operations from taking place concurrently.

Conclusion: It depends on your code.

If the read-only phase is brief (i.e. brief enough so you can afford blocking other read operations during that time), then I would go for the gratuitous write lock approach.

If the read-only phase may last long and blocking other reads during that time is unacceptable, go for the mutex-protected lock upgrade, but either limit it to one thread per lock (“only T1 may upgrade lock L42, but not other threads”) or provide a way to detect and recover from deadlocks.


¹ Unless resources other than this lock and its mutex come into play