The fundamental issue in this question, to my mind is:
What happens when you raise an exception in a thread's OnTerminate
event handler.
A thread's OnTerminate
event handler is invoked on the main thread, by a call to Synchronize
. Now, your OnTerminate
event handler is raising an exception. So we need to work out how that exception propagates.
If you examine the call stack in your OnTerminate
event handler you will see that it is called on the main thread from CheckSynchronize
. The code that is relevant is this:
try
SyncProc.SyncRec.FMethod; // this ultimately leads to your OnTerminate
except
SyncProc.SyncRec.FSynchronizeException := AcquireExceptionObject;
end;
So, CheckSynchronize
catches your exception and stashes it away in FSynchronizeException
. Excecution then continues, and FSynchronizeException
is later raised. And it turns out, that the stashed away exception is raised in TThread.Synchronize
. The last dying act of TThread.Synchronize
is:
if Assigned(ASyncRec.FSynchronizeException) then
raise ASyncRec.FSynchronizeException;
What this means is that your attempts to get the exception to be raised in the main thread have been thwarted by the framework which moved it back onto your thread. Now, this is something of a disaster because at the point at which raise ASyncRec.FSynchronizeException
is executed, in this scenario, there is no exception handler active. That means that the thread procedure will throw an SEH exception. And that will bring the house down.
So, my conclusion from all this is the following rule:
Never raise an exception in a thread's OnTerminate
event handler.
You will have to find a different way to surface this event in your main thread. For example, queueing a message to the main thread, for example by a call to PostMessage
.
As an aside, you don't need to implement an exception handler in your Execute
method since TThread
already does so.
The implementation of TThread
wraps the call to Execute
in an try/except block. This is in the ThreadProc
function in Classes
. The pertinent code is:
try
Thread.Execute;
except
Thread.FFatalException := AcquireExceptionObject;
end;
The OnTerminate
event handler is called after the exception has been caught and so you could perfectly well elect to re-surface it from there, although not by naively raising it as we discovered above.
Your code would then look like this:
procedure TMyThread.Execute;
begin
raise Exception.Create('Thread Exception!!');
end;
procedure TMyThread.DoOnTerminate(Sender: TObject);
begin
if Assigned(FatalException) and (FatalException is Exception) then
QueueExceptionToMainThread(Exception(FatalException).Message);
end;
And just to be clear, QueueExceptionToMainThread
is some functionality that you have to write!
OnTerminate
event handler results inHalt
being called. I think you'll need to find a different way to re-surface the exception. – OdiliaHalt
? I never found this situation. Can you explain please? – Letitialetizia