[fpc-devel] ref count types / threadsave question
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
> 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.
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