{"id":11583,"date":"2011-02-04T07:00:00","date_gmt":"2011-02-04T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2011\/02\/04\/ready-cancel-wait-for-it-part-3\/"},"modified":"2011-02-04T07:00:00","modified_gmt":"2011-02-04T07:00:00","slug":"ready-cancel-wait-for-it-part-3","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110204-00\/?p=11583","title":{"rendered":"Ready&#8230; cancel&#8230; wait for it! (part 3)"},"content":{"rendered":"<p>\nA customer reported that their application was crashing in RPC,\nand they submitted a sample program which illustrated the same\ncrash as their program.\nTheir sample program was actually based on the\nAsyncRPC sample client program, which was nice, because it\nprovided a mutually-known starting point.\nThey made quite a few changes to the program, but this is\nthe important one:\n<\/p>\n<pre>\n\/\/ old code:\n\/\/ status = RpcAsyncCancelCall(&amp;Async, FALSE);\n\/\/ new code:\n status = RpcAsyncCancelCall(&amp;Async, TRUE);\n<\/pre>\n<p>\n(It was actually more complicated than this,\nbut this is the short version.)\n<\/p>\n<p>\nThe program was crashing for the same reason that\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/02\/02\/10123392.aspx\">\nWednesday&#8217;s I\/O cancellation program was crashing<\/a>:\nThe program issued an asynchronous cancel and didn&#8217;t\nwait for the cancel to complete.\nIn this case, the crash occurred when the RPC call\nfinally completed and RPC went about cleaning up the call\nbased on the information in the now-freed\n<code>RPC_ASYNC_STATE<\/code> structure.\n<\/p>\n<p>\nThe error was probably caused by the not-very-helpful\nname for that last parameter to <code>Rpc&shy;Async&shy;Cancel&shy;Call<\/code>:\n<code>fAbort&shy;Call<\/code>,\nand the accompanying documentation which says,\n&#8220;In an abortive cancel (<i>fAbort&shy;Call<\/i> is TRUE),\nthe <b>Rpc&shy;Async&shy;Cancel&shy;Call<\/b> function sends a cancel\nnotification to the server and client side and the\nasynchronous call is canceled immediately,\nnot waiting for a response from the server.&#8221;\nCompare this to a nonabortive cancel,\nwhere &#8220;the <b>Rpc&shy;Async&shy;Cancel&shy;Call<\/b> function notifies\nthe server of the cancel and the client waits for the\nserver to complete the call.&#8221;\n<\/p>\n<p>\nObviously,\nit&#8217;s faster if you don&#8217;t wait for the server to respond, right?\nLet&#8217;s pass <code>TRUE<\/code>, so that the function cancels the\nasynchronous call immediately without waiting for the server.\nWow, look at how fast our program runs now!\n<\/p>\n<p>\nUnfortunately,\nthe documentation doesn&#8217;t make it sufficiently clear\nthat when you issue a cancellation, you still have to\nwait for the operation to complete before you can clean up\nall the resources associated with that operation.\nAnother way of looking at that last parameter is to think\nof it as <code>fAsync<\/code>.\nIf you pass <code>fAsync = TRUE<\/code>,\nthen the\n<code>Rpc&shy;Async&shy;Cancel&shy;Call<\/code>\nfunction issues the cancellation\nand returns before the operation completes.\nIf you pass <code>fAsync = FALSE<\/code>,\nthen the\n<code>Rpc&shy;Async&shy;Cancel&shy;Call<\/code>\nfunction issues the cancellation\nand waits for the operation to complete before returning.\n<\/p>\n<p>\nIf you switch from a synchronous cancel to an asynchronous cancel,\nthen you become responsible for keeping the\n<code>RPC_ASYNC_STATE<\/code>\nvalid until the cancellation completes.\nIn this case, the customer was using the\n<code>Rpc&shy;Notification&shy;Type&shy;Event<\/code> notification type,\nwhich means that they need to wait for the\n<code>Async.u.hEvent<\/code> to become signaled before they\ncan free the <code>RPC_ASYNC_STATE<\/code>.\n<\/p>\n<p>\nThe customer confirmed the fix and closed the support case.\nAnother problem solved.\n<\/p>\n<p>\nThree months later, the customer reopened the case,\nreporting that after they released a new version of their\nprogram with the aforementioned fix,\nthey were nevertheless getting\n<a HREF=\"https:\/\/winqual.microsoft.com\/\">WinQual<\/a>\ncrashes which looked exactly like the ones that they were\nhaving before they applied the fix.\nIt appears that the fix wasn&#8217;t working.\n<\/p>\n<p>\nUpon closer investigation, it turns out that the customer\noriginally did apply the fix as recommended:\nThey added a\n<code>Wait&shy;For&shy;Single&shy;Object(Async.u.hEvent, INFINITE)<\/code>\ncall before destroying the <code>Async<\/code> object\nto ensure that the cancellation was complete.\nHowever, they became frustrated that sometimes the cancellation\nwould take a long time to complete, so they changed it to\n<\/p>\n<pre>\nWaitForSingleObject(Async.u.hEvent, 5000); \/\/ wait up to 5 seconds\n<\/pre>\n<p>\nThe customer explained,\n&#8220;After the wait fails due to timeout,\nwe just proceed as normal and call\n<code>Rpc&shy;Async&shy;Complete&shy;Call<\/code> and free the the\n<code>RPC_ASYNC_STATE<\/code>. Is that wrong?&#8221;\n<\/p>\n<p>\nUm, yeah.\nChanging the\n<code>Wait&shy;For&shy;Single&shy;Object<\/code>\nfrom an infinite wait\nto one with a timeout means that\nyou just reintroduced the bug that the\n<code>Wait&shy;For&shy;Single&shy;Object<\/code>\nwas originally supposed to fix!\nIf the cancellation takes more than 5&nbsp;seconds,\nthen your code will continue and free the\n<code>RPC_ASYNC_STATE<\/code>,\njust like it did when you didn&#8217;t wait at all.\n<\/p>\n<p>\n&#8220;How long can I wait before assuming that the event will simply\nnever get signaled?&#8221;\n<\/p>\n<p>\nThere is no such duration after which you can safely abandon the operation.\nEven if the event doesn&#8217;t get signaled for 30 minutes\n(say because the computer is thrashing its guts out),\nit may get signaled at 30 minutes and 1 second.\n<\/p>\n<p>\n&#8220;But we don&#8217;t want our program to get stuck waiting for the server.&#8221;\n<\/p>\n<p>\nGreat.\nIt&#8217;s fine to have your program continues running after\nissuing the cancellation, even if the RPC call hasn&#8217;t completed.\nJust don&#8217;t free the <code>RPC_ASYNC_STATE<\/code>\nuntil the call is complete.\nand if you set things up so that your completion event takes the\nform of a callback,\n<a HREF=\"http:\/\/social.msdn.microsoft.com\/Forums\/en-CA\/windowssdk\/thread\/d4ca3eb0-cbc0-4cbe-9626-7d80043969d8\">\nyou can just make the callback free the\n<code>RPC__ASYNC_STATE<\/code><\/a>.\nThen you don&#8217;t have to keep track of the asynchronous call any more;\nthe system will merely call you when it&#8217;s finished, and then you\ncan free the state structure.\n<\/p>\n<p>\n<b>Bonus RPC chatter<\/b>:\n(For the purpose of this discussion, I&#8217;ll use the term\n<i>RPC operation<\/i> instead of <i>RPC call<\/i> so we don&#8217;t have\nconfusion between function calls and RPC calls.)\nA colleague explained the lifetime of an RPC operation as follows:\n<\/p>\n<table BORDER=\"1\" STYLE=\"border-collapse: collapse\" CELLPADDING=\"3\">\n<tbody>\n<tr>\n<td NOWRAP ROWSPAN=\"3\">Submit phase<\/td>\n<td>You call into the MIDL-generated stub.<\/td>\n<td ROWSPAN=\"3\">You cannot call\n    <code>Rpc&shy;Async&shy;Cancel&shy;Call<\/code>\n    during the submit phase.<\/td>\n<\/tr>\n<tr>\n<td>The stub does magic RPC stuff.<\/td>\n<\/tr>\n<tr>\n<td>The stub returns control back to the caller.<\/td>\n<\/tr>\n<tr>\n<td NOWRAP>Pending phase<\/td>\n<td>RPC is waiting for the response to the operation.\n    The operation remains in this phase until\n    the operation completes or is cancelled.\n<\/td>\n<td>You can call\n    <code>Rpc&shy;Async&shy;Cancel&shy;Call<\/code> to cancel the\n    RPC operation and accelerate the transition to the Notified phase.\n<\/td>\n<\/tr>\n<tr>\n<td NOWRAP>Notified phase<\/td>\n<td>RPC informs the application of the result of the operation\n    in a manner described by the <code>Notification&shy;Type<\/code>\n    and <code>RPC_ASYNC_NOTIFICATION_INFO<\/code> members of\n    the <code>RPC_ASYNC_STATE<\/code> structure.\n<\/td>\n<td>You can call\n    <code>Rpc&shy;Async&shy;Cancel&shy;Call<\/code> but it will have no effect\n    since the operation is already complete.\n<\/td>\n<\/tr>\n<tr>\n<td NOWRAP>Completion phase<\/td>\n<td>The application calls\n    the <code>Rpc&shy;Async&shy;Complete&shy;Call<\/code> function to clean up\n    the resources used to track the RPC operation.\n    You exit the completion phase when\n    <code>Rpc&shy;Async&shy;Complete&shy;Call<\/code>\n    returns something other than\n    <code>RPC_S_ASYNC_CALL_PENDING.\n<\/code><\/td>\n<td>You cannot call <code>Rpc&shy;Async&shy;Cancel&shy;Call<\/code> after\n    <code>Rpc&shy;Async&shy;Complete&shy;Call<\/code>\n    indicates that the operation\n    is complete, since that is the call that says &#8220;I&#8217;m all done!&#8221;\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n","protected":false},"excerpt":{"rendered":"<p>A customer reported that their application was crashing in RPC, and they submitted a sample program which illustrated the same crash as their program. Their sample program was actually based on the AsyncRPC sample client program, which was nice, because it provided a mutually-known starting point. They made quite a few changes to the program, [&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-11583","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A customer reported that their application was crashing in RPC, and they submitted a sample program which illustrated the same crash as their program. Their sample program was actually based on the AsyncRPC sample client program, which was nice, because it provided a mutually-known starting point. They made quite a few changes to the program, [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/11583","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=11583"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/11583\/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=11583"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=11583"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=11583"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}