{"id":27003,"date":"2007-05-03T07:00:00","date_gmt":"2007-05-03T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2007\/05\/03\/quick-overview-of-how-processes-exit-on-windows-xp\/"},"modified":"2007-05-03T07:00:00","modified_gmt":"2007-05-03T07:00:00","slug":"quick-overview-of-how-processes-exit-on-windows-xp","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20070503-00\/?p=27003","title":{"rendered":"Quick overview of how processes exit on Windows XP"},"content":{"rendered":"<p>Exiting is one of the scariest moments in the lifetime of a process. (Sort of how landing is one of the scariest moments of air travel.)\n Many of the details of how processes exit are left unspecified in Win32, so different Win32 implementations can follow different mechanisms. For example, Win32s, Windows&nbsp;95, and Windows&nbsp;NT all shut down processes differently. (I wouldn&#8217;t be surprised if Windows&nbsp;CE uses yet another different mechanism.) Therefore, bear in mind that what I write in this mini-series is implementation detail and <i>can change at any time without warning<\/i>. I&#8217;m writing about it because these details can highlight bugs lurking in your code. In particular, I&#8217;m going to discuss the way processes exit on Windows&nbsp;XP.\n I should say up front that I do not agree with many steps in the way processes exit on Windows&nbsp;XP. The purpose of this mini-series is not to justify the way processes exit but merely to fill you in on some of the behind-the-scenes activities so you are better-armed when you have to investigate into a mysterious crash or hang during exit. (Note that I just refer to it as the <i>way<\/i> processes exit on Windows&nbsp;XP rather than saying that it is how process exit is <i>designed<\/i>. As one of my colleagues put it, &#8220;Using the word <i>design<\/i> to describe this is like using the term <i>swimming pool<\/i> to refer to a puddle in your garden.&#8221;)\n When your program calls <code>ExitProcess<\/code> a whole lot of machinery springs into action. First, all the threads in the process (except the one calling <code>ExitProcess<\/code>) are forcibly terminated. This dates back to the old-fashioned theory on how processes should exit: Under the old-fashioned theory, when your process decides that it&#8217;s time to exit, it should already have cleaned up all its threads. The termination of threads, therefore, is just a safety net to catch the stuff you may have missed. <a href=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2007\/05\/02\/2365433.aspx#2375204\"> It doesn&#8217;t even wait two seconds first<\/a>.\n Now, we&#8217;re not talking happy termination like <code>ExitThread<\/code>; that&#8217;s not possible since the thread could be in the middle of doing something. Injecting a call to <code>ExitThread<\/code> would result in <code>DLL_THREAD_DETACH<\/code> notifications being sent at times the thread was not prepared for. Nope, these threads are terminated in the style of <code>TerminateThread<\/code>: Just yank the rug out from under it. Buh-bye. This is an ex-thread.\n Well, that was a pretty drastic move, now, wasn&#8217;t it. And all this after the scary warnings in MSDN that <code>TerminateThread<\/code> is a bad function that should be avoided!\n Wait, it gets worse.\n Some of those threads that got forcibly terminated may have owned critical sections, mutexes, home-grown synchronization primitives (such as spin-locks), all those things that the one remaining thread might need access to during its <code>DLL_PROCESS_DETACH<\/code> handling. Well, mutexes are sort of covered; if you try to enter that mutex, you&#8217;ll get the mysterious <a href=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2005\/09\/12\/463977.aspx\"> <code>WAIT_ABANDONED<\/code> return code<\/a> which tells you that &#8220;Uh-oh, things are kind of messed up.&#8221;\n What about critical sections? There is no &#8220;Uh-oh&#8221; return value for critical sections; <code>EnterCriticalSection<\/code> doesn&#8217;t have a return value. Instead, the kernel just says &#8220;Open season on critical sections!&#8221; I get the mental image of all the gates in a parking garage just opening up and letting anybody in and out. [See <a href=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2010\/01\/22\/9951750.aspx\"> correction<\/a>.]\n As for the home-grown stuff, well, you&#8217;re on your own.\n This means that if your code happened to have owned a critical section at the time somebody called <code>ExitProcess<\/code>, the data structure the critical section is protecting has a good chance of being in an inconsistent state. (Afer all, if it were consistent, you probably would have exited the critical section! Well, assuming you entered the critical section because you were updating the structure as opposed to reading it.) Your <code>DLL_PROCESS_DETACH<\/code> code runs, enters the critical section, and it <i>succeeds<\/i> because &#8220;all the gates are up&#8221;. Now your <code>DLL_PROCESS_DETACH<\/code> code starts behaving erratically because the values in that data structure are inconsistent.\n Oh dear, now you have a pretty ugly mess on your hands.\n And if your thread was terminated while it owned a spin-lock or some other home-grown synchronization object, your <code>DLL_PROCESS_DETACH<\/code> will most likely simply hang indefinitely waiting patiently for that terminated thread to release the spin-lock (which it never will do).\n But wait, it gets worse. That critical section might have been the one that protects the process heap! If one of the threads that got terminated happened to be in the middle of a heap function like <code>HeapAllocate<\/code> or <code>LocalFree<\/code>, then the process heap may very well be inconsistent. If your <code>DLL_PROCESS_DETACH<\/code> tries to allocate or free memory, it may crash due to a corrupted heap.\n Moral of the story: If you&#8217;re getting a <code>DLL_PROCESS_DETACH<\/code> due to process termination,&dagger; don&#8217;t try anything clever. Just return without doing anything and let the normal process clean-up happen. The kernel will close all your open handles to kernel objects. Any memory you allocated will be freed automatically when the process&#8217;s address space is torn down. Just let the process die a quiet death.\n Note that if you were a good boy and cleaned up all the threads in the process before calling <code>ExitThread<\/code>, then you&#8217;ve escaped all this craziness, since there is nothing to clean up.\n Note also that if you&#8217;re getting a <code>DLL_PROCESS_DETACH<\/code> due to dynamic unloading, then you do need to clean up your kernel objects and allocated memory because the process is going to continue running. But on the other hand, in the case of dynamic unloading, no other threads should be executing code in your DLL anyway (since you&#8217;re about to be unloaded), so&mdash;assuming you coded up your DLL correctly&mdash;none of your critical sections should be held and your data structures should be consistent.\n Hang on, this disaster isn&#8217;t over yet. Even though the kernel went around terminating all but one thread in the process, that doesn&#8217;t mean that the creation of new threads is blocked. If somebody calls <code>CreateThread<\/code> in their <code>DLL_PROCESS_DETACH<\/code> (as crazy as it sounds), the thread will indeed be created and start running! But remember, &#8220;all the gates are up&#8221;, so your critical sections are just window dressing to make you feel good.\n (The ability to create threads after process termination has begun is not a mistake; it&#8217;s intentional and necessary. Thread injection is how the debugger breaks into a process. If thread injection were not permitted, you wouldn&#8217;t be able to debug process termination!)\n Next time, we&#8217;ll see how the way process termination takes place on Windows&nbsp;XP caused not one but two problems.\n <b>Footnotes<\/b><\/p>\n<p> &dagger;Everybody reading this article should already know how to determine whether this is the case. I&#8217;m assuming you&#8217;re smart. Don&#8217;t disappoint me. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Exiting is one of the scariest moments in the lifetime of a process. (Sort of how landing is one of the scariest moments of air travel.) Many of the details of how processes exit are left unspecified in Win32, so different Win32 implementations can follow different mechanisms. For example, Win32s, Windows&nbsp;95, and Windows&nbsp;NT all shut [&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":[26],"class_list":["post-27003","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-other"],"acf":[],"blog_post_summary":"<p>Exiting is one of the scariest moments in the lifetime of a process. (Sort of how landing is one of the scariest moments of air travel.) Many of the details of how processes exit are left unspecified in Win32, so different Win32 implementations can follow different mechanisms. For example, Win32s, Windows&nbsp;95, and Windows&nbsp;NT all shut [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/27003","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=27003"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/27003\/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=27003"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=27003"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=27003"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}