Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm using read/write locks on Linux and I've found that trying to upgrade a read locked object to a write lock deadlocks.

i.e.

// acquire the read lock in thread 1.
pthread_rwlock_rdlock( &lock );

// make a decision to upgrade the lock in threads 1.
pthread_rwlock_wrlock( &lock ); // this deadlocks as already hold read lock.

I've read the man page and it's quite specific.

The calling thread may deadlock if at the time the call is made it holds the read-write lock (whether a read or write lock).

What is the best way to upgrade a read lock to a write lock in these circumstances.. I don't want to introduce a race on the variable I'm protecting.

Presumably I can create another mutex to encompass the releasing of the read lock and the acquiring of the write lock but then I don't really see the use of read/write locks. I might as well simply use a normal mutex.

Thx

like image 696
ScaryAardvark Avatar asked Mar 09 '10 08:03

ScaryAardvark


3 Answers

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.

like image 146
AProgrammer Avatar answered Nov 13 '22 16:11

AProgrammer


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

like image 24
user149408 Avatar answered Nov 13 '22 16:11

user149408


Easiest and safest would be to take the write-lock from the moment you could want to change your data instead of from the moment you're sure you will change it. I know that this will make access to your data a bit more serialized.

I was a bit surprised when reading this question, because I never even considered first taken a read-lock and then upgrading to a write-lock. Well, different situation could need different approaches.

like image 1
stefaanv Avatar answered Nov 13 '22 15:11

stefaanv