Any programming language gives you plenty of opportunities to shoot yourself in the foot, and C# is probably not the worst, but I ran into an interesting example today.
C# has built-in language support for exclusive locking via the lock
keyword, but not for read/write locking. Fortunately, .NET class library has ReaderWriterLockSlim
, and with the help of some extension methods we can make the locks automatically releaseable upon exit from the code block.
You use lock
to lock built-in locks, and lock custom locks using using
(pardon me the pun). For example:
var exclusiveLock = new object();
lock (exclusiveLock) { /* stuff */ }
// ReaderWriterLockSlim.Write() is an extension method
// It returns an auxilliary object that releases the lock when disposed
var rwLock = new ReaderWriterLock();
using (rwLock.Write()) { /* more stuff */ }
This is bad, but not terrible. What is terrible is that the lock
keyword works on any object, including the custom lock, but not the way you’d expect. In my case I converted from exclusive locks to R/W locks, but forgot to change some lock
keywords into using
, so I ended up with something like this:
var rwLock = new ReaderWriterLockSlim(); lock (rwLock.Write()) { /* stuff */ } using (rwLock.Read()) { /* more stuff */ }
This compiles just fine, but fails at runtime with an exception:
Unhandled Exception: System.Threading.LockRecursionException: A read lock may not be acquired with the write lock held in this mode.
What’s going on here and what does this mean? The write lock is acquired and never released. Then the code proceeds to acquire a read lock, and that (thankfully) fails. The exception message is not ideal, the cryptic “this mode” means “non-recursive locking mode”, which is the default for reader/writer locks in .NET, but that problem is minor.
The idea that any object can have an exclusive lock attached to it looks dubious, but benign. However, the corollary that a read/write lock object can have an exclusive lock attach to it is outright bad. Objects are objects, and locks are locks, and converting any object into an exclusive lock does not do any good to anyone.
The lock
keyword should have been restricted only to actual locks, just like the using
keyword is restricted to objects that implement IDisposable
.
What C# should have had but doesn’t, is built-in support for disposable reader/writer locks. As a result, everyone seems to invent their own variation. Mine is here, loosely based on a StackOverflow article that I can’t easily find now, but I am still not claiming all the credit. You can find a couple of dozen more if you search the Internet, and this is not a good sign, such thing should be standard.
Permalink
Cool. I knew in Java you can lock any object. C# designer(s) probably wanted feature parity with Java. Locking the lock is funny/crazy depending on your POV.
Permalink
The annoying part is in some contexts locking the lock makes sense, and is even recommended: some say it is better to lock on a private field than on a publicly visible object, so that people won’t be able to mess up with your locking state.