Warning: Seriously geeky stuff ahead. May not be suitable for children or anyone who doesn’t love the minutia of JIT optimizations and the CLR memory model.
A reader, Russ, asks:
Given the importance of volatile in light of Greg’s comments here: http://blogs.msdn.com/grantri/archive/2004/09/07/226355.aspx. why doesn’t vb.net support a similar construct? or are we not allowed to write high-performance multithreaded code using vb? :p
The construct that Russ is referring to is volatile fields. C# allows you to declare field with the “volatile” modifier, which results in all accesses to the field being prepended in the IL with the opcode “volatile.” I’d like to pretend that I could adequately explain to you what volatile reads and writes actually do, but there is a high probability that I would get it wrong because the CLR memory model is not my thing. It is, however, Vance’s thing and you can check out a message he wrote that talks in more detail about the subject.
Although VB doesn’t support the volatile modifier, you’re not up a creek without a paddle. In fact, as I said above, marking a field as volatile in C# doesn’t do anything to the field itself – it just makes sure that the compiler prefixes all reads and writes with the volatile modifier. Volatility is really a property of reading and writing from a field, not a property of the field itself. In fact, there may be situations where you only care at certain points whether you do a volatile read or write, in which case the volitile field modifier is overkill (because it makes all reads and writes volatile). A slightly more ideal situation would be able to mark just a particular read or write as being volatile.
The CLR provides a way to do this, sort of. The type System.Threading.Thread provides VolatileRead and VolatileWrite methods that allow you to do volatile reads and writes from fields without having to do any declarative work at all. So, everything is right with the world, correct? Unfortunately, no. If you look at the definition of VolatileRead and VolatileWrite, they use reference parameters (ByRef) to refer to the field that you’re reading or writing from. This is necessary because if they were regular value parameters (ByVal) the VolatileWrite wouldn’t work at all and VolatileRead wouldn’t give you the desired behavior. (That’s because value parameters are copied onto the stack. That copy wouldn’t be a volatile read. When you pass the address of the value, then the VolatileRead method can do a volatile read of the value at that address.)
The problem is that when you pass a variable to a reference parameter, the type of the variable must exactly match the type of the reference parameter. If it doesn’t, then you have to create a temporary variable of the right type, copy the value into the temporary, call the method and then copy the value out of the temporary. This isn’t an issue for primitive types because there are overloads of VolatileRead and VolatileWrite for all the primitive types. But what about some random reference type like an array (which occurs in Grant’s example)? The closest overload is a ByRef parameter of Object, but that’s still not going to be an exact match. That means you fall into the copy-in/copy-out scheme I talked about a moment ago, which screws everything up because the reads and writes to the temporary variable are not volatile! Arrrrg!
So, are VB users just screwed? Fortunately, no. There’s another method on System.Threading.Thread that can help out here: MemoryBarrier. When you call MemoryBarrier before a write or after a read, you effectively make the read or write a volitile read or write. (In fact, in Whidbey this is how VolatileRead and VolatileWrite are implemented.) You could even write a generic VolatileRead and VolatileWrite to do this for you in VB 2005:
Function VolatileRead(Of T)(ByRef Address As T) As T
VolatileRead = Address
Sub VolatileWrite(Of T)(ByRef Address As T, ByVal Value As T)
Address = Value
So, yes, VB users are allowed to write high-performance multithreaded code… 🙂