简体   繁体   中英

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.

ie

// 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

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.

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.

I think instead of using pthread read/write lock, you can use Posix fcntl() . Here you can upgrade from read to write without any hassle. We are using it for B-tree insertion. Once we come to know the node where insertion happen , we will upgrade it to write lock. Also when we need to split the node we will upgrade the lock of the node, its parent node and children from read to write. Since B-tree is a file based Data structure , it helps to take a lock on region of file.

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 (ie 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 (ie 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM