mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 23:34:52 +02:00
8059533: (process) Make exiting process wait for exiting threads [win]
Reviewed-by: dholmes, dcubed
This commit is contained in:
parent
0d3acc8838
commit
14e05b4858
1 changed files with 113 additions and 47 deletions
|
@ -436,9 +436,9 @@ static unsigned __stdcall java_start(Thread* thread) {
|
|||
}
|
||||
|
||||
// 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 @@ HINSTANCE os::win32::load_Windows_dll(const char* name, char *ebuf,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#define MIN_EXIT_MUTEXES 1
|
||||
#define MAX_EXIT_MUTEXES 16
|
||||
#define MAX_EXIT_HANDLES 16
|
||||
#define EXIT_TIMEOUT 1000 /* 1 sec */
|
||||
|
||||
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;
|
||||
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) {
|
||||
if (os::win32::has_exit_bug()) {
|
||||
static INIT_ONCE init_once_muts = INIT_ONCE_STATIC_INIT;
|
||||
static ExitMutexes* pmuts;
|
||||
// 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 (!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__);
|
||||
if (os::win32::has_exit_bug()) {
|
||||
// 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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue