8057744: (process) Synchronize exiting of threads and process [win]

Reviewed-by: dholmes, dcubed, sla
This commit is contained in:
Ivan Gerasimov 2014-09-10 09:52:41 -07:00
parent 3ad47cdbeb
commit 497f5c44a6
9 changed files with 112 additions and 30 deletions

View file

@ -269,4 +269,8 @@ inline bool os::supports_monotonic_clock() {
return true; return true;
} }
inline void os::exit(int num) {
::exit(num);
}
#endif // OS_AIX_VM_OS_AIX_INLINE_HPP #endif // OS_AIX_VM_OS_AIX_INLINE_HPP

View file

@ -274,4 +274,8 @@ inline bool os::supports_monotonic_clock() {
#endif #endif
} }
inline void os::exit(int num) {
::exit(num);
}
#endif // OS_BSD_VM_OS_BSD_INLINE_HPP #endif // OS_BSD_VM_OS_BSD_INLINE_HPP

View file

@ -263,4 +263,8 @@ inline bool os::supports_monotonic_clock() {
return Linux::_clock_gettime != NULL; return Linux::_clock_gettime != NULL;
} }
inline void os::exit(int num) {
::exit(num);
}
#endif // OS_LINUX_VM_OS_LINUX_INLINE_HPP #endif // OS_LINUX_VM_OS_LINUX_INLINE_HPP

View file

@ -157,4 +157,8 @@ inline bool os::supports_monotonic_clock() {
return true; return true;
} }
inline void os::exit(int num) {
::exit(num);
}
#endif // OS_SOLARIS_VM_OS_SOLARIS_INLINE_HPP #endif // OS_SOLARIS_VM_OS_SOLARIS_INLINE_HPP

View file

