Locking

Locking

Is there a good open source MREW lock object out there that

– Supports nested locking 

– Lets a ReadLock be upgraded to a WriteLock and back down again when the write is done

– That doesn’t burn CPU cycles like it was roasting peanuts?

12 thoughts on “Locking


  1. Lars Fosdal You can also use an optimistic approach if write locks are infrequent: acquire a read lock, and if it appears a write is needed, do nothing, release the read lock, then start again with a write lock.


    This means you may do some extra work, but if it’s infrequent it may not matter, as it allows to keep the locking sequence simple and deadlock-proof, as well as use a simpler/faster MREW lock.


    Another thread could slip through between your release and acquisition, but that should not matter, as that other thread could also have acquired the read lock 1 CPU cycle before your release, and that would have had the same practical effect.


    If you can drop your nested lock requirement it will allow you to use the Slim Reader/Writer locks, which are “free” for all practical purposes. And since nested locks are another potential nest for deadlocks, you might as well strive to avoid them anyway.


  2. Avoiding nested locks will be a challenge. We have thousands.  Very rarely deadlocks, though – but the occasional timeout and retry as the locks are exclusive for both read and write now.  I was hoping that allowing parallel reads would solve that, but now I am not so sure.


  3. In my experience I’ve never found a MREW lock that outperformed simpler locks such as a critical section. They’re also prone to deadlocking, as Eric mentioned, just look at the bug trackers for both Boost and Delphi.


    The basic Boost lock (boost::mutex) is non-recursive, and while I cursed at it for a while, after re-arranging my code to fit this requirement I warmed up to it.


    In Delphi terms I’d have an AquireAccess method which would return an interface to allow access to the methods that required the data structure to be locked. AquireAccess() would lock the data structure and pass the lock to the access interface implementation, so that it in turn could release the lock when freed. This allows for automatic unlocking when the access interface goes out of scope, or sooner by nilling the reference.


    I could then pass the access interface to methods which needed to manipulate the data structure.


    This way the data structure manipulation routines on both sides could assume the data structure was properly locked, no need for nested locks. It also makes it much more explicit when the locking occurs and who’s responsible.


    Fit my project at least.


  4. Why not just switch to the RCU pattern? It is pretty efficient for small amount of data, and in practice is very performance friendly if you mostly read the resource.


  5. A. Bouchez From what I can see, RCU only works if it’s acceptable to read stale data? If so, that’s a pretty significant constraint. And as you say, the data has to be small.


  6. We don’t really deal with small amounts of data – but we are slowly changing towards a minimum of locking – at the cost of more database accesses.


  7. More database access can introduce many side-effects depending on the database snapshot capabilities (or lack of).


    Another alternative can be immutable structures, but Delphi lacks in the way of immutable structures (const parameters mutable by helpers and record-with-methods… shudder)

Leave a Reply