Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How thread safe are immutable objects?

Everybody says that immutable objects are thread safe, but why is this?

Take the following scenario running on a multi core CPU:

  • Core 1 reads an object at memory location 0x100 and it is cached in the L1/L2 cache of Core 1;
  • The GC collects this object at that memory location because it has become eligible and 0x100 becomes available for new objects;
  • Core 2 allocates an (immutable) object which is located at address 0x100;
  • Core 1 gets a reference to this new object and reads it at memory location 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.

like image 319
Pieter van Ginkel Avatar asked Mar 25 '11 14:03

Pieter van Ginkel


2 Answers

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.

like image 56
John Fisher Avatar answered Nov 18 '22 06:11

John Fisher


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.

like image 31
supercat Avatar answered Nov 18 '22 04:11

supercat