Home > database >  InterlockedCompareExchangePointer with Delphi string
InterlockedCompareExchangePointer with Delphi string

Time:03-26

In my server code I use the following pattern for creating "just in time" objects:

function TSomeObject.GetChildObjects: TChildObjects;
var
  ChildObjects: TChildObjects;
begin
  if FChildObjects=nil then
    begin
      ChildObjects:=TChildObjects.Create;
// Fill child objects here
      if InterlockedCompareExchangePointer(Pointer(FChildObjects),ChildObjects,nil) <> nil then
        ChildObjects.Free;
    end;
  result:=FChildObjects;
end;

This works fine, but how would I do something similar with Delphi strings? e.g. if I wanted to initialise a string "just in time" in a multi threaded envrionment? Or do I have to use a critical section? e.g:

function TSomeObject.GetSomeString: string;
var
  s :string;
begin
  if FSomeString='' then
    begin
      s:='Test';
//      InterlockedCompareExchangePointer(Pointer(FSomeString),s,nil);
    end;
  result:=FSomeString;
end;

CodePudding user response:

Strings are reference counted, so it is not enough to just swap the pointer, you have to be manage the reference count, too.

function InterlockedCompareExchangeString(var VTarget: String; const AValue, Compare: String): String; inline;
var
  P: PStrRec;
begin
  if AValue <> '' then begin
    P := PStrRec(PByte(AValue) - SizeOf(StrRec));
    AtomicIncrement(P.refCnt);
  end;
  Result := String(InterlockedCompareExchangePointer(Pointer(VTarget), Pointer(AValue), Pointer(Compare)));
  if (AValue <> '') and (Pointer(Result) <> Pointer(Compare)) then begin
    P := PStrRec(PByte(AValue) - SizeOf(StrRec));
    if AtomicDecrement(P.refCnt) = 0 then
      FreeMem(P);
  end;
end;

Unfortunately, the RTL doesn't expose functions to manipulate a string's reference count, only to query it (StringRefCount()), which is why you have to access and manipulate the string's inner StrRec header manually.

  • Related