views:

163

answers:

5

Let's say I've got a struct that consist of 100 bytes. What guarantees have I got about the following code?

m_myLargeStruct = someValue; // copying 100 bytes
Thread.MemoryBarrier();

// Executed by another thread, after "Thread.MemoryBarrier" was called by the first thread
Console.WriteLine(m_myLargeStruct.ToString());

Does the memory model guarantee that the 100 bytes copy will be complete after the placement of the memory barrier? or do memory barriers only apply for types that are at the size of the processor's architecture? (4 bytes for 32bit and 8 bytes to 64bit).
Is this the reason why the volatile keyword only applies for primitive types? (if i declare an 8 byte member as volatile, this means that an interlocked instrinct will be used to change it's value? [since atomicity isn't guaranteed for types larger than 4 bytes on 32bit machines]).

I hope I was clear enough.. :)
Thanks

+8  A: 

Unless the reading thread has a memory barrier too, I don't think it'll help you much.

Personally I would shy away from:

  • Structs that are that big
  • Getting deeply into the memory model to write lock-free code

... unless you have a really important reason to do so. It's hugely hard to get lock-free coding right with mutable data; I believe that even the experts struggle. I usually find that the "take a lock out for every block accessing the data" approach is easier to get right and is fine in terms of performance for 99% of cases.

I trust the PFX team at Microsoft to get lock-free coding right, and for them to provide me wth ways I can use their code to write my own lock free programs relatively easily. I don't trust myself to get this sort of thing right. If I ever need to explicitly use a memory barrier, that probably means I'm trying too hard.

Jon Skeet
Eamon Nerbonne
@Eamon: You don't need structs to get value semantics, you just need an immutable class. Take the String class for example.
Guffa
I'm asking in a more academic sense ... I don't really have a 100 bytes as a struct. What i'm specifically asking is what is guaranteed, and how. This question isn't about "general good practices at software development" ...
+1  A: 

You need a memory barrier in both places/threads, and of course you need some sort of synchronization between the two so the 2nd's thread's barrier doesn't get 'run' before the first thread's.

Specifically, the writing thread needs a 'release' memory barrier, and the reading thread needs an 'acquire' memory barrier (if the underlying platform supports the separate barrier semantics).

Unless you're asking out of academic curiosity or you're writing your own framework, you should really just use a synchronization object from the library/framework/platform. Trying to get all this stuff correct is tricky, and it's already done in the provided sync objects.

Michael Burr
+2  A: 

You need another memory barrier in the second thread, before WriteLine. (If your system provides asymmetric memory barriers, it's enough to execute a Release barrier after assignment and an Acquire barrier before WriteLine).

Data size does not matter.

atzz
do you know any articles that properly explain this behavior?
Not offhand... though try this [ http://www.niallryan.com/node/138 ] and this [ http://en.wikipedia.org/wiki/Memory_barrier ].
atzz
A: 

Well, first of all you shouldn't have a struct that is that large. Unless you are extremely careful about how you use the struct, it will be slower than using a class. Also, it's counter-intuitive to the value semantics of structs.

That said, the memory barrier will guarantee that the struct is copied. The optimisation will not move any instructions across the barrier.

The volatile keyword is a bit different. It guarantees that no operations against the variable is optimised away, and it does guarantee the order of memory accesses. However, for data types that can't be accessed atomically, it's mostly useless for threading purposes as you can still read half of a new value and half of an old value.

Guffa
No, volatile does guarantee ordering. Writing to a volatile variable has "release" semantics and reading from one has "acquire" semantics IIRC. (See ECMA 335 for details.)
Jon Skeet
@Jon: Yes, you recall correctly. I corrected that part.
Guffa
A: 

Clearly the answer is no, or rather, you have no guarantees about anything. Nothing prevents the operating system from swapping out the thread that is writing to the 100 byte struct before starting the thread that prints out the 100 byte struct.

A memory barrier is used when you want to coordinate access to data through a flag or some other atomic value. I don't know what exactly you are trying to do, so I can't give you good example code about how you should do it.

MSN
After some intensive reading in Duffy's book, I came to realize that this entire question came due to a misconception from my part, thanks