I'm guessing you assume that the method queued from the thread is part of the thread it self and not part of an other class.
i'm no expert but to my knowledge,
no not the method but the call itself is placed into a queuelist.
lets see what the code tells us.
class procedure TThread.Queue(aThread: TThread; aMethod: TThreadMethod); static;
var
queueentry: PThreadQueueEntry;
begin
{ ensure that we have a valid TThread instance }
if not Assigned(aThread) then
aThread := CurrentThread;
New(queueentry);
FillChar(queueentry^, SizeOf(TThreadQueueEntry), 0);
queueentry^.Thread := aThread;
queueentry^.Method := aMethod;
{ the queueentry is freed by CheckSynchronize (or by RemoveQueuedEvents) }
ThreadQueueAppend(queueentry);
end;
Well my pascal might not be that good, but to me it looks like that, only the method is attached to the queue. Not the call, the method to be called, is marked with the thread and is placed in the list.
When you invoke TThread.Queue and afaik the call itself will be placed into a list owned by the thread. As long as the thread exist this list is being processed like fifo. Therefor this list grows.
Well that makes no sense to me so lets look at the ThreadQueueAppend code to see what goes on.
procedure ThreadQueueAppend(aEntry: TThread.PThreadQueueEntry);
begin
{ do we really need a synchronized call? }
if GetCurrentThreadID = MainThreadID then begin
ExecuteThreadQueueEntry(aEntry);
if not Assigned(aEntry^.SyncEvent) then
Dispose(aEntry);
end else begin
System.EnterCriticalSection(ThreadQueueLock);
try
{ add the entry to the thread queue }
if Assigned(ThreadQueueTail) then begin
ThreadQueueTail^.Next := aEntry;
end else
ThreadQueueHead := aEntry;
ThreadQueueTail := aEntry;
finally
System.LeaveCriticalSection(ThreadQueueLock);
end;
{ ensure that the main thread knows that something awaits }
RtlEventSetEvent(SynchronizeTimeoutEvent);
if assigned(WakeMainThread) then
WakeMainThread(aEntry^.Thread);
{ is this a Synchronize or Queue entry? }
if Assigned(aEntry^.SyncEvent) then begin
RtlEventWaitFor(aEntry^.SyncEvent);
if Assigned(aEntry^.Exception) then
raise aEntry^.Exception;
end;
end;
end;
Well alarm bell No 1. If the thread that is queuing a method is the main thread the method is executed on the spot and never queued. That is a huge bug. The only reason one will queue a method from the main thread is to be executed on a later time, after the current process has finished and the code forces it to be executed now. Wow!! Noted but that is not todays concern, on to the problem at hand.
the code first locks access to the thread with a critical section then uses the variable ThreadQueueTail that is not declared in the procedure it self to append the queued method to what it looks like a single linked list. hmm the only way this could be thread bound is if the variables threadQueueHead and ThreadQueueTails are some how re declared for each thread the only construct that I know of that can do that is a threadvar lets find out the declaration of those variables
var
{ event executed by SychronizeInternal to wake main thread if it sleeps in
CheckSynchronize }
SynchronizeTimeoutEvent: PRtlEvent;
{ the head of the queue containing the entries to be Synchronized - Nil if the
queue is empty }
ThreadQueueHead: TThread.PThreadQueueEntry;
{ the tail of the queue containing the entries to be Synchronized - Nil if the
queue is empty }
ThreadQueueTail: TThread.PThreadQueueEntry;
{ used for serialized access to the queue }
ThreadQueueLock: TRtlCriticalSection;
{ this list holds all instances of external threads that need to be freed at
the end of the program }
ExternalThreads: TThreadList;
{ this list signals that the ExternalThreads list is cleared and thus the
thread instances don't need to remove themselves }
ExternalThreadsCleanup: Boolean = False;
{ this must be a global var, otherwise unwanted optimizations might happen in
TThread.SpinWait() }
SpinWaitDummy: LongWord;
threadvar
{ the instance of the current thread; in case of an external thread this is
Nil until TThread.GetCurrentThread was called once (the RTLs need to ensure
that threadvars are initialized with 0!) }
CurrentThreadVar: TThread;
It looks like a single list for all threads to me,l as expected.
The hidden self parameter in the call to tthread.queue refers to the thread itself. Passing nil ... well ... you saw the code posted.
So far we found out that when the queue method exits, the method queued is already ready for execution, and plays no farther role in the list or execution of the application. So the main question now is why does the list needs to know which thread added which method to the list so I looked at the checkSynchronise procedure and found nothing that uses that information. So there mast be something else that uses it and after a bit of searching I found a couple of methods that use that information and guess what they are all methods of TThread that remove methods from the queue.
I quess that is helpfull, there might be a reason you want a previously queue method not to be executed, although in its current implementation it will remove all entries of that method from the queue not only the first or the last or the one with data = X which makes it a bit limited.
As soon as you terminate the thread the calls places into the queuelist will be removed so that the responsible thread is able to terminate immediately.
Yeap you are write here is the destructor of TThread.
destructor TThread.Destroy;
begin
if not FExternalThread then begin
SysDestroy;
if FHandle <> TThreadID(0) then
CloseThread(FHandle);
end;
RemoveQueuedEvents(Self);
DoneSynchronizeEvent;
{ set CurrentThreadVar to Nil? }
inherited Destroy;
end;
Now knowing that the queue method has no role what so ever in the list after it finishes executing, knowing that the queued method might be anything from any class or worst no class, knowing that the only peace of code that checks who added the method in the queue is in TThread it self, can some one please explain to me what is this suppose to protect? Oh and since you are on it, please explain to me it how is this queue processed from a lazarus application too, I did not find any thing in the TApplication class to follow and TApplication.QueueAsyncCall has its own list which might introduce a different behavior at the end.