In dapper code in Link.TryAdd method, there is the following piece of code:
var snapshot = Interlocked.CompareExchange(ref head, null, null);
Why is this required instead of simple:
var snapshot = head;
both lines do not change the value of head, both lines assign the value of head to snapshot. Why the first one was chosen over the second?
Edit: the code I’m referring to is here: https://github.com/SamSaffron/dapper-dot-net/blob/77227781c562e65c167bf7a933d69291d5bdc6f3/Dapper/SqlMapper.cs
They want to do a volatile read however there is no overload of
Thread.VolatileReadthat takes a generic type parameter. UsingInterlocked.CompareExchangethis way achieves the same result.The problem they are trying to solve is that the JIT compiler can optimize away the assignment to a temp if it sees fit. This can cause threading problems if another thread mutates the head reference while the current thread is using it in a sequence of operations.
Edit:
The issue is not that a stale value is read at beginning of
TryAdd. The problem is that on line 105 they need to compare the current head to the previous head (held insnapshot). If there is an optimization then there is nosnapshotvariable holding the previous value andheadis read again at that point. It is very likely thatCompareExchangesucceeds even though head might have changed between lines 103 and 105. The result is that a node in the list is lost if two threads callTryAddsimultaneously.