Merge pull request #377 from adierking/fls

Ignore Windows FLS destructors during process exit
diff --git a/src/queue.c b/src/queue.c
index 896ed21..f78cb17 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -961,9 +961,57 @@
 }
 #endif
 
+#if defined(_WIN32)
+static bool
+_dispatch_process_is_exiting(void)
+{
+	// The goal here is to detect if the current thread is executing cleanup
+	// code (e.g. FLS destructors) as a result of calling ExitProcess(). Windows
+	// doesn't provide an official method of getting this information, so we
+	// take advantage of how ExitProcess() works internally. The first thing
+	// that it does (according to MSDN) is terminate every other thread in the
+	// process. Logically, it should not be possible to create more threads
+	// after this point, and Windows indeed enforces this. Try to create a
+	// lightweight suspended thread, and if access is denied, assume that this
+	// is because the process is exiting.
+	//
+	// We aren't worried about any race conditions here during process exit.
+	// Cleanup code is only run on the thread that already called ExitProcess(),
+	// and every other thread will have been forcibly terminated by the time
+	// that happens. Additionally, while CreateThread() could conceivably fail
+	// due to resource exhaustion, the process would already be in a bad state
+	// if that happens. This is only intended to prevent unwanted cleanup code
+	// from running, so the worst case is that a thread doesn't clean up after
+	// itself when the process is about to die anyway.
+	const size_t stack_size = 1;  // As small as possible
+	HANDLE thread = CreateThread(NULL, stack_size, NULL, NULL,
+			CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
+	if (thread) {
+		// Although Microsoft recommends against using TerminateThread, it's
+		// safe to use it here because we know that the thread is suspended and
+		// it has not executed any code due to a NULL lpStartAddress. There was
+		// a bug in Windows Server 2003 and Windows XP where the initial stack
+		// would not be freed, but libdispatch does not support them anyway.
+		TerminateThread(thread, 0);
+		CloseHandle(thread);
+		return false;
+	}
+	return GetLastError() == ERROR_ACCESS_DENIED;
+}
+#endif
+
 void DISPATCH_TSD_DTOR_CC
 _libdispatch_tsd_cleanup(void *ctx)
 {
+#if defined(_WIN32)
+	// On Windows, exiting a process will still call FLS destructors for the
+	// thread that called ExitProcess(). pthreads-based platforms don't call key
+	// destructors on exit, so be consistent.
+	if (_dispatch_process_is_exiting()) {
+		return;
+	}
+#endif
+
 	struct dispatch_tsd *tsd = (struct dispatch_tsd*) ctx;
 
 	_tsd_call_cleanup(dispatch_priority_key, NULL);