Streaming Secure Calls

It is common practice to avoid race conditions (in multi-threaded applications) when triggering events:

EventHandler<EventArgs> temp = SomeEvent; if (temp != null) temp(e); "Remember that delegates are immutable and this is why this technique works in theory. However, what a lot of developers don't realize is that this code could be optimized by the compiler to remove the local temp variable entirely. If this happens, this version of the code is identical to the first version, so a NullReferenceException is still possible." 

The problem (according to the book) is that "this code can be optimized by the compiler to completely remove the local temporary variable. If this happens, this version of the code is identical to the first version, so a NullReferenceException is still possible"

According to the CLR via C #, here is the best way to get the compiler to copy the event pointer.

 virtual void OnNewMail(NewMailEventArgs e) { EventHandler<NewMailEventArgs> temp = Interlocked.CompareExchange(ref NewMail, null, null); if (temp != null) temp(this, e); } 

Here CompareExchange changes the NewMail link to null if it is null and does not change NewMail if it is not null. In other words, CompareExchange does not change the value in NewMail at all, but returns the value inside NewMail in atomic thread safe mode. Richter, Jeffrey (2010-02-12). CLR via C # (p. 265). OReilly Media - A. Kindle Edition.

I use the .Net 4.0 framework and I donโ€™t know how this can work because Interlocked.CompareExchange expects a link to a location, not an event link.

Either there is a mistake in the book, or I misinterpreted it. Has anyone implemented this method? Or do you have a better way to prevent race conditions here?

UPDATE

It was my mistake, iterlocked code works. I simply incorrectly defined it, but according to Bradley (below), it is not needed in .net 2.0 and higher on windows.

+8
multithreading c #
source share
4 answers

The compiler (or JIT) is not allowed to optimize this if/temp (in CLR 2.0 and later); The CLR 2.0 memory model does not allow reading data from the heap (rule No. 2).

Thus, MyEvent cannot be read a second time; The temp value should be read in the if .

See my blog post for an extended discussion of this situation and an explanation of why the standard template is fine.

However, if you work with a CLR (e.g. mono) other than Microsoft, which does not provide a guarantee for the CLR 2.0 memory model (but only follows the ECMA memory model), or you work on Itanium (which has a known weak model of hardware memory), you will need a code, such as Richter, to eliminate a potential race condition.

Regarding your question about Interlocked.CompareExchange , the syntax for public event EventHandler<NewMailEventArgs> NewMail is just C # syntactic sugar for declaring a private field like EventHandler<NewMailEventArgs> and a public event that has add and remove methods. The Interlocked.CompareExchange call reads the value of the private EventHandler<NewMailEventArgs> field, so this code compiles and works as described in Richter; it is simply not needed in the Microsoft CLR.

+7
source share

Now this is only a partial answer to your question, because I can not comment on the use of Interlocked.CompareExchange, however I think this information may be useful.

The problem is that the compiler can optimize this if / temp away,

Well, according to the CLR via C # (pp. 264-265)

[The] code can be optimized by the compiler to completely remove the local variable [...]. If this happens, this version of the code is identical [to the version that references the event twice), so a NullReferenceException is still possible.

This is possible , however, but it is important to know that the Microsoft On-Time Compiler (JIT) never optimizes a local variable. Although this may change, it is unlikely because it is likely to break many applications.

This is because .NET has a strong memory model: http://msdn.microsoft.com/en-us/magazine/cc163715.aspx#S5

Reading and writing cannot be entered.

and

The model does not allow readings to be introduced, because this implies re-reading the value from the memory, and in the memory with a low lock level it can change.

However, Mono, which follows a much weaker memory model , can optimize a local variable.

Bottom line: if you do not plan to use Mono, do not worry about it.

And even then this behavior can be suppressed with the help of volatile declarations.

+3
source share

I assume you missed the interpretation. Location means only a pointer to an object reference [msdn version: destination object that is compared with the comparison and possibly replaced.]. The following code works fine .NEt 4.0

 public class publisher { public event EventHandler<EventArgs> TestEvent; protected virtual void OnTestEvent(EventArgs e) { EventHandler<EventArgs> temp = Interlocked.CompareExchange(ref TestEvent, null, null); if (temp != null) temp(this,e); } } 
+1
source share

I look at the created IL, you will see that the method is called like that

 IL_000d: ldsflda class [mscorlib]System.EventHandler`1<class [mscorlib]System.EventArgs> ConsoleApplication1.Program::MyEvent IL_0012: ldnull IL_0013: ldnull IL_0014: call !!0 [mscorlib]System.Threading.Interlocked::CompareExchange<class [mscorlib]System.EventHandler`1<class [mscorlib]System.EventArgs>>(!!0&,!!0,!!0) 

see ldsflda - my event is static, but it loads the address of the field . This field is an automatically generated delegate field that the compiler generates for each event.

the field is defined as follows:

 .field private static class [mscorlib]System.EventHandler`1<class [mscorlib]System.EventArgs> MyEvent 
0
source share

All Articles