8057744: (process) Synchronize exiting of threads and process [win]
authorigerasim
Wed, 10 Sep 2014 09:52:41 -0700
changeset 26682 f339669ba825
parent 26681 d35d35fbbe43
child 26685 aa239a0dfbea
8057744: (process) Synchronize exiting of threads and process [win] Reviewed-by: dholmes, dcubed, sla
hotspot/src/os/aix/vm/os_aix.inline.hpp
hotspot/src/os/bsd/vm/os_bsd.inline.hpp
hotspot/src/os/linux/vm/os_linux.inline.hpp
hotspot/src/os/solaris/vm/os_solaris.inline.hpp
hotspot/src/os/windows/vm/os_windows.cpp
hotspot/src/os/windows/vm/os_windows.hpp
hotspot/src/os/windows/vm/os_windows.inline.hpp
hotspot/src/share/vm/runtime/java.cpp
hotspot/src/share/vm/runtime/os.hpp
--- a/hotspot/src/os/aix/vm/os_aix.inline.hpp	Wed Sep 10 07:06:37 2014 -0700
+++ b/hotspot/src/os/aix/vm/os_aix.inline.hpp	Wed Sep 10 09:52:41 2014 -0700
@@ -269,4 +269,8 @@
   return true;
 }
 
+inline void os::exit(int num) {
+  ::exit(num);
+}
+
 #endif // OS_AIX_VM_OS_AIX_INLINE_HPP
--- a/hotspot/src/os/bsd/vm/os_bsd.inline.hpp	Wed Sep 10 07:06:37 2014 -0700
+++ b/hotspot/src/os/bsd/vm/os_bsd.inline.hpp	Wed Sep 10 09:52:41 2014 -0700
@@ -274,4 +274,8 @@
 #endif
 }
 
+inline void os::exit(int num) {
+  ::exit(num);
+}
+
 #endif // OS_BSD_VM_OS_BSD_INLINE_HPP
--- a/hotspot/src/os/linux/vm/os_linux.inline.hpp	Wed Sep 10 07:06:37 2014 -0700
+++ b/hotspot/src/os/linux/vm/os_linux.inline.hpp	Wed Sep 10 09:52:41 2014 -0700
@@ -263,4 +263,8 @@
   return Linux::_clock_gettime != NULL;
 }
 
+inline void os::exit(int num) {
+  ::exit(num);
+}
+
 #endif // OS_LINUX_VM_OS_LINUX_INLINE_HPP
--- a/hotspot/src/os/solaris/vm/os_solaris.inline.hpp	Wed Sep 10 07:06:37 2014 -0700
+++ b/hotspot/src/os/solaris/vm/os_solaris.inline.hpp	Wed Sep 10 09:52:41 2014 -0700
@@ -157,4 +157,8 @@
   return true;
 }
 
+inline void os::exit(int num) {
+  ::exit(num);
+}
+
 #endif // OS_SOLARIS_VM_OS_SOLARIS_INLINE_HPP
--- a/hotspot/src/os/windows/vm/os_windows.cpp	Wed Sep 10 07:06:37 2014 -0700
+++ b/hotspot/src/os/windows/vm/os_windows.cpp	Wed Sep 10 09:52:41 2014 -0700
@@ -22,8 +22,8 @@
  *
  */
 
-// Must be at least Windows 2000 or XP to use IsDebuggerPresent
-#define _WIN32_WINNT 0x500
+// Must be at least Windows Vista or Server 2008 to use InitOnceExecuteOnce
+#define _WIN32_WINNT 0x0600
 
 // no precompiled headers
 #include "classfile/classLoader.hpp"
@@ -409,8 +409,6 @@
 
 LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo);
 
-extern jint volatile vm_getting_terminated;
-
 // Thread start routine for all new Java threads
 static unsigned __stdcall java_start(Thread* thread) {
   // Try to randomize the cache line index of hot stack frames.
@@ -432,13 +430,10 @@
     }
   }
 
-  // Diagnostic code to investigate JDK-6573254 (Part I)
-  unsigned res = 90115;  // non-java thread
+  // Diagnostic code to investigate JDK-6573254
+  int res = 90115;  // non-java thread
   if (thread->is_Java_thread()) {
-    JavaThread* java_thread = (JavaThread*)thread;
-    res = java_lang_Thread::is_daemon(java_thread->threadObj())
-          ? 70115        // java daemon thread
-          : 80115;       // java non-daemon thread
+    res = 60115;    // java thread
   }
 
   // Install a win32 structured exception handler around every thread created
@@ -458,12 +453,9 @@
     Atomic::dec_ptr((intptr_t*)&os::win32::_os_thread_count);
   }
 
-  // Diagnostic code to investigate JDK-6573254 (Part II)
-  if (OrderAccess::load_acquire(&vm_getting_terminated)) {
-    return res;
-  }
-
-  return 0;
+  // Thread must not return from exit_process_or_thread(), but if it does,
+  // let it proceed to exit normally
+  return (unsigned)os::win32::exit_process_or_thread(os::win32::EPT_THREAD, res);
 }
 
 static OSThread* create_os_thread(Thread* thread, HANDLE thread_handle, int thread_id) {
@@ -1062,17 +1054,15 @@
 }
 
 
