2

In Delphi there is the procedure UniqueString which forces the parameter passed to it to have a reference count of 1. It is usually done to ensure that so it is safe to pass it to a different thread without messing up the reference counting. (*1)

It has always irked me that I have to assign the string to a variable first before I can call this procedure. Is there any reason why it could not be implemented as a function?

Like:

procedure TMyThread.Create(const _SomeParam: string);
begin
  FStringField := MakeUniqueString(_SomeParam);
  inherited Create(false);
end; 

Instead of:

procedure TMyThread.Create(const _SomeParam: string);
begin
  FStringField := _SomeParam;
  UniqueString(FStringField);
  inherited Create(false);
end; 

And is there any problem with writing such a function as

function MakeUniqueString(const _s: string): string;
begin
  Result := _s;
  UniqueString(Result);
end;

EDIT: *1: Yes, my claim that reference counting is not thread safe is at least outdated or may even have been wrong alltogether. You can stop telling me that.

dummzeuch
  • 10,975
  • 4
  • 51
  • 158
  • BTW, UniqueString is not needed to make that string thread-.safe. It is only needed when the string is given to anything not capable of doing the ref-counting right, like when casting it to PAnsiChar or PWideChar. The String reference counting in Delphi is thread-safe. See this article from Allen Bauer for more insight: https://blog.therealoracleatdelphi.com/2015/11/strings-with-reference-count-1-are.html – Uwe Raabe Aug 22 '19 at 09:14
  • @UweRaabe That blog post is from 2015, so it's about Delphi XE8 or 10.0. For how long has string reference counting been thread safe before that? I vividly remember running into AVs that were hard to track down because of it not being thread safe. That might have been in Delphi 7 or even earlier though. – dummzeuch Aug 22 '19 at 09:28
  • Even in Delphi 7 string reference counting is thread safe. The relevant code is in LStrAsg from system.pas, which also in Delphi 7 uses locks for that. Your problems must have had other reasons. Note: manipulating the string variables is not thread safe out of itself, but the string instances that these variables reference are. – Uwe Raabe Aug 22 '19 at 10:21
  • 2
    The classic use case for UniqueString is when calling CreateProcess whose second arg must be writeable. Without UniqueString you might end up passing a constant string and have an AV. – David Heffernan Aug 22 '19 at 10:28

1 Answers1

4

You can use

FStringField := Copy(_SomeParam, 1);

That will make a unique copy.

(Trivia: although documented as required, you can actually leave out the Count parameter and Copy will copy everything from the starting index to the end.)

Or you can make your own UniqueString:

function MakeUnique(const value: string): string; inline;
begin
  Result := value;
  UniqueString(Result);
end;

FStringField := MakeUnique(_SomeParam);

(Trivia: If you remove the inline, FStringField will actually have refcount 2 until the parent function (TMyThread.Create in your example) exits, because the compiler creates a hidden local variable that receives the result of the MakeUnique call and then assigns it to the FStringField. The string would still be unique so far as the parallel code is concerned.)

To answer the philosophical question - no, I don't think there is any big showstopper that prevents UniqueString by being implemented as a function. It just isn't.

gabr
  • 26,580
  • 9
  • 75
  • 141
  • That ref count = 2 might cause a problem if 1. Reference counting is not thread safe (Uwe/Allen Bauer says otherwise) and 2. TThread.Create already starts the thread (which it used to do in some ancient Delphi versions (Delphi 5?)). So for more recent versions of Delphi there should be no problem. – dummzeuch Aug 22 '19 at 09:33
  • 3
    Reference counting is thread safe. The assignment is not, simply because its not an atomic operation. – Stefan Glienke Aug 22 '19 at 11:06