In the delphi source code we have :
class function TNetEncoding.GetBase64Encoding: TNetEncoding;
var
LEncoding: TBase64Encoding;
begin
if FBase64Encoding = nil then
begin
LEncoding := TBase64Encoding.Create;
if AtomicCmpExchange(Pointer(FBase64Encoding), Pointer(LEncoding), nil) <> nil then
LEncoding.Free
{$IFDEF AUTOREFCOUNT}
else
FBase64Encoding.__ObjAddRef
{$ENDIF AUTOREFCOUNT};
end;
Result := FBase64Encoding;
end;
but I don't understand, they mix Atomic operation (AtomicCmpExchange(Pointer(FBase64Encoding), Pointer(LEncoding), nil)
with non atomic operation like if FBase64Encoding = nil then
and Result := FBase64Encoding;
Is it not a mistake ?
In the comments you make it clear that your concern is that the unprotected memory operations could tear. By tearing we mean that a read thread reads the variable when it is partially written.
That is a valid concern in general, but tearing cannot happen in this case. The reason being is that aligned memory access is guaranteed not to tear. When the memory operation is aligned, as this one is, a reader cannot read a partially written variable. This is typically guaranteed by the hardware bus serializing all memory access within a single cache line.
So, no, this is not a mistake, the code is correct.
The code itself is used to lazily create a singleton. A common technique to do so in a threadsafe manner is double checked locking. This code uses an alternative technique that avoids locking. Instead, the code potentially allows multiple threads to speculatively create the singleton. If more than one thread succeeds in creating the object, the first one to succeed wins, and the other threads destroy their instances and use the one created by the winner thread.
The lock free approach works well provided that it is benign to create additional instances and then destroy them. But this will not always be the case. For example, it may be too expensive to create multiple copies of the instance. In such cases a lock based approach is better.