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 of InterlockedCompareExchange
, your code is broken.
To see this, consider two threads arriving at the if
statement in getInstance
at the same time. Let's consider the three options for which branches they take:
- They both choose the second branch. Then you create two instances and your code no longer implements a singleton.
- They both choose the first branch. Then you never create an instance.
- One choose the first and the other chooses the second. But since there is no lock in the first branch, the thread that takes that route can read
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:
interface
function getInstance: TObject;
implementation
var
AObject: TObject;
function getInstance: TObject;
var
newObject: TObject;
begin
if (AObject = nil) then
begin
//The object doesn't exist yet. Create one.
newObject := TObject.Create;
//It's possible another thread also created one.
//Only one of us will be able to set the AObject singleton variable
if InterlockedCompareExchangePointer(AObject, newObject, nil) <> nil then
begin
//The other beat us. Destroy our newly created object and use theirs.
newObject.Free;
end;
end;
Result := AObject;
end;
The use of InterlockedCompareExchangePointer
erects a full memory barrier around the operation. It is possible one might be able to get away with InterlockedCompareExchangeRelease
to use release semantics (to ensure the construction of the object completes before performing the compare exchange). The problem with that is:
- i'm not smart enough to know if Release semantics alone really will work (just cause a smarter person than me said they would, doesn't mean i know what he's talking about)
- you're constructing an object, the memory barrier performance hit is the least of your worries (it's the thread safety)
Note: Any code released into public domain. No attribution required.