8059533: (process) Make exiting process wait for exiting threads [win]
authorigerasim
Wed, 29 Oct 2014 09:53:56 -0700
changeset 27459 7fce477785a3
parent 27458 eb5f1b4f01e1
child 27460 ca843db0a7e2
8059533: (process) Make exiting process wait for exiting threads [win] Reviewed-by: dholmes, dcubed
hotspot/src/os/windows/vm/os_windows.cpp
--- a/hotspot/src/os/windows/vm/os_windows.cpp	Wed Oct 29 09:19:59 2014 -0700
+++ b/hotspot/src/os/windows/vm/os_windows.cpp	Wed Oct 29 09:53:56 2014 -0700
@@ -436,9 +436,9 @@
   }
 
   // Diagnostic code to investigate JDK-6573254
-  int res = 90115;  // non-java thread
+  int res = 50115;  // non-java thread
   if (thread->is_Java_thread()) {
-    res = 60115;    // java thread
+    res = 40115;    // java thread
   }
 
   // Install a win32 structured exception handler around every thread created
@@ -3740,68 +3740,134 @@
   return NULL;
 }
 
-#define MIN_EXIT_MUTEXES 1
-#define MAX_EXIT_MUTEXES 16
-
-struct ExitMutexes {
-  DWORD count;
-  HANDLE handles[MAX_EXIT_MUTEXES];
-};
-
-static BOOL CALLBACK init_muts_call(PINIT_ONCE, PVOID ppmuts, PVOID*) {
-  static ExitMutexes muts;
-
-  muts.count = os::processor_count();
-  if (muts.count < MIN_EXIT_MUTEXES) {
-    muts.count = MIN_EXIT_MUTEXES;
-  } else if (muts.count > MAX_EXIT_MUTEXES) {
-    muts.count = MAX_EXIT_MUTEXES;
-  }
-
-  for (DWORD i = 0; i < muts.count; ++i) {
-    muts.handles[i] = CreateMutex(NULL, FALSE, NULL);
-    if (muts.handles[i] == NULL) {
-      return FALSE;
-    }
-  }
-  *((ExitMutexes**)ppmuts) = &muts;
+#define MAX_EXIT_HANDLES    16
+#define EXIT_TIMEOUT      1000 /* 1 sec */
+
+static BOOL CALLBACK init_crit_sect_call(PINIT_ONCE, PVOID pcrit_sect, PVOID*) {
+  InitializeCriticalSection((CRITICAL_SECTION*)pcrit_sect);
   return TRUE;
 }
 
 int os::win32::exit_process_or_thread(Ept what, int exit_code) {
+  // Basic approach:
+  //  - Each exiting thread registers its intent to exit and then does so.
+  //  - A thread trying to terminate the process must wait for all
+  //    threads currently exiting to complete their exit.
+
   if (os::win32::has_exit_bug()) {
-    static INIT_ONCE init_once_muts = INIT_ONCE_STATIC_INIT;
-    static ExitMutexes* pmuts;
-
-    if (!InitOnceExecuteOnce(&init_once_muts, init_muts_call, &pmuts, NULL)) {
-      warning("ExitMutex initialization failed in %s: %d\n", __FILE__, __LINE__);
-    } else if (WaitForMultipleObjects(pmuts->count, pmuts->handles,
-                                      (what != EPT_THREAD), // exiting process waits for all mutexes
-                                      INFINITE) == WAIT_FAILED) {
-      warning("ExitMutex acquisition failed in %s: %d\n", __FILE__, __LINE__);
+    // The array holds handles of the threads that have started exiting by calling
+    // _endthreadex().
+    // Should be large enough to avoid blocking the exiting thread due to lack of
+    // a free slot.
+    static HANDLE handles[MAX_EXIT_HANDLES];
+    static int handle_count = 0;
+
+    static INIT_ONCE init_once_crit_sect = INIT_ONCE_STATIC_INIT;
+    static CRITICAL_SECTION crit_sect;
+    int i, j;
+    DWORD res;
+    HANDLE hproc, hthr;
+
+    // The first thread that reached this point, initializes the critical section.
+    if (!InitOnceExecuteOnce(&init_once_crit_sect, init_crit_sect_call, &crit_sect, NULL)) {
+      warning("crit_sect initialization failed in %s: %d\n", __FILE__, __LINE__);
+    } else {
+      EnterCriticalSection(&crit_sect);
+
+      if (what == EPT_THREAD) {
+        // Remove from the array those handles of the threads that have completed exiting.
+        for (i = 0, j = 0; i < handle_count; ++i) {
+          res = WaitForSingleObject(handles[i], 0 /* don't wait */);
+          if (res == WAIT_TIMEOUT) {
+            handles[j++] = handles[i];
+          } else {
+            if (res != WAIT_OBJECT_0) {
+              warning("WaitForSingleObject failed in %s: %d\n", __FILE__, __LINE__);
+              // Don't keep the handle, if we failed waiting for it.
+            }
+            CloseHandle(handles[i]);
+          }
+        }
+
+        // If there's no free slot in the array of the kept handles, we'll have to
+        // wait until at least one thread completes exiting.
+        if ((handle_count = j) == MAX_EXIT_HANDLES) {
+          res = WaitForMultipleObjects(MAX_EXIT_HANDLES, handles, FALSE, EXIT_TIMEOUT);
+          if (res >= WAIT_OBJECT_0 && res < (WAIT_OBJECT_0 + MAX_EXIT_HANDLES)) {
+            i = (res - WAIT_OBJECT_0);
+            handle_count = MAX_EXIT_HANDLES - 1;
+            for (; i < handle_count; ++i) {
+              handles[i] = handles[i + 1];
+            }
+          } else {
+            warning("WaitForMultipleObjects failed in %s: %d\n", __FILE__, __LINE__);
+            // Don't keep handles, if we failed waiting for them.
+            for (i = 0; i < MAX_EXIT_HANDLES; ++i) {
+              CloseHandle(handles[i]);
+            }
+            handle_count = 0;
+          }
+        }
+
+        // Store a duplicate of the current thread handle in the array of handles.
+        hproc = GetCurrentProcess();
+        hthr = GetCurrentThread();
+        if (!DuplicateHandle(hproc, hthr, hproc, &handles[handle_count],
+                             0, FALSE, DUPLICATE_SAME_ACCESS)) {
+          warning("DuplicateHandle failed in %s: %d\n", __FILE__, __LINE__);
+        } else {
+          ++handle_count;
+        }
+
+        // The current exiting thread has stored its handle in the array, and now
+        // should leave the critical section before calling _endthreadex().
+
+      } else { // what != EPT_THREAD
+        if (handle_count > 0) {
+          // Before ending the process, make sure all the threads that had called
+          // _endthreadex() completed.
+          res = WaitForMultipleObjects(handle_count, handles, TRUE, EXIT_TIMEOUT);
+          if (res == WAIT_FAILED) {
+            warning("WaitForMultipleObjects failed in %s: %d\n", __FILE__, __LINE__);
+          }
+          for (i = 0; i < handle_count; ++i) {
+            CloseHandle(handles[i]);
+          }
+          handle_count = 0;
+        }
+
+        // End the process, not leaving critical section.
+        // This makes sure no other thread executes exit-related code at the same
+        // time, thus a race is avoided.
+        if (what == EPT_PROCESS) {
+          ::exit(exit_code);
+        } else {
+          _exit(exit_code);
+        }
+      }
+
+      LeaveCriticalSection(&crit_sect);
     }
   }
 
-  switch (what) {
-  case EPT_THREAD:
+  // We are here if either
+  // - there's no 'race at exit' bug on this OS release;
+  // - initialization of the critical section failed (unlikely);
+  // - the current thread has stored its handle and left the critical section.
+  if (what == EPT_THREAD) {
     _endthreadex((unsigned)exit_code);
-    break;
-
-  case EPT_PROCESS:
+  } else if (what == EPT_PROCESS) {
     ::exit(exit_code);
-    break;
-
-  case EPT_PROCESS_DIE:
+  } else {
     _exit(exit_code);
-    break;
-  }
-
-  // should not reach here
+  }
+
+  // Should not reach here
   return exit_code;
 }
 
-#undef MIN_EXIT_MUTEXES
-#undef MAX_EXIT_MUTEXES
+#undef MAX_EXIT_HANDLES
+#undef EXIT_TIMEOUT
 
 void os::win32::setmode_streams() {
   _setmode(_fileno(stdin), _O_BINARY);