Sometimes when you launch a thread you don’t know when it will complete whatever processing it is tasked with. Sometimes you do. Sometimes it may never complete and will require that you expressly terminate it. Usually any given thread will have a lifecycle that is at least consistently one or the other, but sometimes you will come across an awkward thread which is not so straightforward.
Sometimes you will be creating a thread on behalf of somebody else who is making these decisions.
Lifecycle Options
With Delphi threads you basically have two options when it comes to thread lifetimes, governed by the FreeOnTerminate property of the thread object.
If set TRUE, then the thread will destroy itself upon termination. If set (or left) FALSE, then it will not, and someone/something will have to free it at some point.
But what if you have a thread which you would like to free itself when it completes, but which you would like to be able to terminate at will, before it completes normally, under some circumstances (or where this decision is beyond your control) ?
If you set FreeOnTerminate to TRUE and the thread you are trying to terminate has already completed then anything you attempt using a reference to that thread is likely to cause an exception since the thread object will have already been Free‘d:
thread := TMyThread.Create(FALSE); // Time passes and things change.... if someCondition then thread.Terminate;
Even calling thread.WaitFor or testing if thread.Terminated could blow up in our face if the thread has already terminated (and has thus already Free‘d itself).
What to do ?
This was part of the same exercise that involved the use of Windows message based thread synchronisation – the threading is an entirely private implementation detail, so I had the additional problem of how to expose those parts of the thread that were appropriate whilst keeping the rest secure. i.e. a more accurate representation of the situation was:
thread := SomeFunctionWhichCreatesAThreadIMightTerminate(..); // Time passes and things change.... if someCondition then thread.Terminate;
By returning a reference to the thread itself, as well as the Terminate method, the consumer of my function has direct access to the thread and can do whatever they wanted with it, which is simply not appropriate.
I realised that I could avoid the problem with a bit of additional indirection.
Sneaking Up On The Problem
Instead of returning a reference to the worker thread itself I would return a reference to a lightweight container which held the thread reference and exposed only that functionality which I wished to provide to any potential consumers:
TThreadContainer = class private fThread: TMyThread; public procedure Terminate; end; procedure TThreadContainer.Terminate; begin fThread.Terminate; end;
This keeps the consumer at arms length from the thread but does not solve the problem of the situation where the container is potentially used to Terminate the thread after it has in fact already terminated and Free‘d itself.
But it does make a solution possible, via a simple change to the TMyThread class itself.
The function that created the worker thread would first create the required container. A reference to the container is then provided to the thread constructor which keeps a reference to it as well as setting the container.fThread member to self.
The thread class then also implements a destructor and NIL‘s the container.fThread.
constructor TMyThread.Create(...; aContainer: TThreadContainer); begin fContainer := aContainer; fContainer.fThread := self; ... FreeOnTerminate := TRUE; inherited Create(FALSE); end; destructor TMyThread.Destroy; begin fContainer.fThread := NIL; inherited Destroy; end;
Now, in the Terminate method of the container, I can first check that the thread reference is still assigned. If it is, then the thread may still be safely terminated. If it is not, then the thread has already terminated and the container Terminate method need do nothing.
However, in solving this problem a new one is created.
A potential race condition has been introduced since the thread could terminate (and free) in the interval between testing the assignment of the fThread reference and calling Terminate upon it.
This is possible because of an often overlooked aspect of using FreeOnTerminate threads: The thread object is destroyed as the last act of the thread itself. i.e. the thread class destructor is executed by the worker thread, not by the VCL thread.
The potential problem here is a far less likely occurrence than the thread terminating before being expressly terminated, but it is a potential problem and one that would be incredibly difficult to track down were it ever to occur.
A similar problem does not arise in the constructor because that is called in the context of the thread creating the new thread instance.
Race to the Finish
A critical section on the container solves this race condition situation (created and destroyed by the container itself – trivial details not shown).
procedure TThreadContainer.Terminate; begin fCS.Enter; try if Assigned(fThread) then fThread.Terminate; finally fCS.Leave; end; end; destructor TMyThread.Destroy; begin fContainer.fCS.Enter; try fContainer.fThread := NIL; finally fContainer.fCS.Leave; end; inherited Destroy; end;
Another problem solved. But there is another one: Who cleans up the container ?
The thread cannot Free it otherwise we simply re-introduce the original problem.
The consumer (holding the reference returned from the function) cannot free it either, since if it does so without terminating a still running thread the thread will throw an exception when it is destroyed.
In my particular case, the consumer may not even be concerned with terminating the thread state at all. They may not even be holding on to a reference to the container in the first place !
One further change plugs this gap: Implement the container as a class with a reference counted lifetime. An interfaced class.
Once More Into the Breach
Using an interface is even better since it means I need only expose the interface implemented by the container, further hiding the implementation details that should not concern the consumer !
Note: If you are asking yourself “Why not implement the interface and use reference counted lifetime management on the thread itself ?” you should remember that if the thread runs to the end of it’s natural life then we want it to Free itself, which will cause problems when any interface references are later Release‘d. i.e. we’re back to the very first problem all over again.
The container is where the interface will be implemented, and the interface is very simple:
type IThreadContainer = interface ['{FE327C1C-7A6F-4A9C-8FB1-B3C1845E5CAD}'] procedure Terminate; end;
The change to the container class to a reference counted, interfaced object declaring support for this interface is similarly straightforward:
TThreadContainer = class(TCOMInterfacedObject, IThreadContainer) ...
Note: TCOMInterfacedObject is my own version of TInterfacedObject which contains bug fixes over the VCL TInterfacedObject and makes the COM reference counted lifetime explicit in the name. There is a companion class which supports interfaces without reference counted lifetime..
The only minor complication is in addressing the potential situation where the consumer of my function chooses not to hold on to the container interface. In that situation there will be no references maintained to the container and it will most likely be Release‘d and thus Free‘d before the thread itself terminates, causing problems in the thread destructor.
To prevent this, as well as holding on to a reference to the container object using an object reference, the thread must retain a reference to the container interface in addition to the object reference (to access the members not exposed through the interface) and release that reference only when destroyed.
TMyThread = class(TThread) private fContainerRef: IThreadContainer; fContainer: TThreadContainer; ... end; constructor TMyThread.Create(...; aContainer: TThreadContainer); begin fContainerRef := aContainer; fContainer := aContainer; fContainer.fThread := self; ... end; destructor TMyThread.Destroy; begin fContainer.CS.Enter; try fContainer.Thread := NIL; fContainerRef := NIL; finally fContainer.CS.Leave; end; inherited; end;
If you’ve had your morning coffee you should be jumping up and down right about now, pointing at the screen and yelling at me about a glaring mistake.
Determined to Find a Solution
As written above, if the thread is holding on to the only reference to the container then at the point at which the destructor attempts to leave the critical section both that critical section and the container will have been destroyed, thanks to the deterministic nature of reference counted lifetime management.
Fortunately, that same determinism provides us with a simple solution.
Before entering the critical section the destructor need only to take an additional reference to the container, which it will release only after leaving the critical section. This reference can be a simple IUnknown (or IInterface if you prefer) since we won’t be using it to do anything, other than maintaining that additional reference:
destructor TMyThread.Destroy; var localRef: IUnknown; begin localRef := fContainer; fContainer.CS.Enter; try fContainer.Thread := NIL; fContainerRef := NIL; finally fContainer.CS.Leave; localRef := NIL; end; inherited; end;
I explicitly NIL rather than leaving the reference to drop out of scope only to make the intended use clear but also to counter any (perhaps remote) possibility that the compiler will optimise the otherwise unused localRef variable out of existence.
Note: These life-preserving additional references could be achieved without the use of additional member or local variables by using explicit calls to _AddRef and _Release at the appropriate points instead.
Bringing It All Together
In a coming-soon post it will become clear why I’ve been working on thread synchronization and thread lifetime issues. Although it is a VCL based Delphi project it is actually connected with my interest in Android development with Oxygene.
All will become clear.
Eventually. ๐
Hi very nice post about threading, I like it very much.
But still the threading always give me headaches, with all these loose threads, they should be woven in a decent textile instead of running amok like that…
I still feel like there should be a thread management structure which keeps a tab on what is going on in your app, keep a list of threads so that you can see if your thread is still alive from there and not care if it is destroyed already.
That would be something in the lines of
-TThread manager with a Thread manager singleton defined.
->Thread stub (encapsulating a thread) which would be very close to the TMythread and linking to the real thread.
-> the real TThread.
this would take care of a lot of issues and certainly create a lot of new issues.
My small list of VCL vs. threading usage ๐
– FreeOnTerminate – deprecate, it’s not safe to use
– TThread.Queue – never use it if you don’t properly handle it with CheckSynchronize (especially at destruction flow)
– Application.ProcessMessages – avoid/never try to use again
– Never hold lock while calling TThread.Synchronize
– TThread.Destroy – destroy objects (used in Execute) after ‘inherited Destroy;’ (if you don’t call WaitFor/WaitForSingleObject before destroying thread)
Why not simply use OnTerminate event of TThread?
Why the need to create new interfaces and I don’t know what else? This just makes Thread initialization time longer.
The only difference that OnTerminate would make is that it would be synchronized with the VCL thread, via the Synchronize() mechanism. The only aspect of this implementation that it would remove is the need for the critical section on the thread container, but it would not remove the need for the container itself and it is the container that demands the use of an interface to manage it’s lifetime.
The interface is to manage the lifetime of the wrapper object and that is still required to avoid exposing the thread object directly which is not addressed at all by moving the destructor code to an OnTerminate method.
Setting up that interfaced container is negligible cost. I don’t know why you think it is “expensive”. It is certainly far, far less overhead than invoking the Synchronize() mechanism to achieve what can be done with a critical section instead. ๐
If it is so wrong to expose the thread object directly why isn’t the thread object already warped into some kinda warper class or interface as you are trying to do?
And yes the OnTerminate event is processed in Main thread, but at the time it is processed the thread has already finished its work. So freing it up there won’t cause any problems.
The only problem that I see is that if you try to acces any of the thread-sลกecific variables or methods from some other part of the code and the thread has already been destroyed and freed you will get Access Violation becouse the object you are trying to access no longer exists.
And this is the same problem as you could be facing with every class.
So tell me something: Do you create warper classes and interfaces to other classes just to avoid AccessViolations?
Isn’t there a simplier way to simply check if variable that should be pointing to your thread is asigned to TThread object?
Let me get this straight… you are saying that I shouldn’t wrap my thread in a wrapper because if it needed a wrapper it should already be wrapped ?
You aren’t making sense.
The issue with using OnTerminate was not that it would cause problems, simply that it was no different than using the destructor (with appropriate, lightweight synchronization) and OnTerminate relies on the historically unreliable, massively over-engineered and – in this case – unnecessary Synchronize() mechanism. Your proposed alternative solution was just a different, less efficient way of achieving exactly the same thing. It didn’t solve anything new.
The “only problem” that you see is precisely the problem I need to avoid, and no, it is not the same problem that is faced with every class since not every class can be running for a potentially indeterminate length of time after which it will free itself because the consumer (beyond my control) cannot be relied upon to free it but which that consumer (beyond my control) may choose, need or be forced to dispose of beforehand.
It’s the “beyond my control” part that you don’t seem to grasp.
My solution works for my problem. You might have chosen to go about it differently but I’m not so certain that your results would have been as robust as you don’t appear to have properly understood the problem. ๐
Yes it is posible that I realy don’t understand the problem you are facing correctly. I would probably need to know some more information about what are you trying to do.
Anywhay since you are already making warper class for your thread why not go a step further and make this class into a full featured mutithreading manager.
I bet something like this would always come in handy.
Such threading frameworks already exist. Using them here would not be appropriate as it would a) be unnecessary since my requirements, though relatively complex, do not demand a fully featured threading manager and b) introduce a dependency on a 3rd party library that I might be OK with but which would then also be imposed on anybody choosing to use what I am working on.
I guess it would be much simpler to use omnithreadlibrary https://code.google.com/p/omnithreadlibrary with the small disadvantage of
dependency of a thirdparty solution.
If you are having to “guess” then I think it’s fair to say that you can only say that it “might” be simpler. ๐
And I believe you would guess wrong.
Using a library where 90% (more. I suspect) of that library is superfluous to the problem at hand and which would require that the library be learned before the (at most) 10% of it that may be useful can be applied is not simpler.
If may be less effort, but only if you have already used that library to solve this problem or one sufficiently similar that you can apply a readily adapted pattern. But if you do not have experience of that library then it probably isn’t less effort (or more reliable) either.
On the other hand, applying well understood and tried and tested techniques (Windows message queues and critical sections for synchronization and interfaces for lifetime management) allows for the smallest, simplest solution and is very easily applied (and understood).
imho
The best thing about OmniThread lib rary is that you don’t have to use whole library but only parts that come usefull and you can easily combine it with standard Delphi TThreads.
If you need more info don’t hesitate to ask Primoz Gabrielcic author of the mentioned library for help.
Also reading his in-making book about mutithreading could definitly come usefull.
One note thou. OmniThread library is currently limited to Windows OS only and last time I spoke to Primoz he sad that currently he has no “motivation” to make it crossplatform compatible as it would require major rewrite of the whole library as it hevily relies on Windows API.
That is why I haven’t recomended this in the first place as I have a feeling you are interested in multiple platforms.
Multiple platforms yes. Using Delphi (and therefore FireMonkey) absolutely not. ๐