RSE
RSE

Reputation: 322

writing and reading strings in multithread environment

Having two threads running simultaneously can give strange behavior when writing to and reading from a variable from both threads simultaneously. It can be thread safe, but is not in every case.

Thread safe example: TThread.Terminated

The Boolean Terminated just reads FTerminated, which is set only once and since it is a Boolean, the writing process is atomic. So the value can be read in the MainThread as well as in the thread and is always thread safe to read.

My example: I have a string, which is written only once. Unlike TThread.Terminated, the writing of my string is not atomic, so the reading of it is not thread safe per se. But there may be a thread safe way in a special case: I have a situation where I just want to compare the string to another string. I only do something if they are the same (and it's not critical if they are not equal because the string is just not completely written yet). So I thought about whether this may be thread safe or not. So what happens exactly when the string is written and what may go wrong if I read the string when it's only half way written?

Steps to be done when writing a string:

  1. Reference Count = 1:
    1. Allocate additional memory, if new string is longer than old one
    2. Copy Characters
    3. Set new string length
    4. Deallocate memory, if new string is shorter than old one
  2. Reference Count > 1 (due to copy-on-write semantics a new string instance is needed):
    1. Allocate memory for new string instance
    2. Copy characters to new location and set length of the string
    3. Locate string instance pointer to new location

Under what circumstances is it safe to read the string which is written to in just this same moment?

  1. Reference Count = 1:
    1. It is only (and in this case always) safe to read if the order of steps is as listed above and reading the string right before its length is set only gives the set length back (not all the allocated bytes)
  2. Reference Count > 1:
    1. It is only (and in this case always) safe to read if the pointer to the string is set as the last step (as setting this pointer is an atomic operation) or if length is initialized to 0 before the pointer to the string is set and the conditions for the case "Reference Count = 1" apply to the new string

Question to the ones who have such deep-knowledge: Are my assumptions true? If yes, can I rely on this safely? Or is it a such bad idea to rely on this implementation specifics that it's not even worth to think about all this and just not read strings unprotectedly when they are written to in another thread?

Upvotes: 0

Views: 1922

Answers (2)

Yuriy Afanasenkov
Yuriy Afanasenkov

Reputation: 1440

Example of what could happen without any lock.

String is being written: it should become bigger than it was, so new memory is allocated. But pointer is not yet modified, it points to old string.

At the same time reading thread got a pointer and began to read old string.

Context switched again to writing thread. It changed pointer, so now it is valid. Old string got refcount 0 and was immediately freed.

Context switch again: reading thread continues to process old string, but now it is access to deallocated memory which may easily result in access violation.

Upvotes: 1

kludg
kludg

Reputation: 27493

Delphi strings are "thread-safe" only in a sense that a string's reference count is guarantied to be valid in a multithreaded code.

Copy-On-Write of Delphi strings is not a threadsafe operation; if you need a multithreaded read/write access to the same string you generally should use some synchronization, otherwise you are potentially in trouble.

Upvotes: 3

Related Questions