On windows, to stop and destroy an anonymous thread, i simply do FmyTask.free => that will call destroy => and that inside the destroy will set terminate = true and call waitfor to wait the task is finished => and finally clean the memory used
but on ARC, everything is different :( i use this code :
TMyObject
private
FMyTask: TThread;
public
destructor Destroy; override;
procedure DoSomething;
end;
destructor TMyObject.Destroy;
begin
FMyTask.free; // << will do nothing because FMyTask.refcount = 2 !! how it's possible ?
FMyTask:= nil;
inherited;
end;
procedure TMyObject.DoSomething;
begin
FMyTask:= Thread.CreateAnonymousThread(
procedure
begin
sleep(10000000);
end);
FMyTask.FreeOnTerminate := False;
FMyTask.Start;
end;
and i do nothing else than just
MyObject := TmyObject.create;
MyObject.DoSomething;
MyObject.free;
MyObject := nil;
but as you see, in the onDestroy of TmyObject, FMyTask have a refcount of 2 !! so it's mean that the FMyTask will be not destroyed (refcount will be decreased to 1) with TmyObject (but later) and you imagine all the bug that can results :(
it's sean that a reference to fMyTask is keep inside this function :
function ThreadProc(const Thread: TThread): Integer;
var
FreeThread: Boolean;
{$IFDEF MACOS}
pool: Pointer;
{$ENDIF MACOS}
begin
{$IFDEF AUTOREFCOUNT}
Thread.__ObjAddRef; // this ensures the instance remains for as long as the thread is running
{$ENDIF}
TThread.FCurrentThread := Thread;
{$IF Defined(POSIX)}
if Thread.FSuspended then
pthread_mutex_lock(Thread.FCreateSuspendedMutex);
{$ENDIF POSIX}
{$IFDEF MACOS}
// Register the auto release pool
pool := objc_msgSend(objc_msgSend(objc_getClass('NSAutoreleasePool'),
sel_getUid('alloc')), sel_getUid('init'));
{$ENDIF MACOS}
try
Thread.FStarted := True;
if not Thread.Terminated then
try
Thread.Execute;
except
Thread.FFatalException := AcquireExceptionObject;
end;
finally
Result := Thread.FReturnValue;
FreeThread := Thread.FFreeOnTerminate;
Thread.DoTerminate;
Thread.FFinished := True;
SignalSyncEvent;
if FreeThread then
begin
Thread.DisposeOf;
{$IFDEF AUTOREFCOUNT}
Thread.__ObjRelease; // This will clear the thread reference that was added by setting FreeOnTerminate.
{$ENDIF}
end;
{$IFDEF AUTOREFCOUNT}
Thread.__ObjRelease; // This will clear the thread reference we added above. This may initiate disposal.
{$ENDIF}
{$IFDEF USE_LIBICU}
// Destroy Collator Cache
ClearCollatorCache;
{$ENDIF}
{$IF Defined(MSWINDOWS)}
EndThread(Result);
{$ELSEIF Defined(POSIX)}
{$IFDEF MACOS}
// Last thing to do in thread is to drain the pool
objc_msgSend(pool, sel_getUid('drain'));
{$ENDIF MACOS}
{$IFDEF ANDROID}
// Detach the NativeActivity virtual machine to ensure the proper relase of JNI context attached to the current thread
PJavaVM(System.JavaMachine)^.DetachCurrentThread(PJavaVM(System.JavaMachine));
{$ENDIF ANDROID}
// Directly call pthread_exit since EndThread will detach the thread causing
// the pthread_join in TThread.WaitFor to fail. Also, make sure the EndThreadProc
// is called just like EndThread would do. EndThreadProc should not return
// and call pthread_exit itself.
if Assigned(EndThreadProc) then
EndThreadProc(Result);
pthread_exit(Result);
{$ENDIF POSIX}
end;
end;
is it a normal behavior ? if yes, i m the only one to think that arc is the worse think of delphi ?
ARC must keep an extra reference to the thread in order not free it too soon if the creator should go out of scope.
Under non-ARC, when you call FMyThread.Free
, the Destroy
destructor is called and subsequently Terminate;
and WaitFor;
With ARC, calling Free
decrements ref count and checks for zero. Since there is still one reference left, the destructor is not called.
This means that the pattern to free a thread (with FreeOnTerminate=false
) should be:
fMyThread.Terminate;
fMyThread.WaitFor; // Wait for the thread to finish
fMyThread.Free;
This means that the extra reference taken by ARC is removed and the thread object will be released any time after the creator goes out of scope.
Note that this pattern for freeing a thread (with FreeOnTerminate=false
) works in all compilers, and also avoids rare race conditions when terminating threads in non-ARC land.
General rule for all threads that are set to FreeOnTerminate = true
is they should not be accessed from outside by reference.