{"id":1925,"date":"2013-10-16T18:49:54","date_gmt":"2013-10-16T06:49:54","guid":{"rendered":"https:\/\/www.deltics.co.nz\/blog\/?p=1925"},"modified":"2013-10-16T18:49:54","modified_gmt":"2013-10-16T06:49:54","slug":"vcl-threading-synchronization","status":"publish","type":"post","link":"https:\/\/www.deltics.co.nz\/blog\/posts\/1925\/","title":{"rendered":"VCL Threading &#8211; Synchronization"},"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>Although I am using <strong>Oxygene<\/strong> a lot these days, Delphi remains my tool of choice for Win32 (and x64) development, together with the VCL.<\/p>\n<p>Hence this post.<\/p>\n<p>A long time ago, in a galaxy far far away, Delphi was a Windows only development tool.  16 was the number of the bits with which it was concerned, and 1 was the number of threads in the process (though it was not called a thread).<\/p>\n<p>It was a simpler time.<\/p>\n<p>Then the bits were doubled and the number of threads did multiply, but it was important still that some things happened only on the original thread, though for a time a blinded eye could be turned since &#8211; no matter the plurality of threads &#8211; with only one piece of silicon to share, only one thread could be running at any one time.<\/p>\n<p>Yet lo, <strong>TThread.Synchronize()<\/strong> was conceived.<\/p>\n<p><!--more--><\/p>\n<h2>No More Inner Verity<\/h2>\n<p>Ok, that&#8217;s enough faux-k lore type language.  Back to proper English.  \ud83d\ude42<\/p>\n<p>As anyone that ever did multi-threading in those glory days will (or should) tell you, the <strong>Synchronize()<\/strong> mechanism had problems rights from the start, especially if you managed to get your hands on a PC with more than one <em>actual<\/em> CPU.  You quickly learned to avoid it like the proverbial.<\/p>\n<p>Those problems were eventually fixed I believe, but by then I had learned to use other techniques and it remained a relatively complex mechanism for achieving what was &#8211; on <strong>Windows<\/strong> &#8211; mostly achievable by far simpler means.  Remember, it was not a general purpose thread synchronization mechanism.  The sole purpose of <strong>Synchronize()<\/strong> was to ensure that some specified method was performed on the application main thread, sometimes referred to as the &#8220;VCL thread&#8221; because it was primarily where your VCL UI code would run.<\/p>\n<p>These days of course, the Delphi <strong>TThread<\/strong> class finds itself co-opted into a role for which it was never originally designed: a cross-platform abstraction of whatever threading capabilities exist on the various supported platforms, with a concomitant explosion in complexity of the class (in terms of trying to understand it from perusing the source at least).<\/p>\n<p>Of course, the &#8220;VCL thread&#8221; per se doesn&#8217;t exist in this new world order, since the VCL remains planted firmly in Windows soil, with <strong>TThread<\/strong> serving both masters &#8211; VCL and the other guy.  You know of whom I speak: Bit of a chimp.  With a tendency toward combustion.  \ud83d\ude09<\/p>\n<p>But back to <strong>Windows<\/strong>.<\/p>\n<p>If you are working on code that is similarly firmly planted in <strong>Windows<\/strong>, <strong>Synchronize()<\/strong> is massively over-engineered for the purpose it serves.  Especially when you consider that <strong>Windows<\/strong> already provides a reliable and efficient mechanism for passing control from one thread to another synchronously.<\/p>\n<p>That mechanism is <strong>messaging<\/strong>.<\/p>\n<h2>I Send a Message<\/h2>\n<p>Messages in <strong>Windows<\/strong> can be <strong>post<\/strong>ed (asynchronously) or <strong>send<\/strong>ed [sic] (synchronously).<\/p>\n<p>If a thread uses <code>SendMessage()<\/code> to send a message to a window created on some other thread, the synchronous behaviour is maintained by <strong>Windows<\/strong> itself, which will manage the business of suspending the calling thread, processing the message on the thread of the recipient window, and then returning the result to the calling thread.<\/p>\n<p>When you simply wish to notify the main (VCL) thread of some event, this is perfect and &#8211; with a bit of additional work &#8211; can be extended to pass whatever additional data about that event you wish.<\/p>\n<p>I have created a class which provides the basis for just such a mechanism, where-ever I may need it.  Typically this is in some library function which involves creating a worker thread but which supports some sort of VCL thread callback mechanism.<\/p>\n<p>The threading aspects of the implementation are entirely transparent to the consumer of the library function, including the message handling device.<\/p>\n<p>As an example, I shall show the key aspects of a thread implementation calling an event with a string parameter, where that event is to be handled in the VCL thread.<\/p>\n<p>The signature of the event that will be supported will be:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  type\r\n    TStringEvent = procedure(const aString: String) of object;\r\n<\/pre>\n<p>This is the only aspect of this mechanism that needs to be exposed in the interface of my library unit.  The rest is implementation detail that the consumer of that library code need not be concerned with.<\/p>\n<h2>Message Handler Implementation<\/h2>\n<p>First of all, the mechanism I implemented requires that I derive a class from a <strong>TMessageHandler<\/strong> base (which I shall cover in a later post).  This is the intermediary that will translate <strong>Windows<\/strong> messages sent from my thread(s) into the event notifications supported by those threads.<\/p>\n<p>In this case there is just one message involved.  I implement a method to receive and process that message in the VCL thread, and a corresponding method to be called by the thread wishing to send that message.<\/p>\n<p>Also, a single instance of this particular message handler will cater for all threads that might be involved.  Everything that the handler will need to translate messages into VCL events will be delivered via the messages themselves, so the declaration of the handler is very simple:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  const\r\n    MSG_OUTPUT = WM_USER;\r\n\r\n  type\r\n    TOutputHandler = class(TMessageHandler)\r\n      procedure Output(var aMessage: TMessage); message MSG_OUTPUT;\r\n      procedure SendOutput(const aThread: TMyThread; const aString: String);\r\n    end;\r\n<\/pre>\n<p>An instance of this handler must be created in the VCL thread context in order to function correctly (compliance with this is ensured via assertions in the <strong>TMessageHandler<\/strong> base class).  The simplest way to ensure this is to create the handler in unit <strong>initialization<\/strong> (and destroy it in <strong>finalization<\/strong>):<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n\r\n  var\r\n    _OutputHandler: TOutputHandler;\r\n\r\ninitialization\r\n  _OutputHandler := TOutputHandler.Create;\r\n\r\nfinalization\r\n  _OutputHandler.Free;\r\nend.\r\n<\/pre>\n<p>Remember, all of this is safely tucked away in the implementation of my unit.  <strong>_OutputHandler<\/strong> is a unit implementation variable, sometimes (incorrectly imho) called a &#8220;global&#8221;.  The leading underscore in the name is my personal convention in such cases.<\/p>\n<p>The implementation of the methods on the handler class are straightforward.  First, sending the message:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  procedure SendOutput(const aThread: TMyThread; const aString: String);\r\n  begin\r\n    SendMessage(MSG_OUTPUT, Integer(aThread), Integer(PChar(aString)));\r\n  end;\r\n<\/pre>\n<p>The protocol for this message is that a reference to the sending thread is passed in <strong>WParam<\/strong> with a pointer to the string for the event in <strong>LParam<\/strong>.<\/p>\n<p><em><strong>NOTE:<\/strong> This code was originally written for <strong>32-bit Windows<\/strong> and I have not yet gone through it to address any potential <strong>64-bit<\/strong> issues deriving from the packaging of pointers in message <strong>WParam<\/strong> and <strong>LParam<\/strong>.  There may not be any or there may be plenty.  I simply haven&#8217;t needed to consider it yet<\/em><\/p>\n<p>These params are then unpacked and used to invoke the target event handler in the message processing method:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  procedure TOutputHandler.Output(var aMessage: TMessage); \r\n  var\r\n    thread: TMyThread;\r\n    event: TStringEvent;\r\n    ptr: PChar;\r\n    str: String;\r\n  begin\r\n    thread  := TMyThread(aMessage.WParam);\r\n    event   := thread.OutputEvent;\r\n\r\n    ptr := PChar(aMessage.LParam);\r\n    str := ptr;\r\n\r\n    event(str);\r\n  end;\r\n<\/pre>\n<p>The steps here are laid out plainly so that each step can be followed clearly.<\/p>\n<p>Each thread that potentially uses this handler will have an event handler that it wishes to be used.  A reference to this event handler is maintained on the thread itself so the first thing that the message handler must do is extract the thread reference from the <strong>WParam<\/strong>, and from that obtain the target event handler.<\/p>\n<p>Next, the string to be sent with the event is passed in the <strong>LParam<\/strong>.  Or rather, a pointer to the string.  So <strong>LParam<\/strong> is first cast as <strong>PChar<\/strong> and then assigned to a local <strong>String<\/strong> variable.<\/p>\n<p>With the target <strong>event<\/strong> and <strong>str<\/strong>ing in hand, the message handler can then simply call the event, passing that string.<\/p>\n<p><strong>All of this occurs in the context of the VCL thread<\/strong>.<\/p>\n<p>For an instance of <strong>TMyThread<\/strong> to send a string event to the assigned event handler all it has to do is call the <strong>SendOutput()<\/strong> method of the <strong>_OutputHandler<\/strong> unit variable:<\/p>\n<pre class=\"brush: delphi; title: ; notranslate\" title=\"\">\r\n  procedure TMyThread.Execute;\r\n  var\r\n    s: String;\r\n  begin\r\n    while NOT Terminated do\r\n    begin\r\n      \/\/ ...\r\n\r\n      _OutputHandler.SendOutput( self, s );\r\n\r\n      \/\/ ...\r\n    end;  \r\n  end;\r\n<\/pre>\n<h2>Less Is More<\/h2>\n<p>This is obviously not a general purpose synchronization mechanism and it is &#8220;more complicated&#8221; than simply using <strong>Synchronize()<\/strong> in the sense that it involves implementing a specific message handler class (which is, in essence, a message marshalling class).<\/p>\n<p>But the mechanism that underpins your custom handlers is very simple.  The <strong>TMessageHandler<\/strong> class consists of very little code, most of which simply ensures correct usage rather than &#8220;doing&#8221; anything itself.  The behaviour of your callbacks is also very easily understood &#8211; callbacks are scheduled for processing just like any other message.<\/p>\n<p>They arrive on and are dispatched from your application message queue just like every other (Windows message based) event that your application has to deal with.<\/p>\n<p>This does create a marked difference between this mechanism and <code>Synchronize()<\/code>.<\/p>\n<p>With <code>TThread.Synchronize()<\/code> exceptions raised by\/escaping from the synchronized method will propagate back to the calling thread and (usually) must be handled by the thread.<\/p>\n<p>Exceptions raised during the execution of event handlers invoked by my message handler mechanism propagate not to the calling thread but to the <em>application message loop<\/em>.  Unless you handle them in the event handler itself, they will be deemed &#8220;unhandled&#8221; and your application default exception handler will be invoked.<\/p>\n<p>However, I regard this difference as another <em>advantage<\/em> of the message based mechanism.<\/p>\n<p>It makes implementing a thread-callback event no different than, say, implementing a <strong>Button.OnClick()<\/strong> event on a form and more often than not, this is exactly as it should be.<\/p>\n<h3>Coming Next&#8230;<\/h3>\n<p>Next time I shall look at another threading technique, this time involving asynchronous processes of indeterminate lifetime which may expire naturally or require express termination.<\/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> 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 bits with which it was [&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,194,243,16,93,14],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1TKYv-v3","jetpack_sharing_enabled":true,"jetpack-related-posts":[{"id":1930,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1930\/","url_meta":{"origin":1925,"position":0},"title":"VCL Threading &#8211; Indeterminate Lifetimes","date":"18 Oct 2013","format":false,"excerpt":"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\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":1925,"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":807,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/807\/","url_meta":{"origin":1925,"position":2},"title":"Use Knowledge of Your Own Threads to Extract Optimal Performance&#8230;","date":"29 Sep 2011","format":false,"excerpt":"\"The Delphi Geek\" recently blogged about a performance bottleneck he had identified in FastMM when used with a particular conditional define. Although not directly related, his post reminded me of an experience I had many years ago, working on a highly complex multi-threaded system (long before FastMM) and the strategy\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1297,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/1297\/","url_meta":{"origin":1925,"position":3},"title":"Of Threads, Stacks and RAM &#8211; Part 1","date":"28 Nov 2012","format":false,"excerpt":"Roberto Schneiders recently drew my attention to the first post on his new blog (which I can recommend as a good read :) ), presenting the results of some performance testing of DataSnap that he had been involved with which proved to be very interesting (if initially somewhat disappointing). But\u2026","rel":"","context":"In &quot;Delphi&quot;","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.deltics.co.nz\/blog\/wp-content\/uploads\/Screen-Shot-2012-11-28-at-20.24.21-.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":576,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/576\/","url_meta":{"origin":1925,"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":2118,"url":"https:\/\/www.deltics.co.nz\/blog\/posts\/2118\/","url_meta":{"origin":1925,"position":5},"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":[]}],"_links":{"self":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1925"}],"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=1925"}],"version-history":[{"count":7,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1925\/revisions"}],"predecessor-version":[{"id":1936,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/posts\/1925\/revisions\/1936"}],"wp:attachment":[{"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/media?parent=1925"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/categories?post=1925"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.deltics.co.nz\/blog\/wp-json\/wp\/v2\/tags?post=1925"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}