zeus
zeus

Reputation: 13345

How to catch unhandled exception that are raised in background thread?

I have for habit to execute anonymous thread like :

TThread.CreateAnonymousThread(
   procedure
   begin
     .....
   end).start;

But the problem is that if some unhandled exception will raise during the execution, then i will be not warned about it! For the main thread we have Application.OnException. Do we have something similar for background thread ?

Upvotes: 6

Views: 1637

Answers (4)

zeus
zeus

Reputation: 13345

Ok, finally I got the best answer :

Assign the global ExceptionAcquired procedure to our own implementation (it is nil by default). This procedure gets called for unhandled exceptions that happen in other threads than the main thread.

ExceptionAcquired := MyGlobalExceptionAcquiredHandler; 

Upvotes: 1

zeus
zeus

Reputation: 13345

The answer of J... and Remy Lebeau are good with what delphi offer, but i need a little more and I finally decide to modify a little the unit System.Classes

var
  ApplicationHandleThreadException: procedure (Sender: TObject; E: Exception) of object = nil;

function ThreadProc(const Thread: TThread): Integer;
  ...
  try
    Thread.Execute;
  except
    Thread.FFatalException := AcquireExceptionObject;
    if assigned(ApplicationHandleThreadException) and
       assigned(Thread.FFatalException) and
       (Thread.FFatalException is Exception) and
       (not (Thread.FFatalException is EAbort)) then
      ApplicationHandleThreadException(Thread, Exception(Thread.FFatalException));
  end; 

in this way you just need to assign ApplicationHandleThreadException to handle unhandled exception raise in any TThread. You don't need to be worry about the multi thread because global var like ExceptAddr are declared as threadvar so everything work fine, even to retrieve the stack trace !

https://quality.embarcadero.com/browse/RSP-21269

Upvotes: -1

Remy Lebeau
Remy Lebeau

Reputation: 595632

TThread has a public FatalException property:

If the Execute method raises an exception that is not caught and handled within that method, the thread terminates and sets FatalException to the exception object for that exception. Applications can check FatalException from an OnTerminate event handler to determine whether the thread terminated due to an exception.

For example:

procedure TMyForm.DoSomething;
begin
  ...
  thread := TThread.CreateAnonymousThread(...);
  thread.OnTerminate := ThreadTerminated;
  thread.Start;
  ...
end;

procedure TMyForm.ThreadTerminated(Sender: TObject);
begin
  if TThread(Sender).FatalException <> nil then
  begin
   ...
  end;
end;

Upvotes: 10

J...
J...

Reputation: 31393

N̶o̶,̶ ̶t̶h̶e̶r̶e̶ ̶i̶s̶ ̶n̶o̶t̶h̶i̶n̶g̶ ̶s̶i̶m̶i̶l̶a̶r̶ ̶f̶o̶r̶ ̶a̶ ̶b̶a̶c̶k̶g̶r̶o̶u̶n̶d̶ ̶t̶h̶r̶e̶a̶d̶.̶ (Thanks, Remy!)

The best solution is to always be absolutely certain to never let an exception escape from a thread. Ideally, your thread procedures should look something like this :

TThread.CreateAnonymousThread(
   procedure
   begin
     try
       { your code}
     except
       {on E : Exception do}
       {... handle it!}            
     end;
   end).start;

Upvotes: 6

Related Questions