-
-void os::abort(bool dump_core)
-{
+void os::abort(bool dump_core) {
   os::shutdown();
   // no core dump on Windows
-  ::exit(1);
+  win32::exit_process_or_thread(win32::EPT_PROCESS, 1);
 }
 
 // Die immediately, no exit hook, no abort hook, no cleanup.
 void os::die() {
-  _exit(-1);
+  win32::exit_process_or_thread(win32::EPT_PROCESS_DIE, -1);
 }
 
 // Directory routines copied from src/win32/native/java/io/dirent_md.c
@@ -3632,6 +3622,10 @@
 bool   os::win32::_is_windows_2003    = false;
 bool   os::win32::_is_windows_server  = false;
 
+// 6573254
+// Currently, the bug is observed across all the supported Windows releases,
+// including the latest one (as of this writing - Windows Server 2012 R2)
+bool   os::win32::_has_exit_bug       = true;
 bool   os::win32::_has_performance_count = 0;
 
 void os::win32::initialize_system_info() {
@@ -3728,6 +3722,69 @@
   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;
+  return TRUE;
+}
+
+int os::win32::exit_process_or_thread(Ept what, int exit_code) {
+  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__);
+    }
+  }
+
+  switch (what) {
+    case EPT_THREAD:
+      _endthreadex((unsigned)exit_code);
+      break;
+
+    case EPT_PROCESS:
+      ::exit(exit_code);
+      break;
+
+    case EPT_PROCESS_DIE:
+      _exit(exit_code);
+      break;
+  }
+
+  // should not reach here
+  return exit_code;
+}
+
+#undef MIN_EXIT_MUTEXES
+#undef MAX_EXIT_MUTEXES
+
 void os::win32::setmode_streams() {
   _setmode(_fileno(stdin), _O_BINARY);
   _setmode(_fileno(stdout), _O_BINARY);
--- a/hotspot/src/os/windows/vm/os_windows.hpp	Wed Sep 10 07:06:37 2014 -0700
+++ b/hotspot/src/os/windows/vm/os_windows.hpp	Wed Sep 10 09:52:41 2014 -0700
@@ -36,6 +36,7 @@
 
 class win32 {
   friend class os;
+  friend unsigned __stdcall java_start(class Thread*);
 
  protected:
   static int    _vm_page_size;
@@ -47,6 +48,7 @@
   static bool   _is_nt;
   static bool   _is_windows_2003;
   static bool   _is_windows_server;
+  static bool   _has_exit_bug;
   static bool   _has_performance_count;
 
   static void print_windows_version(outputStream* st);
@@ -69,8 +71,12 @@
   // load dll from Windows system directory or Windows directory
   static HINSTANCE load_Windows_dll(const char* name, char *ebuf, int ebuflen);
 
-  private:
-    static void initialize_performance_counter();
+ private:
+  enum Ept { EPT_THREAD, EPT_PROCESS, EPT_PROCESS_DIE };
+  // Wrapper around _endthreadex(), exit() and _exit()
+  static int exit_process_or_thread(Ept what, int exit_code);
+
+  static void initialize_performance_counter();
 
  public:
   // Generic interface:
@@ -88,6 +94,9 @@
   // Tells whether the platform is Windows 2003
   static bool is_windows_2003() { return _is_windows_2003; }
 
+  // Tells whether there can be the race bug during process exit on this platform
+  static bool has_exit_bug() { return _has_exit_bug; }
+
   // Returns the byte size of a virtual memory page
   static int vm_page_size() { return _vm_page_size; }
 
--- a/hotspot/src/os/windows/vm/os_windows.inline.hpp	Wed Sep 10 07:06:37 2014 -0700
+++ b/hotspot/src/os/windows/vm/os_windows.inline.hpp	Wed Sep 10 09:52:41 2014 -0700
@@ -100,6 +100,10 @@
   return win32::_has_performance_count;
 }
 
+inline void os::exit(int num) {
+  win32::exit_process_or_thread(win32::EPT_PROCESS, num);
+}
+
 #define CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(f) \
         os::win32::call_test_func_with_wrapper(f)
 
--- a/hotspot/src/share/vm/runtime/java.cpp	Wed Sep 10 07:06:37 2014 -0700
+++ b/hotspot/src/share/vm/runtime/java.cpp	Wed Sep 10 09:52:41 2014 -0700
@@ -430,8 +430,6 @@
   }
 }
 
-jint volatile vm_getting_terminated = 0;
-
 // Note: before_exit() can be executed only once, if more than one threads
 //       are trying to shutdown the VM at the same time, only one thread
 //       can run before_exit() and all other threads must wait.
@@ -462,8 +460,6 @@
     }
   }
 
-  OrderAccess::release_store(&vm_getting_terminated, 1);
-
   // The only difference between this and Win32's _onexit procs is that
   // this version is invoked before any threads get killed.
   ExitProc* current = exit_procs;
@@ -587,7 +583,7 @@
 void vm_direct_exit(int code) {
   notify_vm_shutdown();
   os::wait_for_keypress_at_exit();
-  ::exit(code);
+  os::exit(code);
 }
 
 void vm_perform_shutdown_actions() {
--- a/hotspot/src/share/vm/runtime/os.hpp	Wed Sep 10 07:06:37 2014 -0700
+++ b/hotspot/src/share/vm/runtime/os.hpp	Wed Sep 10 09:52:41 2014 -0700
@@ -482,8 +482,8 @@
   // run cmd in a separate process and return its exit code; or -1 on failures
   static int fork_and_exec(char *cmd);
 
-  // os::exit() is merged with vm_exit()
-  // static void exit(int num);
+  // Call ::exit() on all platforms but Windows
+  static void exit(int num);
 
   // Terminate the VM, but don't exit the process
   static void shutdown();