{"id":16043,"date":"2009-11-13T07:00:00","date_gmt":"2009-11-13T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2009\/11\/13\/you-thought-reasoning-about-signals-was-bad-reasoning-about-a-total-breakdown-of-normal-functioning-is-even-worse\/"},"modified":"2022-08-05T06:40:57","modified_gmt":"2022-08-05T13:40:57","slug":"20091113-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20091113-00\/?p=16043\/","title":{"rendered":"You thought reasoning about signals was bad, reasoning about a total breakdown of normal functioning is even worse"},"content":{"rendered":"<p>A customer came to the Windows team with a question, the sort of question which on its face seems somewhat strange, which is itself a sign that the question is merely the tip of a much more dangerous iceberg.<\/p>\n<blockquote class=\"q\"><p>Under what circumstances will the <code>GetEnvironmentVariable<\/code> function hang?<\/p><\/blockquote>\n<p>This is kind of an open-ended question. I mean, for example, somebody might sneak in and call <code>SuspendThread<\/code> on your thread while <code>GetEnvironmentVariable<\/code> is running, which will look like a hang because the call never completes because the thread is frozen.<\/p>\n<p>But the real question for the customer is, &#8220;What sort of problem are you seeing that is manifesting itself in an apparent hang in the <code>GetEnvironmentVariable<\/code> function?&#8221;<\/p>\n<p>The customer was kind enough to elaborate.<\/p>\n<blockquote class=\"q\"><p>We have a global unhandled exception filter in our application so we can log all failures. After we finish logging, we call <code>ExitProcess<\/code>, but we find that the application never actually exits. If we connect a debugger to the stuck application, we see it hung in <code>GetEnvironmentVariable<\/code>.<\/p><\/blockquote>\n<p>Your gut response should be, &#8220;Holy cow, I&#8217;m surprised you even got that far!&#8221;<\/p>\n<p>This isn&#8217;t one of those global unhandled exception filters that got installed because your program plays some really clever game with exceptions, No, this is an &#8220;Oh no, my program just crashed and I want to log it&#8221; exception handler. In other words, when this exception handler &#8220;handles&#8221; an exception, it&#8217;s because your program has encountered some sort of serious internal programming error for which the program did not know how to recover. We saw earlier that <a title=\"How can SIGINT be safely delivered on the main thread?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20080728-00\/?p=21453\"> you can&#8217;t do much in a signal handler<\/a> because you might have interrupted a block of code which was in the middle of updating some data structures, leaving them momentarily inconsistent. But this exception filter is in an even worse state: Not only is there a good chance that the program is in the middle of updating something and left it in an inconsistent state, you are in fact <i>guaranteed<\/i> that the system is in a corrupted state.<\/p>\n<p>Why is this a guarantee? Because if the system were in a consistent state, you wouldn&#8217;t have crashed!<\/p>\n<p>Programming is about establishing invariants, perturbing them, and then re-establishing them. It is a game of stepping-stone from one island of consistency to another. But the code that does the perturbing and the re-establishing assumes that it&#8217;s starting from a consistent state to begin with. For example, a function that removes a node from a doubly-linked list manipulates some backward and forward link pointers (temporarily violating the linked list invariant), and then when it&#8217;s finished, the linked list is back to a consistent state. But this code assumes that the linked list is not corrupted to begin with!<\/p>\n<p>Let&#8217;s look again at that call to <code>ExitProcess<\/code>. That&#8217;s going to detach all the DLLs, calling each DLL&#8217;s <code>DllMain<\/code> with the <code>DLL_PROCESS_DETACH<\/code> notification. But of course, those <code>DllMain<\/code> are going to assume that the data structures are intact and nothing is corrupted. On the other hand, you know for a fact that these prerequisites are not met\u2014the program crashed precisely because something is corrupted. One DLL might walk a linked list\u2014but you might have crashed because that linked list is corrupted. Another DLL might try to delete a critical section\u2014but you might have crashed because the data structure containing the critical section is corrupted.<\/p>\n<p>Heck, the crash might have been inside somebody&#8217;s <code>DLL_PROCESS_DETACH<\/code> handler to begin with, for all you know.<\/p>\n<p>&#8220;Yeah, but the documentation for <code>TerminateProcess<\/code> says that it does not clean up shared memory.&#8221;<\/p>\n<p>Well, it depends on what you mean by <i>clean up<\/i>. The reference count on the shared memory is properly decremented when the handle is automatically closed as part of process cleanup, and the shared memory will be properly freed once there are no more references to it. It is not cleaned up in the sense of &#8220;corruption is repaired&#8221;\u2014but of course the operating system can&#8217;t do that because it doesn&#8217;t know what the semantics of your shared memory block are.<\/p>\n<p>But this is hardly anything to get concerned about because <i>your program doesn&#8217;t know how to un-corrupt the data either<\/i>.<\/p>\n<p>&#8220;It also says that DLLs don&#8217;t receive their <code>DLL_PROCESS_DETACH<\/code> notification.&#8221;<\/p>\n<p>As we saw before, this is a good thing in the case of a corrupted process, because the code that runs in <code>DLL_PROCESS_DETACH<\/code> assumes that your process has not been corrupted in the first place. There&#8217;s no point running it <i>when you know the process is corrupted<\/i>. You&#8217;re just making a bad situation worse.<\/p>\n<p>&#8220;It also says that I\/O will be in an indeterminate state.&#8221;<\/p>\n<p>Well yeah, but that&#8217;s no worse than what you have now, which is that your I\/O is in an indeterminate state. You don&#8217;t know what buffers your process hasn&#8217;t flushed, but since your process is corrupted, you have no way of finding out anyway.<\/p>\n<p>&#8220;Are you seriously recommending that I use <code>TerminateProcess<\/code> to exit the last chance exception handler?!?&#8221;<\/p>\n<p>Your process is unrecoverably corrupted. (This is a fact, because if there were a way to recover from it, <i>you would have done it instead of crashing<\/i>.) What other options are there?<\/p>\n<p>Quit while you&#8217;re behind.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When things are bad, don&#8217;t be surprised that they don&#8217;t work.<\/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-16043","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>When things are bad, don&#8217;t be surprised that they don&#8217;t work.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/16043","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=16043"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/16043\/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=16043"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=16043"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=16043"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}