{"id":36803,"date":"2005-01-05T07:01:00","date_gmt":"2005-01-05T07:01:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2005\/01\/05\/pulseevent-is-fundamentally-flawed\/"},"modified":"2005-01-05T07:01:00","modified_gmt":"2005-01-05T07:01:00","slug":"pulseevent-is-fundamentally-flawed","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050105-00\/?p=36803","title":{"rendered":"PulseEvent is fundamentally flawed"},"content":{"rendered":"<p>\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/dllproc\/base\/pulseevent.asp\">\nThe <code>PulseEvent<\/code> function<\/a>\nreleases one thread (or all threads, if manual-reset)\nwhich is\/are waiting for the pulsed event, then returns the\nevent to the unset state.\nIf no threads happen to be waiting, then the event goes to\nthe unset state without anything happening.\n<\/p>\n<p>\nAnd there&#8217;s the flaw.\n<\/p>\n<p>\nHow do you know whether the thread that you think is waiting on\nthe event really is?\nSurely you can&#8217;t use something like\n<\/p>\n<pre>\nSignalSemaphore(hOtherSemaphore);\nWaitForSingleObject(hEvent, INFINITE);\n<\/pre>\n<p>\nbecause there is a race between the signal and the wait.\nThe thread that the semaphore is alerting might complete\nall its work and pulse the event before you get around to\nwaiting for it.\n<\/p>\n<p>\nYou can try using\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/en-us\/dllproc\/base\/signalobjectandwait.asp\">\nthe <code>SignalObjectAndWait<\/code> function<\/a>,\nwhich combines the signal and wait into a single operation.\nBut even then, you can&#8217;t be sure that the thread is waiting\nfor the event at the moment of the pulse.\n<\/p>\n<p>\nWhile the thread is sitting waiting for the event,\na device driver or part of the kernel itself\nmight ask to borrow the thread to do some\nprocessing (by means of a &#8220;kernel-mode APC&#8221;).\nDuring that time, the thread is <strong>not<\/strong> in the\nwait state.\n(It&#8217;s being used by the device driver.)\nIf the <code>PulseEvent<\/code> happens while the thread\nis being &#8220;borrowed&#8221;, then it will <strong>not<\/strong>\nbe woken from the wait, because the <code>PulseEvent<\/code>\nfunction wakes only threads that were waiting\n<strong>at the time the <code>PulseEvent<\/code> occurs<\/strong>.\n<\/p>\n<p>\nNot only are you (as a user-mode program) unable to prevent\nkernel mode from doing this to your thread,\nyou cannot even detect that it has occurred.\n<\/p>\n<p>\n(One place where you are likely to see this sort of thing happening\nis if you have the debugger attached to the process, since the\ndebugger does things like suspend and resume threads,\nwhich result in kernel APCs.)\n<\/p>\n<p>\nAs a result, the <code>PulseEvent<\/code> function is useless\nand should be avoided.  It continues to exist solely for backwards\ncompatibility.\n<\/p>\n<p>\n<strong>Sidebar<\/strong>:\nThis whole business with kernel APCs also means that you cannot\npredict which thread will be woken when you signal a semaphore,\nan auto-reset event, or some other synchronization object that\nreleases a single thread when signalled.  If a thread is &#8220;borrowed&#8221;\nto service a kernel APC, then when it is returned to the wait list,\nit &#8220;goes back to the end of the line&#8221;.\nConsequently, the order of objects waiting for a kernel object\nis unpredictable and cannot be relied upon.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The PulseEvent function releases one thread (or all threads, if manual-reset) which is\/are waiting for the pulsed event, then returns the event to the unset state. If no threads happen to be waiting, then the event goes to the unset state without anything happening. And there&#8217;s the flaw. How do you know whether the thread [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-36803","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The PulseEvent function releases one thread (or all threads, if manual-reset) which is\/are waiting for the pulsed event, then returns the event to the unset state. If no threads happen to be waiting, then the event goes to the unset state without anything happening. And there&#8217;s the flaw. How do you know whether the thread [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/36803","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=36803"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/36803\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=36803"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=36803"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=36803"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}