Is the following code safe (considering it in isolation) from a torn read?
private static double flopsErrorMargin = 0.01d;
public static double FlopsErrorMargin {
get {
double result = flopsErrorMargin;
Thread.MemoryBarrier();
return result;
}
set {
Interlocked.Exchange(ref flopsErrorMargin, value);
}
}
The atomic write operation (Interlocked.Exchange()
) is required because a double is not guaranteed to be written in a single operation on the .NET platform (excepting implementation details on 64-bit environments).
However, do I also need a 'mirror' operation on the read side? E.g. am I still at risk of getting a torn read because I do not read the value atomically?
My hunch is that I will not, because I think that another memory access (e.g. a read) can not happen concurrently with any other atomic operation, even if that other access is not itself atomic. But I would like some confirmation!
No, Torn reads are possible. Assume your field access is reading the data and interleaved partway by the Interlocked.Exchange
, then other 32 bits will be the updated value of Exchange
and thus produces torn read.
For atomic read, you need to use Interlocked.Read (in 32 bit machines).
The Read method is unnecessary on 64-bit systems, because 64-bit read operations are already atomic. On 32-bit systems, 64-bit read operations are not atomic unless performed using Read
Which also means that torn values are possible.
You could define your own atomic Read
for double
as follows
public static double Read(ref double location)
{
return Interlocked.CompareExchange(ref location, 0d, 0d);
}
This is how Interlocked.Read(long)
is implemented internally.