X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libjava%2Fwin32-threads.cc;h=f2dbaaf9454aa7001b93f59f68c0bec8584181fd;hb=d02454a0d2e61b542b55db4f65bb6763d714a922;hp=974f15622a57a2f700cc03a7763e8f26b2317570;hpb=98cf095aceed6a89d0524b8cfbcb11a5ee823c70;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libjava/win32-threads.cc b/libjava/win32-threads.cc index 974f15622a5..f2dbaaf9454 100644 --- a/libjava/win32-threads.cc +++ b/libjava/win32-threads.cc @@ -1,6 +1,7 @@ // win32-threads.cc - interface between libjava and Win32 threads. -/* Copyright (C) 1998, 1999 Red Hat, Inc. +/* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2006 Free Software + Foundation, Inc. This file is part of libgcj. @@ -15,8 +16,9 @@ details. */ #ifdef HAVE_BOEHM_GC extern "C" { -#include #include +// #define's STRICT, which conflicts with Modifier.h +#undef STRICT }; #endif /* HAVE_BOEHM_GC */ @@ -59,53 +61,196 @@ DWORD _Jv_ThreadDataKey; #define FLAG_DAEMON 0x02 // +// Helper +// +inline bool +compare_and_exchange(LONG volatile* dest, LONG cmp, LONG xchg) +{ + return InterlockedCompareExchange((LONG*) dest, xchg, cmp) == cmp; + // Seems like a bug in the MinGW headers that we have to do this cast. +} + +// // Condition variables. // +// we do lazy creation of Events since CreateEvent() is insanely +// expensive, and because the rest of libgcj will call _Jv_CondInit +// when only a mutex is needed. + +inline void +ensure_condvar_initialized(_Jv_ConditionVariable_t *cv) +{ + if (cv->ev[0] == 0) + { + cv->ev[0] = CreateEvent (NULL, 0, 0, NULL); + if (cv->ev[0] == 0) JvFail("CreateEvent() failed"); + + cv->ev[1] = CreateEvent (NULL, 1, 0, NULL); + if (cv->ev[1] == 0) JvFail("CreateEvent() failed"); + } +} + +inline void +ensure_interrupt_event_initialized(HANDLE& rhEvent) +{ + if (!rhEvent) + { + rhEvent = CreateEvent (NULL, 0, 0, NULL); + if (!rhEvent) JvFail("CreateEvent() failed"); + } +} + +// Reimplementation of the general algorithm described at +// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html (isomorphic to +// 3.2, not a cut-and-paste). + int -_Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, jlong millis, jint nanos) +_Jv_CondWait(_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu, jlong millis, jint nanos) { + if (mu->owner != GetCurrentThreadId ( )) + return _JV_NOT_OWNER; + + _Jv_Thread_t *current = _Jv_ThreadCurrentData (); + java::lang::Thread *current_obj = _Jv_ThreadCurrent (); + + // Now that we hold the interrupt mutex, check if this thread has been + // interrupted already. + EnterCriticalSection (¤t->interrupt_mutex); + ensure_interrupt_event_initialized (current->interrupt_event); + jboolean interrupted = current_obj->interrupt_flag; + LeaveCriticalSection (¤t->interrupt_mutex); + + if (interrupted) + { + return _JV_INTERRUPTED; + } + + EnterCriticalSection (&cv->count_mutex); + ensure_condvar_initialized (cv); + cv->blocked_count++; + LeaveCriticalSection (&cv->count_mutex); + DWORD time; - DWORD rval; + if ((millis == 0) && (nanos > 0)) time = 1; + else if (millis == 0) time = INFINITE; + else time = millis; + + // Record the current lock depth, so it can be restored + // when we reacquire it. + int count = mu->refcount; + int curcount = count; + + // Call _Jv_MutexUnlock repeatedly until this thread + // has completely released the monitor. + while (curcount > 0) + { + _Jv_MutexUnlock (mu); + --curcount; + } - // FIXME: check for mutex ownership? + // Set up our array of three events: + // - the auto-reset event (for notify()) + // - the manual-reset event (for notifyAll()) + // - the interrupt event (for interrupt()) + // We wait for any one of these to be signaled. + HANDLE arh[3]; + arh[0] = cv->ev[0]; + arh[1] = cv->ev[1]; + arh[2] = current->interrupt_event; + DWORD rval = WaitForMultipleObjects (3, arh, 0, time); + + EnterCriticalSection (¤t->interrupt_mutex); + + // If we were unblocked by the third event (our thread's interrupt + // event), set the thread's interrupt flag. I think this sanity + // check guards against someone resetting our interrupt flag + // in the time between when interrupt_mutex is released in + // _Jv_ThreadInterrupt and the interval of time between the + // WaitForMultipleObjects call we just made and our acquisition + // of interrupt_mutex. + if (rval == (WAIT_OBJECT_0 + 2)) + current_obj->interrupt_flag = true; + + interrupted = current_obj->interrupt_flag; + LeaveCriticalSection (¤t->interrupt_mutex); + + EnterCriticalSection(&cv->count_mutex); + cv->blocked_count--; + // If we were unblocked by the second event (the broadcast one) + // and nobody is left, then reset the event. + int last_waiter = (rval == (WAIT_OBJECT_0 + 1)) && (cv->blocked_count == 0); + LeaveCriticalSection(&cv->count_mutex); + + if (last_waiter) + ResetEvent (cv->ev[1]); + + // Call _Jv_MutexLock repeatedly until the mutex's refcount is the + // same as before we originally released it. + while (curcount < count) + { + _Jv_MutexLock (mu); + ++curcount; + } + + return interrupted ? _JV_INTERRUPTED : 0; +} - _Jv_MutexUnlock (mu); +void +_Jv_CondInit (_Jv_ConditionVariable_t *cv) +{ + // we do lazy creation of Events since CreateEvent() is insanely expensive + cv->ev[0] = 0; + InitializeCriticalSection (&cv->count_mutex); + cv->blocked_count = 0; +} - if((millis == 0) && (nanos > 0)) - time = 1; - else if(millis == 0) - time = INFINITE; - else - time = millis; +void +_Jv_CondDestroy (_Jv_ConditionVariable_t *cv) +{ + if (cv->ev[0] != 0) + { + CloseHandle (cv->ev[0]); + CloseHandle (cv->ev[1]); - rval = WaitForSingleObject (*cv, time); - _Jv_MutexLock (mu); + cv->ev[0] = 0; + } - if (rval == WAIT_FAILED) - return _JV_NOT_OWNER; // FIXME? - else - return 0; + DeleteCriticalSection (&cv->count_mutex); } -// -// Mutexes. -// +int +_Jv_CondNotify (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu) +{ + if (mu->owner != GetCurrentThreadId ( )) + return _JV_NOT_OWNER; + + EnterCriticalSection (&cv->count_mutex); + ensure_condvar_initialized (cv); + int somebody_is_blocked = cv->blocked_count > 0; + LeaveCriticalSection (&cv->count_mutex); + + if (somebody_is_blocked) + SetEvent (cv->ev[0]); + + return 0; +} int -_Jv_MutexLock (_Jv_Mutex_t *mu) +_Jv_CondNotifyAll (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu) { - DWORD rval; + if (mu->owner != GetCurrentThreadId ( )) + return _JV_NOT_OWNER; - // FIXME: Are Win32 mutexs recursive? Should we use critical section objects - rval = WaitForSingleObject (*mu, INFINITE); + EnterCriticalSection (&cv->count_mutex); + ensure_condvar_initialized (cv); + int somebody_is_blocked = cv->blocked_count > 0; + LeaveCriticalSection (&cv->count_mutex); - if (rval == WAIT_FAILED) - return GetLastError (); // FIXME: Map to errno? - else if (rval == WAIT_TIMEOUT) - return ETIMEDOUT; - else - return 0; + if (somebody_is_blocked) + SetEvent (cv->ev[1]); + + return 0; } // @@ -117,17 +262,20 @@ _Jv_InitThreads (void) { _Jv_ThreadKey = TlsAlloc(); _Jv_ThreadDataKey = TlsAlloc(); - daemon_mutex = CreateMutex(NULL, 0, NULL); - daemon_cond = CreateEvent(NULL, 0, 0, NULL); + daemon_mutex = CreateMutex (NULL, 0, NULL); + daemon_cond = CreateEvent (NULL, 1, 0, NULL); non_daemon_count = 0; } _Jv_Thread_t * _Jv_ThreadInitData (java::lang::Thread* obj) { - _Jv_Thread_t *data = new _Jv_Thread_t; + _Jv_Thread_t *data = (_Jv_Thread_t*)_Jv_Malloc(sizeof(_Jv_Thread_t)); data->flags = 0; + data->handle = 0; data->thread_obj = obj; + data->interrupt_event = 0; + InitializeCriticalSection (&data->interrupt_mutex); return data; } @@ -135,7 +283,11 @@ _Jv_ThreadInitData (java::lang::Thread* obj) void _Jv_ThreadDestroyData (_Jv_Thread_t *data) { - delete data; + DeleteCriticalSection (&data->interrupt_mutex); + if (data->interrupt_event) + CloseHandle(data->interrupt_event); + CloseHandle(data->handle); + _Jv_Free(data); } void @@ -193,7 +345,7 @@ _Jv_ThreadUnRegister () // This function is called when a thread is started. We don't arrange // to call the `run' method directly, because this function must // return a value. -static DWORD __stdcall +static DWORD WINAPI really_start (void* x) { struct starter *info = (struct starter *) x; @@ -207,7 +359,7 @@ really_start (void* x) WaitForSingleObject (daemon_mutex, INFINITE); non_daemon_count--; if (! non_daemon_count) - PulseEvent (daemon_cond); + SetEvent (daemon_cond); ReleaseMutex (daemon_mutex); } @@ -225,7 +377,6 @@ _Jv_ThreadStart (java::lang::Thread *thread, _Jv_Thread_t *data, _Jv_ThreadStart return; data->flags |= FLAG_START; - // FIXME: handle marking the info object for GC. info = (struct starter *) _Jv_AllocBytes (sizeof (struct starter)); info->method = meth; info->data = data; @@ -239,25 +390,178 @@ _Jv_ThreadStart (java::lang::Thread *thread, _Jv_Thread_t *data, _Jv_ThreadStart else data->flags |= FLAG_DAEMON; - HANDLE h = CreateThread(NULL, 0, really_start, info, 0, &id); + data->handle = GC_CreateThread(NULL, 0, really_start, info, 0, &id); _Jv_ThreadSetPriority(data, thread->getPriority()); - - //if (!h) - //JvThrow (); } void _Jv_ThreadWait (void) { - WaitForSingleObject(daemon_mutex, INFINITE); - if(non_daemon_count) - SignalObjectAndWait(daemon_mutex, daemon_cond, INFINITE, 0); - ReleaseMutex(daemon_mutex); + WaitForSingleObject (daemon_mutex, INFINITE); + if (non_daemon_count) + { + ReleaseMutex (daemon_mutex); + WaitForSingleObject (daemon_cond, INFINITE); + } +} + +// +// Interrupt support +// + +HANDLE +_Jv_Win32GetInterruptEvent (void) +{ + _Jv_Thread_t *current = _Jv_ThreadCurrentData (); + EnterCriticalSection (¤t->interrupt_mutex); + ensure_interrupt_event_initialized (current->interrupt_event); + HANDLE hEvent = current->interrupt_event; + LeaveCriticalSection (¤t->interrupt_mutex); + return hEvent; } void _Jv_ThreadInterrupt (_Jv_Thread_t *data) { - MessageBox(NULL, "Unimplemented", "win32-threads.cc:_Jv_ThreadInterrupt", MB_OK); - // FIXME: + EnterCriticalSection (&data->interrupt_mutex); + ensure_interrupt_event_initialized (data->interrupt_event); + data->thread_obj->interrupt_flag = true; + SetEvent (data->interrupt_event); + LeaveCriticalSection (&data->interrupt_mutex); +} + +// park() / unpark() support + +void +ParkHelper::init () +{ + // We initialize our critical section, but not our event. + InitializeCriticalSection (&cs); + event = NULL; +} + +void +ParkHelper::init_event() +{ + EnterCriticalSection (&cs); + if (!event) + { + // Create an auto-reset event. + event = CreateEvent(NULL, 0, 0, NULL); + if (!event) JvFail("CreateEvent() failed"); + } + LeaveCriticalSection (&cs); +} + +void +ParkHelper::deactivate () +{ + permit = ::java::lang::Thread::THREAD_PARK_DEAD; +} + +void +ParkHelper::destroy() +{ + if (event) CloseHandle (event); + DeleteCriticalSection (&cs); +} + +/** + * Releases the block on a thread created by _Jv_ThreadPark(). This + * method can also be used to terminate a blockage caused by a prior + * call to park. This operation is unsafe, as the thread must be + * guaranteed to be live. + * + * @param thread the thread to unblock. + */ +void +ParkHelper::unpark () +{ + using namespace ::java::lang; + LONG volatile* ptr = &permit; + + // If this thread is in state RUNNING, give it a permit and return + // immediately. + if (compare_and_exchange + (ptr, Thread::THREAD_PARK_RUNNING, Thread::THREAD_PARK_PERMIT)) + return; + + // If this thread is parked, put it into state RUNNING and send it a + // signal. + if (compare_and_exchange + (ptr, Thread::THREAD_PARK_PARKED, Thread::THREAD_PARK_RUNNING)) + { + init_event (); + SetEvent (event); + } +} + +/** + * Blocks the thread until a matching _Jv_ThreadUnpark() occurs, the + * thread is interrupted or the optional timeout expires. If an + * unpark call has already occurred, this also counts. A timeout + * value of zero is defined as no timeout. When isAbsolute is true, + * the timeout is in milliseconds relative to the epoch. Otherwise, + * the value is the number of nanoseconds which must occur before + * timeout. This call may also return spuriously (i.e. for no + * apparent reason). + * + * @param isAbsolute true if the timeout is specified in milliseconds from + * the epoch. + * @param time either the number of nanoseconds to wait, or a time in + * milliseconds from the epoch to wait for. + */ +void +ParkHelper::park (jboolean isAbsolute, jlong time) +{ + using namespace ::java::lang; + LONG volatile* ptr = &permit; + + // If we have a permit, return immediately. + if (compare_and_exchange + (ptr, Thread::THREAD_PARK_PERMIT, Thread::THREAD_PARK_RUNNING)) + return; + + // Determine the number of milliseconds to wait. + jlong millis = 0, nanos = 0; + + if (time) + { + if (isAbsolute) + { + millis = time - ::java::lang::System::currentTimeMillis(); + nanos = 0; + } + else + { + millis = 0; + nanos = time; + } + + if (nanos) + { + millis += nanos / 1000000; + if (millis == 0) + millis = 1; + // ...otherwise, we'll block indefinitely. + } + } + + if (millis < 0) return; + // Can this ever happen? + + if (compare_and_exchange + (ptr, Thread::THREAD_PARK_RUNNING, Thread::THREAD_PARK_PARKED)) + { + init_event(); + + DWORD timeout = millis==0 ? INFINITE : (DWORD) millis; + WaitForSingleObject (event, timeout); + + // If we were unparked by some other thread, this will already + // be in state THREAD_PARK_RUNNING. If we timed out, we have to + // do it ourself. + compare_and_exchange + (ptr, Thread::THREAD_PARK_PARKED, Thread::THREAD_PARK_RUNNING); + } }