{"id":512,"date":"2014-02-22T11:19:28","date_gmt":"2014-02-21T22:19:28","guid":{"rendered":"http:\/\/www.paradicesoftware.com\/blog\/?p=512"},"modified":"2014-02-22T11:23:44","modified_gmt":"2014-02-21T22:23:44","slug":"puzzle-fpcs-teventobject-in-windows","status":"publish","type":"post","link":"http:\/\/www.paradicesoftware.com\/blog\/2014\/02\/puzzle-fpcs-teventobject-in-windows\/","title":{"rendered":"Puzzle: FPC&#8217;s TEventObject, in Windows"},"content":{"rendered":"<p>This post is kind of an open question. I&#8217;m hoping that I can get some comments, any comments, on it \ud83d\ude42<\/p>\n<p>Let&#8217;s set up a situation with a problem, and look at different solutions for addressing the problem. Which one do you think is best, and more importantly, why?<\/p>\n<h3>The Setup<\/h3>\n<p>There is a multithreaded application, which offloads work items to a worker thread, and uses <em>TEventObject.SetEvent<\/em> call to signal to the main thread &#8220;hey, all done&#8221;. Note: the worker thread doesn&#8217;t just complete this one task and then exit; it instead sleeps until another work item is generated.<\/p>\n<h3>The Problem<\/h3>\n<p>We want the main thread to wait for\u00a0either\u00a0a Windows message to be received, OR for the worker thread&#8217;s event to be signalled. Win32 provides a handy function for doing exactly this: <a title=\"MSDN - MsgWaitForMultipleObjects\" href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/ms684242(v=vs.85).aspx\" target=\"_blank\"><em>MsgWaitForMultipleObjects<\/em><\/a> with the event object&#8217;s handle and with <em>QS_ALLINPUT<\/em> as the wakemask.<\/p>\n<p>However, FPC&#8217;s implementation of\u00a0<em>TEventObject<\/em> doesn&#8217;t expose the OS handle, so we can&#8217;t call Win32 functions on it. (<em><a href=\"http:\/\/www.freepascal.org\/docs-html\/fcl\/syncobjs\/teventobject.html\">TEventObject<\/a><\/em>\u00a0inherits from<em> THandleObject<\/em>, whose reason for existance is encapsulating an operating system handle, however it&#8217;s actual &#8220;Handle&#8221; property is a pointer and the documentation explicitly states\u00a0<em>TEventHandle i<\/em>s an opaque type and should not be used in user code).<\/p>\n<p>From looking at the actual implementation of <em>TEventObject<\/em>, you can determine that the Windows Handle needed is the very first member of the structure pointed to by the &#8220;Handle&#8221; property, however.<\/p>\n<p><em>TEventObject.WaitFor()<\/em> is clearly the intended usage, however that would block on that event only, and doesn&#8217;t wake up if window messages are received, therefore doesn&#8217;t address the situation.<\/p>\n<p>(Note: it makes perfect sense why\u00a0<em>TEventObject<\/em> is abstracting the Windows handle; otherwise it would be too easy to break cross-platform source compatibility, which is something FPC tries really hard to maintain. However, in this scenario the project is locked to Win32 for a number of reasons, so we should take advantage of it&#8217;s capability to wait for both events and messages without polling if we can.)<\/p>\n<h3>The Options<\/h3>\n<p>Here are the options, as I see it (I may have missed some!)<\/p>\n<ol>\n<li><strong>Reinvent:<\/strong> Reimplement the whole <em>TEventObject<\/em> functionality, which will mostly be line-for-line identical except that the windows Handle will be an accessible property instead of hidden behind an opaque pointer type.<\/li>\n<li><strong>Typecast:<\/strong> Apply the typecast of <em>HANDLE(TEventObject.Handle^)<\/em> to grab the windows handle out of the opaque type, disable the warning, and just hope the implementation behind the opaque type doesn&#8217;t change in a future version of FPC.<\/li>\n<li><strong>Monitor:<\/strong> &#8211; Create a third thread, whose only purpose in life is to call <em>TEventObject.WaitFor<\/em>. Then back in the main thread, call <em>MsgWaitForMultipleObjects<\/em> on the third\u00a0thread&#8217;s <em>TThread.Handle<\/em> (since it&#8217;s implementation is different: the<em> TThread.Handle<\/em> property\u00a0IS a directly usable Windows handle without a typecast.<\/li>\n<li><strong>Poll:<\/strong> Call <em>TEventObject.WaitFor<\/em> with a small timeout of say 10 millseconds, and pump window messages between waits.<\/li>\n<\/ol>\n<h3>Discussion Please!<\/h3>\n<p>In my view,:<\/p>\n<ol>\n<li><strong>Reinvent<\/strong> is the safest, but also doesn&#8217;t feel great; turning our backs on FPC&#8217;s otherwise simple and elegant synchronization objects.<\/li>\n<li><strong>Typecast<\/strong> is the easiest and results in exactly the behaviour that we want, but &#8220;it&#8217;s not guaranteed safe forever&#8221;.<\/li>\n<li><strong>Monitor<\/strong> is safe, but burning an entire extra thread seems like overkill, and maybe worse than polling in #4.<\/li>\n<li><strong>Poll<\/strong> works, but eats a lot of unnecessary context switches if the application is just idling and there&#8217;s no work going on. (A Sleep(10) in an idle loop consumes about 1% of an entire logical processor just in context switches)<\/li>\n<\/ol>\n<p>Would love to hear your thoughts about how you would approach this. Remember the problem isn&#8217;t that the problem can&#8217;t be resolved (there are at least 4 solutions above, and maybe more I&#8217;ve missed), the problem is picking the &#8216;best&#8217; solution, so your thoughts about\u00a0<strong>why<\/strong>\u00a0you would recommend a particular approach are the most valuable.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post is kind of an open question. I&#8217;m hoping that I can get some comments, any comments, on it \ud83d\ude42 Let&#8217;s set up a situation with a problem, and look at different solutions for addressing the problem. Which one do you think is best, and more importantly, why? The Setup There is a multithreaded<\/p>\n","protected":false},"author":2722,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,70],"tags":[35,60,71,72,57,32],"class_list":["post-512","post","type-post","status-publish","format-standard","hentry","category-code","category-puzzle","tag-fpc","tag-msgwaitformultipleobjects","tag-puzzle-2","tag-teventobject","tag-threads","tag-win32"],"_links":{"self":[{"href":"http:\/\/www.paradicesoftware.com\/blog\/wp-json\/wp\/v2\/posts\/512","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.paradicesoftware.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.paradicesoftware.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.paradicesoftware.com\/blog\/wp-json\/wp\/v2\/users\/2722"}],"replies":[{"embeddable":true,"href":"http:\/\/www.paradicesoftware.com\/blog\/wp-json\/wp\/v2\/comments?post=512"}],"version-history":[{"count":5,"href":"http:\/\/www.paradicesoftware.com\/blog\/wp-json\/wp\/v2\/posts\/512\/revisions"}],"predecessor-version":[{"id":517,"href":"http:\/\/www.paradicesoftware.com\/blog\/wp-json\/wp\/v2\/posts\/512\/revisions\/517"}],"wp:attachment":[{"href":"http:\/\/www.paradicesoftware.com\/blog\/wp-json\/wp\/v2\/media?parent=512"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.paradicesoftware.com\/blog\/wp-json\/wp\/v2\/categories?post=512"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.paradicesoftware.com\/blog\/wp-json\/wp\/v2\/tags?post=512"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}