C# Garbage Collector, Threading and Compiler/Jitter optimization

Let's assume that our program has a central point (an instance of a Document class), where all kinds of information are referenced. Now we have two threads. Both threads have access to our "document" and "document" contains a reference to let's say "params" (an object that holds some kind of information). So if we have a reference to "document" we can use "document.params" to get our params object.

Thread 1 does the following:

Params tempParams = document.params; // get a local reference to documents.params
int a = tempParams.a; // read data from params
// thread 1 (this thread) gets interrupted by thread 2
int b = tempParams.b; // read data from params
int c = tempParams.c; // read data from params

Thread 2 does the following:

Params newParams = new Params();
... // fill newParams with new parameters
lock(obj) {
    document.params = newParams; // update params in document
}

So the content of "params" is never changed, but if a change is needed a new copy is generated and the reference "document.params" get's updated to the new Params block which is an atomic action.

Now the big question is:

Is it possible that the jitter might optimize the code of thread 1 that way that tempParams is not a memory address but a CPU register? And if Thread 2 updates the document.params reference, there is no reference in memory that points to the old "Params" block, since the reference in Thread 1 is only in a CPU register. And if just in this moment the garbage collector starts, how can it see that the old "Params" block is still in use?

The other question would be: might it happen that the jitter optimizes away the tempParams variable and uses document.params.a/b/c directly. In this case Thread 1 would see the swap of the Params object which is not intended. The use of tempParams shall ensure that Thread 1 access a/b/c from the same Params object that was in document.params when the reference was copied.

Jon Skeet
people
quotationmark

Is it possible that the jitter might optimize the code of thread 1 that way that tempParams is not a memory address but a CPU register?

I suspect that's possible - but that won't stop the garbage collector from treating it as a use of the reference. If it did, that would be a GC bug.

The other question would be: might it happen that the jitter optimizes away the tempParams variable and uses document.params.a/b/c directly.

That would be a JIT bug, IMO. There's no guarantee that thread 1 will see a change to document.params (so that's still a risk to consider in different scenarios), but given that it has copied the reference into a local variable (tempParams) and that variable never changes its value, all accesses via tempParams will address the same object. (There's no risk that tempParams.a will read from one object but tempParams.b will read from a different one.)

Just to bring some of the commentary below into this answer - there's some discussion around whether it's valid for a JIT to "optimize" code such that it appears to change the value of a local variable. This MSDN article certainly suggests it would be valid, for example. I saw something similar and blogged about it a long time ago. I'm 99% sure I talked to someone (possibly Joe Duffy) around whether that effective read introduction was valid by ECMA-335, and their impression was that it wasn't. However, I can't find any definite documentation for that, and ECMA-335 is at least unclear on the matter.

The ECMA-335 (CLI) specification is definitely laxer than the CLR 2.0 model that MS has implemented for some time now, but I don't believe it's quite that lax. If you can't rely on local variables being isolated from change, it's very hard to write any valid code, IMO.

people

See more on this question at Stackoverflow