{"id":7763,"date":"2012-04-27T07:00:00","date_gmt":"2012-04-27T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2012\/04\/27\/a-process-shutdown-puzzle-episode-2\/"},"modified":"2012-04-27T07:00:00","modified_gmt":"2012-04-27T07:00:00","slug":"a-process-shutdown-puzzle-episode-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20120427-00\/?p=7763","title":{"rendered":"A process shutdown puzzle, Episode 2"},"content":{"rendered":"<p>\nA customer reported that their program would very sporadically\ncrash in the function\n<a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms682036(VS.85).aspx\">\n<code>Close&shy;Thread&shy;pool&shy;Cleanup&shy;Group&shy;Members<\/code><\/a>.\nThe customer was kind enough to provide a stack trace at the point\nof the crash:\n<\/p>\n<pre>\nntdll!RtlUnhandledExceptionFilter2+0x31e\nKERNELBASE!UnhandledExceptionFilter+0x175\nntdll!RtlUserThreadStart$filt$0+0x3f\nntdll!__C_specific_handler+0x8f\nntdll!RtlpExecuteHandlerForException+0xd\nntdll!RtlDispatchException+0x3a6\nntdll!RtlRaiseException+0x223\nntdll!TppRaiseInvalidParameter+0x48\nntdll!TpReleaseCleanupGroupMembers+0x246\nlitware!CThreadPool::UnInitialize+0x22\nlitware!_CRT_INIT+0xbf\nlitware!__DllMainCRTStartup+0x18b\nntdll!LdrpCallInitRoutine+0x3f\nntdll!LdrShutdownProcess+0x205\nntdll!RtlExitUserProcess+0x90\nkernel32!ExitProcessImplementation+0xa\ncontoso!wmain+0x193\ncontoso!__wmainCRTStartup+0x13d\nkernel32!BaseThreadInitThunk+0xd\nntdll!RtlUserThreadStart+0x1d\n<\/pre>\n<p>\nThe customer wondered,\n&#8220;Could the problem be that my cleanup group does not have\na callback?\nMSDN seems to suggest that this is okay.&#8221;\n<\/p>\n<p>\nThe exception being thrown is\n<code>STATUS_INVALID_PARAMETER<\/code>,\nbut that doesn&#8217;t really say much.\n<\/p>\n<p>\nBut that&#8217;s okay, because the smoking gun isn&#8217;t the exception\nbeing raised.\nIt&#8217;s in the stack.\n<\/p>\n<p>\nDo you see it?\n<\/p>\n<p>\nThe code is calling\n<code>Close&shy;Thread&shy;pool&shy;Cleanup&shy;Group&shy;Members<\/code>\nfrom inside <code>DllMain<\/code>\nwhile handling the\n<code>DLL_PROCESS_DETACH<\/code> notification.\nLooking further up the stack, you can see this was triggered by\na call to <code>ExitProcess<\/code>,\nand now all the stuff you know about\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2007\/05\/03\/2383346.aspx\">\nhow processes exit<\/a>\nkicks in.\n<\/p>\n<p>\nFor example, that the first thing that happens is that all threads\nare forcibly terminated.\n<\/p>\n<p>\nThat&#8217;s your next clue.\n<\/p>\n<p>\nObserve that the customer&#8217;s DLL is trying to communicate with the\nthread pool during process termination.\nBut wait, all the threads have already been terminated.\nIt&#8217;s trying to communicate with a nonexistent thread pool.\n<\/p>\n<p>\nThe thread pool realizes,\n&#8220;Hey, like I&#8217;ve already been destroyed.\nI can&#8217;t do what you ask because there is no thread pool any more.\nYou want me to block until all currently executing callback\nfunctions finish,\nbut those callback functions will never finish (if they even exist at all)\n<i>because the threads hosting their thread pool got destroyed<\/i>.\nNot that I can tell whether they are executing or not,\nbecause I am already destroyed.\nThe only options are to hang or crash.\nI think I&#8217;ll crash.&#8221;\n<\/p>\n<p>\nThe customer needs to restructure the program so that it either\ncleans up its thread pool work before the\n<code>ExitProcess<\/code>,\nor it can simply skip all thread pool operations when the reason for\nthe <code>DLL_PROCESS_DETACH<\/code> is process termination.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A customer reported that their program would very sporadically crash in the function Close&shy;Thread&shy;pool&shy;Cleanup&shy;Group&shy;Members. The customer was kind enough to provide a stack trace at the point of the crash: ntdll!RtlUnhandledExceptionFilter2+0x31e KERNELBASE!UnhandledExceptionFilter+0x175 ntdll!RtlUserThreadStart$filt$0+0x3f ntdll!__C_specific_handler+0x8f ntdll!RtlpExecuteHandlerForException+0xd ntdll!RtlDispatchException+0x3a6 ntdll!RtlRaiseException+0x223 ntdll!TppRaiseInvalidParameter+0x48 ntdll!TpReleaseCleanupGroupMembers+0x246 litware!CThreadPool::UnInitialize+0x22 litware!_CRT_INIT+0xbf litware!__DllMainCRTStartup+0x18b ntdll!LdrpCallInitRoutine+0x3f ntdll!LdrShutdownProcess+0x205 ntdll!RtlExitUserProcess+0x90 kernel32!ExitProcessImplementation+0xa contoso!wmain+0x193 contoso!__wmainCRTStartup+0x13d kernel32!BaseThreadInitThunk+0xd ntdll!RtlUserThreadStart+0x1d The customer wondered, &#8220;Could the [&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-7763","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 program would very sporadically crash in the function Close&shy;Thread&shy;pool&shy;Cleanup&shy;Group&shy;Members. The customer was kind enough to provide a stack trace at the point of the crash: ntdll!RtlUnhandledExceptionFilter2+0x31e KERNELBASE!UnhandledExceptionFilter+0x175 ntdll!RtlUserThreadStart$filt$0+0x3f ntdll!__C_specific_handler+0x8f ntdll!RtlpExecuteHandlerForException+0xd ntdll!RtlDispatchException+0x3a6 ntdll!RtlRaiseException+0x223 ntdll!TppRaiseInvalidParameter+0x48 ntdll!TpReleaseCleanupGroupMembers+0x246 litware!CThreadPool::UnInitialize+0x22 litware!_CRT_INIT+0xbf litware!__DllMainCRTStartup+0x18b ntdll!LdrpCallInitRoutine+0x3f ntdll!LdrShutdownProcess+0x205 ntdll!RtlExitUserProcess+0x90 kernel32!ExitProcessImplementation+0xa contoso!wmain+0x193 contoso!__wmainCRTStartup+0x13d kernel32!BaseThreadInitThunk+0xd ntdll!RtlUserThreadStart+0x1d The customer wondered, &#8220;Could the [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/7763","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=7763"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/7763\/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=7763"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=7763"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=7763"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}