[fpc-devel] Suspicion about TThread.Synchronize
Martin Frb
lazarus at mfriebe.de
Wed Feb 6 22:04:30 CET 2019
Actually from a further review of the source, there may be another issue:
TThread.Synchronize
will call
ThreadQueueAppend(AThread.FSynchronizeEntry);
AThread.FSynchronizeEntry is the re-usable entry
(which is fine, only one synchronize call can be active in any given
thread. (Unless you are in a synchronize event, and call
MyWaitingThread.synchronize from inside the event....))
ThreadQueueAppend puts the FSynchronizeEntry at the end of the queue.
It does NOT clear FSynchronizeEntry^.Next, it expects it to be nil
already. => TThread.Synchronize will reset Next to nil *after*
ThreadQueueAppend returns
ThreadQueueAppend waits for the synchronize call to finish.
But, if the sync event threw an exception, this exception is returned to
the thread.
ThreadQueueAppend checks if an exception was returned and raises it.
In case of such an exception TThread.Synchronize will never get to execute
AThread.FSynchronizeEntry^.Next := Nil;
The next time that thread calls TThread.Synchronize, the Next pointer is
dirty.
--------------------
The below code demonstrates this (cheaply using "sleep" to ensure
threads are executed in desired order....)
MySync2 Is called only once, yet the output is
x
x2
x
x2
program Project1;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads} cthreads, {$ENDIF}{$ENDIF}
Classes, sysutils;
type
MT1= class(TThread)
procedure Execute; override;
private
procedure MySync;
end;
{ MT2 }
MT2= class(TThread)
procedure Execute; override;
private
procedure MySync2;
end;
var
T1: MT1;
T2: MT2;
{ MT2 }
procedure MT2.Execute;
begin
try
Synchronize(@MySync2);
except end;
while true do sleep(100*1000);
end;
procedure MT2.MySync2;
begin
writeln('x2 ');
raise Exception.Create('Foo'); // prevent event^.Method from being
set to nil
end;
procedure MT1.Execute;
begin
try
Synchronize(@MySync);
except end;
Sleep(10*1000);
try
Synchronize(@MySync);
except end;
while true do sleep(100*1000);
end;
procedure MT1.MySync;
begin
writeln('x');
raise Exception.Create('Foo'); // prevent event^.Next from being set
to nil
end;
begin
T1 := MT1.Create(False);
Sleep(2*1000);
T2 := MT2.Create(False);
Sleep(2*1000);
CheckSynchronize(30*1000);
CheckSynchronize(30*1000);
CheckSynchronize(30*1000);
CheckSynchronize(30*1000);
readln;
end.
More information about the fpc-devel
mailing list