{"id":91811,"date":"2015-08-14T07:00:00","date_gmt":"2015-08-14T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/20150814-00\/?p=91811\/"},"modified":"2019-03-13T12:18:32","modified_gmt":"2019-03-13T19:18:32","slug":"20150814-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20150814-00\/?p=91811","title":{"rendered":"Windows started picking up the really big pieces of TerminateThread garbage on the sidewalk, but it&#8217;s still garbage on the sidewalk"},"content":{"rendered":"<p>Ah, <code>Terminate&shy;Thread<\/code>. There are still people who think that there are valid scenarios for calling <code>Terminate&shy;Thread<\/code>. <!--more--><\/p>\n<blockquote CLASS=\"q\">\n<p>Can you explain how <code>Exit&shy;Thread<\/code> works? <\/p>\n<p>We are interested because we have a class called <code>Thread&shy;Class<\/code>. We call the <code>Start()<\/code> method , and then the <code>Stop()<\/code> method, and then the <code>Wait&shy;Until&shy;Stopped()<\/code> method, and then the process hangs with this call stack: <\/p>\n<pre>\nntdll!ZwWaitForSingleObject\nntdll!RtlpWaitOnCriticalSection\nntdll!RtlEnterCriticalSection\nntdll!LdrShutdownThread\nntdll!RtlExitUserThread\nkernel32!BaseThreadInitThunk\nntdll!RtlUserThreadStart\n<\/pre>\n<p>Can you help us figure out what&#8217;s going on? <\/p>\n<\/blockquote>\n<p>From the stack trace, it is clear that the thread is shutting down, and the loader (<code>Ldr<\/code>) is waiting on a critical section. The critical section the loader is most famous for needing is the so-called <i>loader lock<\/i> which is used for various things, most notably to make sure that all DLL thread notification are serialized. <\/p>\n<p>I guessed that the call to <code>Wait&shy;Until&shy;Stopped()<\/code> was happening inside <code>Dll&shy;Main<\/code>, which created a deadlock because the thread cannot exit until it delivers its <code>Dll&shy;Main<\/code> notifications, but it can&#8217;t do that until the calling thread exits <code>Dll&shy;Main<\/code>. <\/p>\n<p>The customer did some more debugging: <\/p>\n<blockquote CLASS=\"q\">\n<p>The debugger reports the critical section as <\/p>\n<pre>\nCritSec ntdll!LdrpLoaderLock+0 at 77724300\nWaiterWoken        No\nLockCount          3\nRecursionCount     1\nOwningThread       a80\nEntryCount         0\nContentionCount    3\n*** Locked\n<\/pre>\n<p>The critical section claims that it is owned by thread <code>0xa80<\/code>, but there is no such active thread in the process. In the kernel debugger, a search for that thread says <\/p>\n<pre>\nLooking for thread Cid = a80 ...\nTHREAD 8579e1c0  Cid 0b58.0a80  Teb: 00000000 Win32Thread: 00000000 TERMINATED\nNot impersonating\nDeviceMap                 862f8a98\nOwning Process            0       Image:         &lt;Unknown&gt;\nAttached Process          84386d90       Image:         Contoso.exe\nWait Start TickCount      12938474       Ticks: 114780 (0:00:29:50.579)\nContext Switch Count      8             \nUserTime                  00:00:00.000\nKernelTime                00:00:00.000\nWin32 Start Address 0x011167c0\nStack Init 0 Current bae35be0 Base bae36000 Limit bae33000 Call 0\nPriority 10 BasePriority 8 PriorityDecrement 2 IoPriority 2 PagePriority 5\n<\/pre>\n<p><code>Contoso.exe<\/code> is our process. <\/p>\n<\/blockquote>\n<p>Okay, we&#8217;re getting somewhere now. The thread <code>0xa80<\/code> terminated while it held the loader lock. When you run the program under a debugger, do you see any exceptions that might suggest that the thread terminated abnormally? <\/p>\n<blockquote CLASS=\"q\">\n<p>We found the cause of the problem. We use <code>Terminate&shy;Thread<\/code> in the other place. That causes the thread to continue to hold the loader lock after it has terminated. <\/p>\n<\/blockquote>\n<p>It&#8217;s not clear what the customer meant by &#8220;the other place&#8221;, but no matter. The cause of the problem was found: They were using <code>Terminate&shy;Thread<\/code>. <\/p>\n<p>At this point, Larry Osterman was inspired to write a poem. <\/p>\n<blockquote CLASS=\"m\"><p>How many times does<br>it have to be said: Never<br>call TerminateThread. <\/p><\/blockquote>\n<p>In the ensuing discussion, somebody suggested, <\/p>\n<blockquote CLASS=\"q\"><p>One case where it is okay to use <code>Terminate&shy;Thread<\/code> is if the thread was created suspended and has never been resumed. I believe it is perfectly legal to terminate it, at least in Windows Vista and later. <\/p><\/blockquote>\n<p>No, it is not &#8220;perfectly legal,&#8221; for certain values of &#8220;perfectly legal.&#8221; <\/p>\n<p>What happened is that Windows Vista added some code to try to limit the impact of a bad idea. Specifically, it added code to free the thread&#8217;s stack when the thread was terminated, so that each terminated thread didn&#8217;t leak a megabyte of memory. In the parlance of earlier discussion, I referred to this as <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2010\/03\/11\/9976571.aspx\">stop throwing garbage on the sidewalk<\/a>. <\/p>\n<p>In this case, it&#8217;s like saying, &#8220;It&#8217;s okay to run this red light because the city added a delayed green to the cross traffic.&#8221; The city added a delayed green to the cross traffic because people were running the light and the city didn&#8217;t want people to die. That doesn&#8217;t mean that it&#8217;s okay to run the light now. <\/p>\n<p>Unfortunately, the guidance that says &#8220;Sometimes it&#8217;s okay to call <code>Terminate&shy;Thread<\/code>&#8221; has seeped into our own Best Practices documents. The <a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/windows\/desktop\/dn633971(v=vs.85).aspx\">Dynamic-Link Library Best Practices<\/a> under <i>Best Practices for Synchronization<\/i> describes a synchronization model which actually involves calling <code>Terminate&shy;Thread<\/code>. <\/p>\n<p><i>Do not do this<\/i>. <\/p>\n<p>It&#8217;s particularly sad because <a HREF=\"https:\/\/view.officeapps.live.com\/op\/view.aspx?src=http%3A%2F%2Fdownload.microsoft.com%2Fdownload%2Fa%2Ff%2F7%2Faf7777e5-7dcd-4800-8a0a-b18336565f5b%2FDLL_bestprac.doc\">the downloadable version of the document<\/a> references both Larry and me telling people to stop doing crazy things in <code>Dll&shy;Main<\/code>, and terminating threads is definitely a crazy thing. <\/p>\n<p>(The solution to the problem described in the whitepaper is not to use <code>Terminate&shy;Thread<\/code>. It&#8217;s to use <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2013\/11\/05\/10463645.aspx\">the <code>Free&shy;Library&shy;And&shy;Exit&shy;Thread<\/code> pattern<\/a>.) <\/p>\n<p>Now the history. <\/p>\n<p>Originally, there was no <code>Terminate&shy;Thread<\/code> function. The original designers felt strongly that no such function should exist because there was no safe way to terminate a thread, and there&#8217;s no point having a function that cannot be called safely. But people screamed that they needed the <code>Terminate&shy;Thread<\/code> function, even though it wasn&#8217;t safe, so the operating system designers caved and added the function because people demanded it. Of course, those people who insisted that they needed <code>Terminate&shy;Thread<\/code> now regret having been given it. <\/p>\n<p>It&#8217;s one of those &#8220;Be careful what you wish for&#8221; things. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>So stop throwing garbage on the sidewalk.<\/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":[2],"class_list":["post-91811","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>So stop throwing garbage on the sidewalk.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/91811","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=91811"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/91811\/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=91811"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=91811"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=91811"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}