@ -22,8 +22,8 @@
* *
*/ */
// Must be at least Windows 2000 or XP to use IsDebuggerPresent // Must be at least Windows Vista or Server 2008 to use InitOnceExecuteOnce
#define _WIN32_WINNT 0x500 #define _WIN32_WINNT 0x0600
// no precompiled headers // no precompiled headers
#include "classfile/classLoader.hpp" #include "classfile/classLoader.hpp"
@ -409,8 +409,6 @@ struct tm* os::localtime_pd(const time_t* clock, struct tm* res) {
LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo); LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo);
extern jint volatile vm_getting_terminated;
// Thread start routine for all new Java threads // Thread start routine for all new Java threads
static unsigned __stdcall java_start(Thread* thread) { static unsigned __stdcall java_start(Thread* thread) {
// Try to randomize the cache line index of hot stack frames. // Try to randomize the cache line index of hot stack frames.
@ -432,13 +430,10 @@ static unsigned __stdcall java_start(Thread* thread) {
} }
} }
// Diagnostic code to investigate JDK-6573254 (Part I) // Diagnostic code to investigate JDK-6573254
unsigned res = 90115; // non-java thread int res = 90115; // non-java thread
if (thread->is_Java_thread()) { if (thread->is_Java_thread()) {
JavaThread* java_thread = (JavaThread*)thread; res = 60115; // java thread
res = java_lang_Thread::is_daemon(java_thread->threadObj())
? 70115 // java daemon thread
: 80115; // java non-daemon thread
} }
// Install a win32 structured exception handler around every thread created // Install a win32 structured exception handler around every thread created
@ -458,12 +453,9 @@ static unsigned __stdcall java_start(Thread* thread) {
Atomic::dec_ptr((intptr_t*)&os::win32::_os_thread_count); Atomic::dec_ptr((intptr_t*)&os::win32::_os_thread_count);
} }
// Diagnostic code to investigate JDK-6573254 (Part II) // Thread must not return from exit_process_or_thread(), but if it does,
if (OrderAccess::load_acquire(&vm_getting_terminated)) { // let it proceed to exit normally
return res; return (unsigned)os::win32::exit_process_or_thread(os::win32::EPT_THREAD, res);
}
return 0;
} }
static OSThread* create_os_thread(Thread* thread, HANDLE thread_handle, int thread_id) { static OSThread* create_os_thread(Thread* thread, HANDLE thread_handle, int thread_id) {
@ -1062,17 +1054,15 @@ void os::check_or_create_dump(void* exceptionRecord, void* contextRecord, char*
} }
void os::abort(bool dump_core) {
void os::abort(bool dump_core)
{
os::shutdown(); os::shutdown();
// no core dump on Windows // 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. // Die immediately, no exit hook, no abort hook, no cleanup.
void os::die() { 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 // Directory routines copied from src/win32/native/java/io/dirent_md.c
@ -3632,6 +3622,10 @@ bool os::win32::_is_nt = false;
bool os::win32::_is_windows_2003 = false; bool os::win32::_is_windows_2003 = false;
bool os::win32::_is_windows_server = 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; bool os::win32::_has_performance_count = 0;
void os::win32::initialize_system_info() { void os::win32::initialize_system_info() {
@ -3728,6 +3722,69 @@ HINSTANCE os::win32::load_Windows_dll(const char* name, char *ebuf, int ebuflen)
return NULL; 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() { void os::win32::setmode_streams() {
_setmode(_fileno(stdin), _O_BINARY); _setmode(_fileno(stdin), _O_BINARY);
_setmode(_fileno(stdout), _O_BINARY); _setmode(_fileno(stdout), _O_BINARY);

View file

@ -36,6 +36,7 @@ static const char* path_separator() { return ";"; }
class win32 { class win32 {
friend class os; friend class os;
friend unsigned __stdcall java_start(class Thread*);
protected: protected:
static int _vm_page_size; static int _vm_page_size;
@ -47,6 +48,7 @@ class win32 {
static bool _is_nt; static bool _is_nt;
static bool _is_windows_2003; static bool _is_windows_2003;
static bool _is_windows_server; static bool _is_windows_server;
static bool _has_exit_bug;
static bool _has_performance_count; static bool _has_performance_count;
static void print_windows_version(outputStream* st); static void print_windows_version(outputStream* st);
@ -69,8 +71,12 @@ class win32 {
// load dll from Windows system directory or Windows directory // load dll from Windows system directory or Windows directory
static HINSTANCE load_Windows_dll(const char* name, char *ebuf, int ebuflen); static HINSTANCE load_Windows_dll(const char* name, char *ebuf, int ebuflen);
private: private:
static void initialize_performance_counter(); 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: public:
// Generic interface: // Generic interface:
@ -88,6 +94,9 @@ class win32 {
// Tells whether the platform is Windows 2003 // Tells whether the platform is Windows 2003
static bool is_windows_2003() { return _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 // Returns the byte size of a virtual memory page
static int vm_page_size() { return _vm_page_size; } static int vm_page_size() { return _vm_page_size; }

View file

@ -100,6 +100,10 @@ inline bool os::supports_monotonic_clock() {
return win32::_has_performance_count; 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) \ #define CALL_TEST_FUNC_WITH_WRAPPER_IF_NEEDED(f) \
os::win32::call_test_func_with_wrapper(f) os::win32::call_test_func_with_wrapper(f)

View file

@ -430,8 +430,6 @@ extern "C" {
} }
} }
jint volatile vm_getting_terminated = 0;
// Note: before_exit() can be executed only once, if more than one threads // 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 // are trying to shutdown the VM at the same time, only one thread
// can run before_exit() and all other threads must wait. // can run before_exit() and all other threads must wait.
@ -462,8 +460,6 @@ void before_exit(JavaThread * thread) {
} }
} }
OrderAccess::release_store(&vm_getting_terminated, 1);
// The only difference between this and Win32's _onexit procs is that // The only difference between this and Win32's _onexit procs is that
// this version is invoked before any threads get killed. // this version is invoked before any threads get killed.
ExitProc* current = exit_procs; ExitProc* current = exit_procs;
@ -587,7 +583,7 @@ void notify_vm_shutdown() {
void vm_direct_exit(int code) { void vm_direct_exit(int code) {
notify_vm_shutdown(); notify_vm_shutdown();
os::wait_for_keypress_at_exit(); os::wait_for_keypress_at_exit();
::exit(code); os::exit(code);
} }
void vm_perform_shutdown_actions() { void vm_perform_shutdown_actions() {

View file

@ -482,8 +482,8 @@ class os: AllStatic {
// run cmd in a separate process and return its exit code; or -1 on failures // run cmd in a separate process and return its exit code; or -1 on failures
static int fork_and_exec(char *cmd); static int fork_and_exec(char *cmd);
// os::exit() is merged with vm_exit() // Call ::exit() on all platforms but Windows
// static void exit(int num); static void exit(int num);
// Terminate the VM, but don't exit the process // Terminate the VM, but don't exit the process
static void shutdown(); static void shutdown();