I am creating a thread, and then say want to wait for it to terminate with a WFSO call (simplified pseudo-code below, obviously no one wants to wait on a thread right after creating it).
constructor TFileScannerThread.Create(Parameters)
begin
/// assign parameters to private variables, and call
inherited Create(true); //Create Suspended
FreeOnTerminate := true;
Start;
end;
In the main thread
fst := TFileScannerThread.Create(BaseFolder, ScanMode, AbortEvent);
/// I presume, the next call would block the main thread until the child thread is done
/// but, it seems to create a deadlock & WFSO never returns
/// I get a WAIT_TIMEOUT if I use a finite wait time
WaitForsingleObject(fst.Handle, INFINITE);
What am I doing wrong/missing? If I do not have the WFSO the thread completes in about 10 seconds.
Edit: Creating with FreeOnTerminate=false does not create this issue.
Waiting on a
TThreadobject that usesFreeOnTerminate=trueis a race condition, as the object could be freed at any moment once itsExecute()method has exited. So unless you can guarantee that you are callingWaitForSingleObject()before the thread'sOnTerminateevent is fired, then you are not guaranteed to have a valid object on which to read itsHandleproperty. Basically, once the thread's constructor exits, all bets are off when usingFreeOnTerminate=true, unless you use anOnTerminateevent handler.FreeOnTerminate=trueis meant for "create and forget" kind of threads. If you need to refer to a thread for any reason, usingFreeOnTerminate=truebecomes dangerous if you are not very careful.If you are going to wait on a
TThreadobject, there is no point on usingFreeOnTerminate=trueon that object, since you can just free the object yourself once the wait is complete. Doing so ensures the object remains valid until you manually free it, thus you can use itsHandleat any time:(you don't need to call
Start()inside the constructor, useCreateSuspended=Falseinstead, the thread will not actually begin running until after the constructor exits)That being said, it is impossible for
WaitForSingleObject()to returnWAIT_TIMEOUTon anINFINITEtimeout. But it is possible for it to returnWAIT_FAILED, such as when theTThreadobject is freed beforeWaitForSingleObject()is called, or even while it is still waiting on theHandle, thus closing theHandlewhile it is being used.With regard to a deadlock, if you have an
OnTerminateevent handler assigned to theTThreadobject, that handler gets called via theTThread::Synchronize()method so that the handler runs in the main thread. But, if the main thread is blocked onWaitForSingleObject(), then it can't service anyTThread::Synchronize()(orTThread::Queue()) requests. Thus, you end up with the worker thread waiting on the main thread while the main thread is waiting on the worker thread - deadlock.To avoid that, you can call
WaitForSingleObject()in a loop with a short timeout so that you can call the RTL'sCheckSynchronize()function periodically while you are waiting:There are other issues to deal with when using a blocking wait, like
SendMessage()calls from other threads to the main thread. So you would need to service those requests as well:Alternatively, add the
Classes.SyncEventhandle to the wait as well (TThread::Synchronize()andTThread::Queue()signal it internally when there are requests pending):FYI,
TThreadhas its ownWaitFor()method that performs a blocking wait on the thread to terminate, while servicingTThread::Synchronize()/TThread::Queue()andSendMessage()requests, similar to above:Just note that calling
TThread::WaitFor()on aTThreadobject that usesFreeOnTerminate=trueis not safe, either. It will either fail with anEThreadorEOSErrorexception when theHandleis closed between internal loop iterations, or it will likely just crash outright when it tries to access theHandleof aTThreadobject that has already been freed.