Подозреваю что проблема в использовании CompareExchange не по назначению. В частности, возможно, играет роль очерёдность разыменования "ref T". Например если будет:
поток 1:
value = *ref T;//value == ref T == X
поток 2:
Write<T>(ref T, Y)//ref T == Y
поток 1:
comparand = *ref T;//comparand == Y
Interlocked.CEX(ref T, =X, =Y) //упс, непреднамеренно заменили результат Write на первое прочитанное значение
В качестве пруфа — пишет works при замене ReadInterlocked на:
Interlocked.CompareExchange(ref location, null, null);