This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
Re: Patch [ecj]: Refactor Parking Code + Win32 Implementation
- From: Mohan Embar <gnustuff at thisiscool dot com>
- To: Andrew Haley <aph at redhat dot com>
- Cc: java-patches at gcc dot gnu dot org
- Date: Fri, 08 Dec 2006 16:45:56 -0600
- Subject: Re: Patch [ecj]: Refactor Parking Code + Win32 Implementation
- Reply-to: gnustuff at thisiscool dot com
Hi Andrew,
> > Thanks again for reviewing this. If you want me to put the mutex
> > locking and unlocking inside the if statement and rerun my test on
> > FC5, let me know.
>
>That would be interesting. If it works, feel free to commit.
I committed this slightly modified version, which passes the
simple test I emailed earlier. It moves the locking and unlocking
to inside the if statement (and removes an extra semicolon in one
of the Win32 files).
-- Mohan
http://www.thisiscool.com/
http://www.animalsong.org/
2006-12-08 Mohan Embar <gnustuff@thisiscool.com>
* posix-threads.cc (_Jv_ThreadUnpark): Removed.
(ParkHelper::unpark): Ported from _Jv_ThreadUnpark.
(ParkHelper::deactivate): Implemented.
(_Jv_ThreadPark): Removed.
(ParkHelper::park): Ported from _Jv_ThreadPark; moved
mutex locking and unlocking to inside if statement.
* win32-threads.cc (compare_and_exchange): New helper function.
(_Jv_ThreadUnpark, _Jv_ThreadPark): Removed.
(ParkHelper::init): Implemented.
(ParkHelper::init_event): Implemented.
(ParkHelper::deactivate): Implemented.
(ParkHelper::destroy): Implemented.
(ParkHelper::unpark): Implemented.
(ParkHelper::park): Implemented.
* java/lang/natThread.cc (initialize_native): Use ParkHelper
instead of POSIX synchronization constructs.
(finalize_native): Likewise.
(interrupt): Use ParkHelper method instead of _Jv_ThreadUnpark().
(finish_): Use ParkHelper::deactivate().
* include/jvm.h (struct natThread): Use ParkHelper instead of POSIX
synchronization constructs.
* include/posix-threads.h: Include sysdep/locks.h
(_Jv_ThreadUnpark, _Jv_ThreadPark): Removed.
(ParkHelper): New struct.
(ParkHelper::init): Implemented.
(ParkHelper::destroy): Implemented.
* include/win32-threads.h (ParkHelper): New struct.
(TEXT): undefined this macro.
* sun/misc/natUnsafe.cc (unpark): Use ParkHelper instead of
_Jv_ThreadUnpark.
(park): Use ParkHelper instead of _Jv_ThreadPark.
Index: posix-threads.cc
===================================================================
--- posix-threads.cc (revision 119567)
+++ posix-threads.cc (working copy)
@@ -347,13 +347,11 @@
*
* @param thread the thread to unblock.
*/
-
void
-_Jv_ThreadUnpark (::java::lang::Thread *thread)
+ParkHelper::unpark ()
{
using namespace ::java::lang;
- natThread *nt = (natThread *) thread->data;
- volatile obj_addr_t *ptr = &nt->park_permit;
+ volatile obj_addr_t *ptr = &permit;
/* If this thread is in state RUNNING, give it a permit and return
immediately. */
@@ -366,13 +364,22 @@
if (compare_and_swap
(ptr, Thread::THREAD_PARK_PARKED, Thread::THREAD_PARK_RUNNING))
{
- pthread_mutex_lock (&nt->park_mutex);
- pthread_cond_signal (&nt->park_cond);
- pthread_mutex_unlock (&nt->park_mutex);
+ pthread_mutex_lock (&mutex);
+ pthread_cond_signal (&cond);
+ pthread_mutex_unlock (&mutex);
}
}
/**
+ * Sets our state to dead.
+ */
+void
+ParkHelper::deactivate ()
+{
+ permit = ::java::lang::Thread::THREAD_PARK_DEAD;
+}
+
+/**
* 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
@@ -387,14 +394,11 @@
* @param time either the number of nanoseconds to wait, or a time in
* milliseconds from the epoch to wait for.
*/
-
void
-_Jv_ThreadPark (jboolean isAbsolute, jlong time)
+ParkHelper::park (jboolean isAbsolute, jlong time)
{
using namespace ::java::lang;
- Thread *thread = Thread::currentThread();
- natThread *nt = (natThread *) thread->data;
- volatile obj_addr_t *ptr = &nt->park_permit;
+ volatile obj_addr_t *ptr = &permit;
/* If we have a permit, return immediately. */
if (compare_and_swap
@@ -444,22 +448,22 @@
}
}
- pthread_mutex_lock (&nt->park_mutex);
if (compare_and_swap
(ptr, Thread::THREAD_PARK_RUNNING, Thread::THREAD_PARK_PARKED))
{
+ pthread_mutex_lock (&mutex);
if (millis == 0 && nanos == 0)
- pthread_cond_wait (&nt->park_cond, &nt->park_mutex);
+ pthread_cond_wait (&cond, &mutex);
else
- pthread_cond_timedwait (&nt->park_cond, &nt->park_mutex,
- &ts);
+ pthread_cond_timedwait (&cond, &mutex, &ts);
+ pthread_mutex_unlock (&mutex);
+
/* 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_swap
(ptr, Thread::THREAD_PARK_PARKED, Thread::THREAD_PARK_RUNNING);
}
- pthread_mutex_unlock (&nt->park_mutex);
}
static void
Index: win32-threads.cc
===================================================================
--- win32-threads.cc (revision 119567)
+++ win32-threads.cc (working copy)
@@ -61,6 +61,16 @@
#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.
//
@@ -420,6 +430,42 @@
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
@@ -428,11 +474,26 @@
*
* @param thread the thread to unblock.
*/
-
void
-_Jv_ThreadUnpark (::java::lang::Thread *thread)
+ParkHelper::unpark ()
{
- // WRITEME:
+ 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);
+ }
}
/**
@@ -450,9 +511,57 @@
* @param time either the number of nanoseconds to wait, or a time in
* milliseconds from the epoch to wait for.
*/
-
void
-_Jv_ThreadPark (jboolean isAbsolute, jlong time)
+ParkHelper::park (jboolean isAbsolute, jlong time)
{
- // WRITEME:
+ 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);
+ }
}
Index: java/lang/natThread.cc
===================================================================
--- java/lang/natThread.cc (revision 119567)
+++ java/lang/natThread.cc (working copy)
@@ -54,8 +54,7 @@
_Jv_MutexInit (&nt->join_mutex);
_Jv_CondInit (&nt->join_cond);
- pthread_mutex_init (&nt->park_mutex, NULL);
- pthread_cond_init (&nt->park_cond, NULL);
+ nt->park_helper.init();
nt->thread = _Jv_ThreadInitData (this);
// FIXME: if JNI_ENV is set we will want to free it. It is
@@ -75,9 +74,8 @@
_Jv_MutexDestroy (&nt->join_mutex);
#endif
_Jv_FreeJNIEnv((JNIEnv*)nt->jni_env);
-
- pthread_mutex_destroy (&nt->park_mutex);
- pthread_cond_destroy (&nt->park_cond);
+
+ nt->park_helper.destroy();
}
jint
@@ -131,7 +129,7 @@
// Even though we've interrupted this thread, it might still be
// parked.
- _Jv_ThreadUnpark (this);
+ nt->park_helper.unpark ();
}
}
@@ -214,7 +212,7 @@
__sync_synchronize();
natThread *nt = (natThread *) data;
- nt->park_permit = THREAD_PARK_DEAD;
+ nt->park_helper.deactivate ();
group->removeThread (this);
#ifdef ENABLE_JVMPI
Index: include/jvm.h
===================================================================
--- include/jvm.h (revision 119567)
+++ include/jvm.h (working copy)
@@ -774,9 +774,7 @@
_Jv_ConditionVariable_t join_cond;
// These are used by Unsafe.park() and Unsafe.unpark().
- volatile obj_addr_t park_permit;
- pthread_mutex_t park_mutex;
- pthread_cond_t park_cond;
+ ParkHelper park_helper;
// This is private data for the thread system layer.
_Jv_Thread_t *thread;
Index: include/posix-threads.h
===================================================================
--- include/posix-threads.h (revision 119567)
+++ include/posix-threads.h (working copy)
@@ -19,6 +19,7 @@
#include <pthread.h>
#include <sched.h>
+#include <sysdep/locks.h>
//
// Typedefs.
@@ -350,7 +351,33 @@
void _Jv_ThreadInterrupt (_Jv_Thread_t *data);
-void _Jv_ThreadUnpark (::java::lang::Thread *thread);
-void _Jv_ThreadPark (jboolean isAbsolute, jlong time);
+// park() / unpark() support
+struct ParkHelper
+{
+ volatile obj_addr_t permit;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+
+ void init ();
+ void deactivate ();
+ void destroy ();
+ void park (jboolean isAbsolute, jlong time);
+ void unpark ();
+};
+
+inline void
+ParkHelper::init ()
+{
+ pthread_mutex_init (&mutex, NULL);
+ pthread_cond_init (&cond, NULL);
+}
+
+inline void
+ParkHelper::destroy ()
+{
+ pthread_mutex_destroy (&mutex);
+ pthread_cond_destroy (&cond);
+}
+
#endif /* __JV_POSIX_THREADS__ */
Index: include/win32-threads.h
===================================================================
--- include/win32-threads.h (revision 119567)
+++ include/win32-threads.h (working copy)
@@ -193,6 +193,28 @@
// See java/lang/natWin32Process.cc (waitFor) for an example.
HANDLE _Jv_Win32GetInterruptEvent (void);
+// park() / unpark() support
+
+struct ParkHelper
+{
+ // We use LONG instead of obj_addr_t to avoid pulling in locks.h,
+ // which depends on size_t, ...
+ volatile LONG permit;
+
+ // The critical section is used for lazy initialization of our event
+ CRITICAL_SECTION cs;
+ HANDLE event;
+
+ void init ();
+ void deactivate ();
+ void destroy ();
+ void park (jboolean isAbsolute, jlong time);
+ void unpark ();
+
+private:
+ void init_event();
+};
+
// Remove defines from <windows.h> that conflict with various things in libgcj code
#undef TRUE
@@ -204,5 +226,6 @@
#undef interface
#undef STRICT
#undef VOID
+#undef TEXT
#endif /* __JV_WIN32_THREADS__ */
Index: sun/misc/natUnsafe.cc
===================================================================
--- sun/misc/natUnsafe.cc (revision 119567)
+++ sun/misc/natUnsafe.cc (working copy)
@@ -238,11 +238,15 @@
void
sun::misc::Unsafe::unpark (::java::lang::Thread *thread)
{
- _Jv_ThreadUnpark (thread);
+ natThread *nt = (natThread *) thread->data;
+ nt->park_helper.unpark ();
}
void
sun::misc::Unsafe::park (jboolean isAbsolute, jlong time)
{
- _Jv_ThreadPark (isAbsolute, time);
+ using namespace ::java::lang;
+ Thread *thread = Thread::currentThread();
+ natThread *nt = (natThread *) thread->data;
+ nt->park_helper.park (isAbsolute, time);
}