[fpc-devel] TThread.Queue and TThread.Destroy

Martin fpc at mfriebe.de
Thu Jun 21 10:26:42 CEST 2018


On 21/06/2018 01:27, Martin wrote:
> fpc 3.0.4 / Linux 64bit (Fedora)
>
> What should happen if:
>
> - A Thread has queued a call with "TThread.Queue"
> - The Thread gets Terminated and Destroyed before the queued method 
> can be executed.
>   That is the thread checks for "Terminated" and exits Execute before 
> the queued method can be executed.
>
> Diving into TThread.Destroy I can see: RemoveQueuedEvents(Self);
> So I would assume that all the queued calls are removed, and will not 
> be executed.
...
>
> class procedure TThread.RemoveQueuedEvents(aThread: TThread; aMethod: 
> TThreadMethod);
> var
>   entry, tmpentry, lastentry: PThreadQueueEntry;
> begin
>   { anything to do at all? }
>   if not Assigned(aThread) or not Assigned(aMethod) then
>     Exit;
Arrgh, my fault, the above is correct, but instead a few lines further

       if Assigned(aThread) and (entry^.Thread <> aThread) then begin
         lastentry := entry;
         entry := entry^.Next;
         Continue;
       end;
       { then check for the method }
       if entry^.Method <> aMethod then begin  ///////////// <<< should 
that not also be "if assigned(aMethod) and ..." // entry^.Method will 
never be equal to nil
         lastentry := entry;
         entry := entry^.Next;
         Continue;
       end;


But even then, I made a test project and still get the call.
tested with fpc 3.0.4 on
- win 10 64 / but 32 bit fpc
- fedora 64bit



program Project1;

uses
   // cthreads,  // uncomment on Linux
   Classes, sysutils;

type
   TTestThread = class(TThread)
   protected
     procedure Execute; override;
   public
   end;

   TTest = class
   public
     procedure CallMeNot;
   end;

var
   Foo: TTestThread;
   Test: TTest;
   event: PRTLEvent;

procedure TTestThread.Execute;
begin
   Queue(@Test.CallMeNot);
   RTLeventSetEvent(event);
   while not Terminated do ;
end;

procedure TTest.CallMeNot;
begin
   writeln('shouldnt be here');
end;

begin
   event := RTLEventCreate;

   Test := TTest.Create;
   Foo := TTestThread.Create(False);
   RTLeventWaitFor(event); // make sure the event is queued

   //TThread.RemoveQueuedEvents(foo, @Test.CallMeNot); // this works
   Sleep(500);

   Foo.Terminate;
   foo.Destroy;

   TThread.RemoveQueuedEvents(foo, @Test.CallMeNot); // this does not 
remove it in most cases // foo still has the value of the pointer to 
freed mem, so it should
   sleep(500);

   CheckSynchronize();
   writeln('done');
   readln;
end.




More information about the fpc-devel mailing list