[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