{"id":1930,"date":"2013-10-18T16:23:44","date_gmt":"2013-10-18T04:23:44","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=1930"},"modified":"2013-10-18T16:23:44","modified_gmt":"2013-10-18T04:23:44","slug":"vcl-threading-indeterminate-lifetimes","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/1930\/","title":{"rendered":"VCL Threading &#8211; Indeterminate Lifetimes"},"content":{"rendered":"<span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">6<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span><p>Sometimes when you launch a thread you don&#8217;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.<\/p>\n<p>Sometimes you will be creating a thread on behalf of somebody else who is making these decisions.<\/p>\n<p><!--more--><\/p>\n<h2>Lifecycle Options<\/h2>\n<p>With Delphi threads you basically have two options when it comes to thread lifetimes, governed by the <strong>FreeOnTerminate<\/strong> property of the thread object.<\/p>\n<p>If set <strong>TRUE<\/strong>, then the thread will destroy itself upon termination.  If set (or left) <strong>FALSE<\/strong>, then it will not, and someone\/something will have to free it at some point.<\/p>\n<p>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) ?<\/p>\n<p>If you set <strong>FreeOnTerminate<\/strong> to <strong>TRUE<\/strong> 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 <strong>Free<\/strong>&#8216;d:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  thread := TMyThread.Create(FALSE);\r\n\r\n  \/\/ Time passes and things change....\r\n\r\n  if someCondition then\r\n    thread.Terminate;\r\n<\/pre>\n<p>Even calling <strong>thread.WaitFor<\/strong> or testing if <strong>thread.Terminated<\/strong> could blow up in our face if the thread has already terminated (and has thus already <strong>Free<\/strong>&#8216;d itself).<\/p>\n<p>What to do ?<\/p>\n<p>This was part of the same exercise that involved the use of <a href=\"https:\/\/www.deltics.co.nz\/blog\/posts\/1925\">Windows message based thread synchronisation<\/a> &#8211; 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:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  thread := SomeFunctionWhichCreatesAThreadIMightTerminate(..);\r\n\r\n  \/\/ Time passes and things change....\r\n\r\n  if someCondition then\r\n    thread.Terminate;\r\n<\/pre>\n<p>By returning a reference to the thread itself, as well as the <strong>Terminate<\/strong> 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.<\/p>\n<p>I realised that I could avoid the problem with a bit of additional indirection.<\/p>\n<h2>Sneaking Up On The Problem<\/h2>\n<p>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:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  TThreadContainer = class\r\n  private\r\n    fThread: TMyThread;\r\n  public\r\n    procedure Terminate;\r\n  end;\r\n\r\n  procedure TThreadContainer.Terminate;\r\n  begin\r\n    fThread.Terminate;\r\n  end;\r\n<\/pre>\n<p>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 <strong>Terminate<\/strong> the thread after it has in fact already terminated and <strong>Free<\/strong>&#8216;d itself.<\/p>\n<p>But it does make a solution possible, via a simple change to the <strong>TMyThread<\/strong> class itself.<\/p>\n<p>The function that created the worker thread would first create the required container.  A reference to the container is then provided to the thread <strong>constructor<\/strong> which keeps a reference to it as well as setting the <strong>container.fThread<\/strong> member to <strong>self<\/strong>.<\/p>\n<p>The thread class then also implements a <strong>destructor<\/strong> and <strong>NIL<\/strong>&#8216;s the <strong>container.fThread<\/strong>.<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n   constructor TMyThread.Create(...; aContainer: TThreadContainer);\r\n   begin\r\n     fContainer := aContainer;\r\n     fContainer.fThread := self;\r\n\r\n     ...\r\n\r\n     FreeOnTerminate := TRUE;\r\n\r\n     inherited Create(FALSE);\r\n   end;\r\n\r\n   destructor TMyThread.Destroy;\r\n   begin\r\n     fContainer.fThread := NIL;\r\n\r\n     inherited Destroy;\r\n   end;\r\n<\/pre>\n<p>Now, in the <strong>Terminate<\/strong> 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 <strong>Terminate<\/strong> method need do nothing.<\/p>\n<p>However, in solving this problem a new one is created.<\/p>\n<p>A potential race condition has been introduced since the thread could terminate (and free) in the interval between testing the assignment of the <strong>fThread<\/strong> reference and calling <strong>Terminate<\/strong> upon it.<\/p>\n<p>This is possible because of an often overlooked aspect of using <strong>FreeOnTerminate<\/strong> threads:  The thread object is destroyed as the last act of the thread itself.  i.e. <em>the thread class destructor is executed by the worker thread, <strong>not<\/strong> by the VCL thread<\/em>.<\/p>\n<p>The potential problem here is a far less likely occurrence than the thread terminating before being expressly terminated, but it <em>is<\/em> a potential problem and one that would be incredibly difficult to track down were it ever to occur.<\/p>\n<p>A similar problem does not arise in the <strong>constructor<\/strong> because that <em>is<\/em> called in the context of the thread creating the new thread instance.<\/p>\n<h2>Race to the Finish<\/h2>\n<p>A critical section on the container solves this race condition situation (created and destroyed by the container itself &#8211; trivial details not shown).<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  procedure TThreadContainer.Terminate;\r\n  begin\r\n    fCS.Enter;\r\n    try\r\n      if Assigned(fThread) then\r\n        fThread.Terminate;\r\n\r\n    finally\r\n      fCS.Leave;\r\n    end;\r\n  end;\r\n\r\n  destructor TMyThread.Destroy;\r\n  begin\r\n    fContainer.fCS.Enter;\r\n    try\r\n      fContainer.fThread := NIL;\r\n\r\n    finally\r\n      fContainer.fCS.Leave;\r\n    end;\r\n\r\n    inherited Destroy;\r\n  end;\r\n<\/pre>\n<p>Another problem solved.  But there is another one:  <em>Who cleans up the container ?<\/em><\/p>\n<p>The thread cannot <strong>Free<\/strong> it otherwise we simply re-introduce the original problem.<\/p>\n<p>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.<\/p>\n<p>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 !<\/p>\n<p>One further change plugs this gap:  <em>Implement the container as a class with a reference counted lifetime<\/em>.  An <strong>interface<\/strong>d class.<\/p>\n<h2>Once More Into the Breach<\/h2>\n<p>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 !<\/p>\n<p><em><strong>Note:<\/strong> If you are asking yourself &#8220;Why not implement the interface and use reference counted lifetime management on the thread itself ?&#8221; you should remember that if the thread runs to the end of it&#8217;s natural life then we want it to <strong>Free<\/strong> itself, which will cause problems when any interface references are later <strong>Release<\/strong>&#8216;d.  i.e. we&#8217;re back to the very first problem all over again.<\/em><\/p>\n<p>The container is where the interface will be implemented, and the interface is very simple:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  type\r\n    IThreadContainer = interface\r\n    ['{FE327C1C-7A6F-4A9C-8FB1-B3C1845E5CAD}']\r\n      procedure Terminate;\r\n    end;\r\n<\/pre>\n<p>The change to the container class to a reference counted, interfaced object declaring support for this interface is similarly straightforward:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n    TThreadContainer = class(TCOMInterfacedObject, IThreadContainer)\r\n      ...\r\n<\/pre>\n<p><em><strong>Note:<\/strong> <strong>TCOMInterfacedObject<\/strong> is my own version of <strong>TInterfacedObject<\/strong> which contains bug fixes over the VCL <strong>TInterfacedObject<\/strong> and makes the COM reference counted lifetime explicit in the name.  There is a companion class which supports interfaces without reference counted lifetime.<\/em>.<\/p>\n<p>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 <strong>Release<\/strong>&#8216;d and thus <strong>Free<\/strong>&#8216;d before the thread itself terminates, causing problems in the thread destructor.<\/p>\n<p>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 <em>in addition<\/em> to the object reference (to access the members not exposed through the interface) and release that reference only when destroyed.<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n\r\n  TMyThread = class(TThread)\r\n  private\r\n    fContainerRef: IThreadContainer;\r\n    fContainer: TThreadContainer;\r\n    ...\r\n  end;\r\n \r\n\r\n  constructor TMyThread.Create(...; aContainer: TThreadContainer);\r\n  begin\r\n    fContainerRef := aContainer;\r\n    fContainer := aContainer;\r\n    fContainer.fThread := self;\r\n    ...\r\n  end;\r\n    \r\n\r\n  destructor TMyThread.Destroy;\r\n  begin\r\n    fContainer.CS.Enter;\r\n    try\r\n      fContainer.Thread := NIL;\r\n      fContainerRef     := NIL;\r\n\r\n    finally\r\n      fContainer.CS.Leave;\r\n    end;\r\n\r\n    inherited;\r\n  end;\r\n<\/pre>\n<p>If you&#8217;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.<\/p>\n<h2>Determined to Find a Solution<\/h2>\n<p>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.<\/p>\n<p>Fortunately, that same determinism provides us with a simple solution.<\/p>\n<p>Before entering the critical section the <strong>destructor<\/strong> need only to take an <em>additional<\/em> reference to the container, which it will release only <em>after<\/em> leaving the critical section.  This reference can be a simple <strong>IUnknown<\/strong> (or <strong>IInterface<\/strong> if you prefer) since we won&#8217;t be using it to do anything, other than maintaining that additional reference:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  destructor TMyThread.Destroy;\r\n  var\r\n    localRef: IUnknown;\r\n  begin\r\n    localRef := fContainer;\r\n    fContainer.CS.Enter;\r\n    try\r\n      fContainer.Thread := NIL;\r\n      fContainerRef     := NIL;\r\n\r\n    finally\r\n      fContainer.CS.Leave;\r\n      localRef := NIL;\r\n    end;\r\n\r\n    inherited;\r\n  end;\r\n<\/pre>\n<p>I explicitly <strong>NIL<\/strong> 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.<\/p>\n<p><strong>Note: <\/strong><em>These life-preserving additional references could be achieved without the use of additional member or local variables by using explicit calls to <strong>_AddRef<\/strong> and <strong>_Release<\/strong> at the appropriate points instead.<\/em><\/p>\n<h3>Bringing It All Together<\/h3>\n<p>In a coming-soon post it will become clear why I&#8217;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.<\/p>\n<p>All will become clear.<\/p>\n<p>Eventually.  \ud83d\ude42<\/p>\n","protected":false},"excerpt":{"rendered":"<p><span class=\"rt-reading-time\" style=\"display: block;\"><span class=\"rt-label rt-prefix\">[Estimated Reading Time: <\/span> <span class=\"rt-time\">6<\/span> <span class=\"rt-label rt-postfix\">minutes]<\/span><\/span> Sometimes when you launch a thread you don&#8217;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 [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[4],"tags":[292,29,194,16,14],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-v8","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":1925,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1925\/","url_meta":{"origin":1930,"position":0},"title":"VCL Threading &#8211; Synchronization","date":"16 Oct 2013","format":false,"excerpt":"Although I am using Oxygene a lot these days, Delphi remains my tool of choice for Win32 (and x64) development, together with the VCL. Hence this post. A long time ago, in a galaxy far far away, Delphi was a Windows only development tool. 16 was the number of the\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1330,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1330\/","url_meta":{"origin":1930,"position":1},"title":"Of Threads, Stacks and RAM &#8211; Part 2","date":"29 Nov 2012","format":false,"excerpt":"In the previous post in this series, we saw that the number of threads that a given process could support was determined by a number of factors, of which the stack size reserved for each thread was key. We also saw how we could change the stack size used by\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2118,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2118\/","url_meta":{"origin":1930,"position":2},"title":"Creation Stories","date":"07 Nov 2013","format":false,"excerpt":"In the beginning there was The Word a HWND. But not all gifts of creation can be relied upon, as I just learned. Or rather, remembered. As regular readers will know, I am currently polishing up my Smoketest framework for release (don't worry, this post won't take long and then\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Very-Pretty.gif?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":263,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/263\/","url_meta":{"origin":1930,"position":3},"title":"Exchange() &#8211; Epilogue and Smoketest","date":"28 Aug 2008","format":false,"excerpt":"A colleague of mine directed me to a further minor refinement of the 'final' Exchange() code I posted the other day. The change is minor but yields a worthwhile performance improvement, but my main reason for bothering to post (yet!) another update is an excuse to introduce the testing framework\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/exchange_after-295x300.gif?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":576,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/576\/","url_meta":{"origin":1930,"position":4},"title":"Commitment Issues","date":"06 Oct 2009","format":false,"excerpt":"No, not a relationship blog and no, not a rant about the relationship between Embarcadero and the Delphi community. \u00a0This is a strictly and purely technical post about what \"Committed\" means in terms of Windows memory, and in particular a key aspect of how that applies to threaded applications. Last\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":48,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/48\/","url_meta":{"origin":1930,"position":5},"title":"Anonymous Methods &#8211; When Should They Be Used?","date":"04 Aug 2008","format":false,"excerpt":"Explores the relevance of authorities supposed to be making a good case for anonymous methods","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1930"}],"collection":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/comments?post=1930"}],"version-history":[{"count":8,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1930\/revisions"}],"predecessor-version":[{"id":1964,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1930\/revisions\/1964"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=1930"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=1930"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=1930"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}