[fpc-devel] ref count types / threadsave question

Martin Frb lazarus at mfriebe.de
Sun Jan 6 16:17:22 CET 2019

On 06/01/2019 15:34, Jonas Maebe wrote:
> On 06/01/19 15:30, Martin Frb wrote:
>> Out of interest...
>> What about TRTLCriticalSection?
>> If a variable is shared between 2 threads, and only accessed within a 
>> TRTLCriticalSection?
> From the mail you replied to:
> ***
> In general: always use an RTLEvent, mutex, *critical section* or 
> similar to access shared data. Those will ensure that the necessary 
> memory barriers get inserted.
> ***
Ups, apologies.

Can't help it but to get back to my initial optimization idea.

Not necessarily to actually request it. Not sure if the savings would 
actually be that notice-able... But to see if in theory it would hold...

The idea was
- local variables of a procedure/function
- with a ref count of 1
Such a variable could in the epilogue be freed, without a "locked dec ref".
(actually not limited to the epilogue, but wherever the compiler inserts 
such a decref)

The original answer was: it could not. The thread may not see, that 
another thread has inc'ed the refcount.

However, from my previous mail, it seems that another thread can only 
increment the refcount, if it has acquired a copy of the variable within 
memory barriers. Thus the first increment to refcnt=2 would be visible.

If another thread would just access any such variable (including via 
PAnsiString) without memory barrier (i.e., without lock) then during the 
other threads fpc_ansistr_incr_ref, the first thread may free the string 
by going to ref=0. (Or the other string risks seeing the string pointing 
to data, while it already is nil in reality).

That also holds, if the original thread stores a copy into a global var, 
since the other thread must get some lock before accessing the global 
var (and thus when the refcount goes up, this is visible).

So if the other thread must go through a lock of some form, to get the 
string, then the first thread is guaranteed to see refcnt=2. (or above)

- Other threads can then add more to the refcount, which the first 
thread does not see, but that does not matter, since the first thread is 
only interested in either 1 or greater than 1.
- Other threads can set it back to 1. The first thread may not see that, 
but that does not matter, because if the first thread sees > 1, it goes 
for the safe locked-decr.
   (If the other thread has set it back to 1, then no other thread can 
increase it again, without a lock.)

Other threads can NOT set the refcnt to 0. If they do, something is 
already seriously broken. As the epilogue of the function may then see 
the none-nil pointer, despite in reality it should be nil.

So if the function goes into its epilogue, and a local variable has a 
refcount of 1, then this local var can be freed without any call to 
fpc_ansistr_decr_ref (any lock).
Because no other thread can have a reference to that variable at that time.

This holds for local vars.
Because the function (and with that the thread for that function) in 
which the variable is declared is guaranteed to own the last ref-count.

For a global var, several threads could at the same time do "s := ''; ". 
Though without lock, that would be unsafe.
If access to the global var would be properly locked at all times (see 
my last big mail) then it might hold... (But I haven't really thought 
through all possibilities.)

Then again, it should. Because fpc_ansistr_decr_ref does not protect the 
variable and the var changes to nil, which is not seen by other threads.
And the initial 2nd ref count would have to be gotten in a men-barrier 
lock too. Or one thread could free the mem, while the other increments 
the ref count.

Each actual variable (holding a ref to the string) is not protected / 
not thread save in it self. Therefore can/must be considered to be owned 
by one thread only.
Only the ref count is protected.

More information about the fpc-devel mailing list