Vector
Vector

Reputation: 11763

What happens to a thread if FreeOnTerminate=true and an exception is thrown in OnTerminate?

Given this example, how do we ensure that the thread instance has been freed? What happens to the thread instance when Thread.FreeOnTerminate=true and you blow up in your Thread.OnTerminate event? Is the thread orphaned unless you handle the exception and free the thread explicitey in OnTerminate?

// Thread constructor

constructor TMyThread.Create(CreateSuspended: Boolean);
begin
  inherited Create(CreateSuspended);
  Self.FreeOnTerminate := True;
end;

// TMyThread OnTerminate event

procedure TMyThread.OnTerminate(Sender: TObject);
var o: TObject;     
begin
  o:=nil;
  showmessage(o.classname); // guaranteed AV
end;

Upvotes: 2

Views: 1683

Answers (3)

X-Ray
X-Ray

Reputation: 2846

setting FreeOnTerminate causes a problem if the constructor were to fail later. It's because it frees the thread once because the constructor failed and then again because of FreeOnTerminate being set.

solution: move it to the Execute method or set it at the end of the constructor where any risky activities were already safely completed...

  TMyThread = class(TThread)
  private
    m_bFreedAlready:boolean;
  protected
    procedure Execute; override;
  public
    constructor Create;
    destructor Destroy; override;
  end;

constructor TMyThread.Create;
begin
  inherited Create(false);

  FreeOnTerminate:=true;

  // something went wrong
  raise Exception.Create('Error Message');
end;

destructor TMyThread.Destroy;
begin
  if m_bFreedAlready then
    MessageBox(0, 'error', 'oops; freed a second time--soon we will have an error!', 0);

  m_bFreedAlready:=true;

  inherited;
end;

procedure TMyThread.Execute;
begin
  inherited;

end;

Using XE3.

Upvotes: 0

Rob Kennedy
Rob Kennedy

Reputation: 163357

The OnTerminate handler is executed in the main thread via Synchronize. If an exception is thrown from Synchronize, it is suppressed in the main thread and transferred (via AcquireExceptionObject) to the calling thread, where it gets raised again. In the version of ThreadProc I'm referring to (the open-source Kylix version from 2001), the exception does not get handled, so the exception propagates up into the OS, which called the thread procedure. The TThread object does not get freed.

The advice not to throw exceptions from destructors evidently applies to other kinds of cleanup routines as well.

Upvotes: 3

Ondrej Kelle
Ondrej Kelle

Reputation: 37221

You should handle all exceptions in your OnTerminate handler because an unhandled exception will cause that the instance of the thread will not be freed (see Classes.ThreadProc implementation). Just enclose the body of your handler in try..except and handle all exceptions.

But your example for 'guaranteed AV' is wrong: Free does not cause an AV if the instance is nil.

Upvotes: 6

Related Questions