Reputation: 11763
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
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
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
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