To get the instance of the class with Singleton pattern, I want use the following function:
This is a sketch
interface
uses SyncObjs;
type
TMCriticalSection = class(TCriticalSection)
private
Dummy : array [0..95] of Byte;
end;
var
InstanceNumber : Integer;
AObject: TObject;
CriticalSection: TMCriticalSection;
function getInstance: TObject;
implementation
uses Windows;
function getInstance: TObject;
begin
//I Want somehow use InterlockedCompareExchange instead of CriticalSession, for example
if InterlockedCompareExchange(InstanceNumber, 1, 0) > 0 then
begin
Result := AObject;
end
else
begin
CriticalSection.Enter;
try
AObject := TObject.Create;
finally
CriticalSection.Leave;
end;
InterlockedIncrement(InstanceNumber);
Result := AObject
end;
end;
initialization
CriticalSection := TMCriticalSection.Create;
InstanceNumber := 0;
finalization;
CriticalSection.Free;
end.
Three Questions:
1- Is this design Thread Safe? Especially the with InterlockedExchange Part.
2- How to use the InterlockedCompareExchange? Is it possible to do what i'm trying?
3- Is this design better than involve all code within the critical section scope?
Remark:
My object is thread safe, only the construction i need to serialize!
This is not the intire code, only the important part, i mean, the getInstance function.
Edit
I NEED to use some kind of singleton object.
Is there any way to use InterlockedCompareExchange to compare if the value of InstanceNumber is zero?
And
1 - Create the object only when is 0, otherwise, return the instance.
2 - When the value is 0: Enter in the critical section. Create the object. Leave critical section.
3 - Would be better to do this way, instead of involve all code within the critical section scope?
Your design does not work and this can be seen even without any understanding of what
InterlockedCompareExchange
does. In fact, irrespective of the meaning ofInterlockedCompareExchange
, your code is broken.To see this, consider two threads arriving at the
if
statement ingetInstance
at the same time. Let's consider the three options for which branches they take:AObject
before the other thread has written it.Personally I'd use double-checked locking if I had to implement your
getInstance
function.There is a technique called "Lock-free initialization" that does what you want:
The use of
InterlockedCompareExchangePointer
erects a full memory barrier around the operation. It is possible one might be able to get away withInterlockedCompareExchangeRelease
to use release semantics (to ensure the construction of the object completes before performing the compare exchange). The problem with that is: