Everybody says that immutable objects are thread safe, but why is this?
Take the following scenario running on a multi core CPU:
0x100
and it is cached in the L1/L2 cache of Core 1;0x100
becomes available for new objects;0x100
;0x100
.In this situation, when Core 1 asks for the value at location 0x100
is it possible that it reads the stale data from its L1/L2 cache? My intuition says that a memory gate is still needed here to ensure that Core 1 reads the correct data.
Is the above analysis correct and is a memory gate required, or am I missing something?
UPDATE:
The situation I describe here is a more complex version of what happens every time the GC does a collect. When the GC collects, memory is reordered. This means that the physical location the object was located at changes and that L1/L2 must be invalidated. Roughly the same applies to the example above.
Since it is reasonable to expect that .NET ensures that after reordering memory, different cores see the correct memory state, the above situation will not be a problem too.
The object's immutability isn't the real question in your scenario. Rather, your description's issue revolves around the reference, list, or other system which points to the object. It would of course need some sort of technique to make sure the old object is no longer availble to the thread which may have tried to access it.
The real point to immutable object's thread safety is that you don't need to write a bunch of code to produce thread safety. Rather the framework, OS, CPU (and whatever else) do the work for you.
I think what you're asking is whether, after an object is created, the constructor returns, and a reference to it is stored somewhere, there is any possibility that a thread on another processor will still see the old data. You offer as a scenario the possibility that a cache line holding instance data for the object was previously used for some other purpose.
Under an exceptionally weak memory model, such a thing might be possible, but I would expect any useful memory model, even a relatively weak one, would ensure that dereferencing an immutable object would be safe, even if such safety required padding objects enough that no cache line be shared between object instances (the GC will almost certainly invalidate all caches when it's done, but without such padding, it would be possible that an immutable object created by core #2 might share a cache line with an object that core #1 had previously read). Without at least that level of safety, writing robust code would require so many locks and memory barriers that it would be hard to write multi-processor code that wasn't slower than single-processor code.
The popular x86 and x64 memory models provide the guarantee you seek, and go much further. Processors coordinate 'ownership' of cache lines; if multiple processors want to read the same cache line, they can do so without impediment. When a processor wants to write a cache line, it negotiates with other processors for ownership. Once ownership is acquired, the processor will perform the write. Other processors will not be able to read or write the cache line until the processor that owns the cache line gives it up. Note that if multiple processors want to write the same cache line simultaneously, they will likely spend most of their time negotiating cache-line ownership rather than performing actual work, but semantic correctness will be preserved.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With