For me it seems a valid destructor. Is it? Should I worry?
It is a bad destructor code.
First, everything bad you have heard about TerminateThread
is true. There is no safe way to terminate thread as it may leave the application in unstable state, and you should never use that function unless you want to immediately close the application, too. And in such cases it is better to just exit the process altogether. See: Calling TerminateThread on a Windows thread when app exits
Windows started picking up the really big pieces of TerminateThread garbage on the sidewalk, but it’s still garbage on the sidewalk
Now the history.
Originally, there was no TerminateThread function. The original
designers felt strongly that no such function should exist because
there was no safe way to terminate a thread, and there’s no point
having a function that cannot be called safely. But people screamed
that they needed the TerminateThread function, even though it wasn’t
safe, so the operating system designers caved and added the function
because people demanded it. Of course, those people who insisted that
they needed TerminateThread now regret having been given it.
It’s one of those “Be careful what you wish for” things.
Additionally, destructor raises an exception, which is something Delphi destructors should never ever do. Raising exceptions in destructor (which are not caught and handled within try..except
block) will cause irreparable memory leaks in application.
There is a better solution?
Yes. Since, Cleanup method will call fThread.Free
which will wait for thread completion and will perform normal thread shutdown there is no need to call TerminateThread
.
Instead of forcing thread termination, it would be better to Cancel
the thread and give it time to gracefully terminate itself. This also may require calling WaitFor
although pumping Windows messages at that point could interfere with other application code.
destructor TBackgroundWorker.Destroy;
begin
if IsWorking then
begin
Cancel;
// WaitFor;
Cleanup(True);
end;
inherited Destroy;
end;
Ultimately, it is not in the domain of component to handle what happens if the thread is still running during shutdown. If there is a need to handle such scenario and prevent shutdown, then this needs to be handled from outside code.
Generally, I would avoid using this component as generalized solutions can create more problems than they are worth. Waiting for a thread by pumping messages is not the greatest design. It may work well in some circumstances and not in others.
It would be better architecture to rely on TThread.WaitFor
function. However, TThread.WaitFor
is blocking call, so that behavior may not fit well into TBackgroundWorker
architecture and desired behavior.
Note: I didn't fully inspect the code of TBackgroundWorker
component so there may be other issues that are not covered in this post.