This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
Re: [ecj] More sun.misc.Unsafe stuff
This is the new, improved, portable version of sun/misc/natUnsafe.cc.
We use gcj's portable locks everywhere, and I've moved all the
POSIXisms into posix-thread.cc. As a result, this should work on all
the systems we have, given appropriate versions of park() and unpark()
for mon-POSIX systems.
2006-10-06 Andrew Haley <aph@redhat.com>
* posix-threads.cc (_Jv_ThreadUnpark, _Jv_ThreadPark): Moved here
from sun/misc/natUnsafe.cc.
* sun/misc/natUnsafe.cc (class spinlock): New class.
(compareAndSwap): New methods.
(compareAndSwapInt, compareAndSwapLong, compareAndSwapObject)
(putOrderedLong, putLongVolatile, putObjectVolatile, putLong)
(getIntVolatile, getObjectVolatile, getLong, getLongVolatile):
Rewrite to use gcj's own atomic functions rather than gcc
builtins.
(unpark): Moved to posix-threads.cc
(park): Likewise.
* include/jvm.h (struct natThread::alive_flag): Moved here from
Thread.java.
(struct natThread): Likewise.
* include/posix-threads.h: (_Jv_ThreadUnpark, _Jv_ThreadPark):
moved here from sun/misc/natUnsafe.cc.
* java/lang/natThread.cc (initialize_native): Set alive_flag here.
(isAlive): Moved here from Thread.java.
(interrupt): alive_flag is now in the natThread structure.
(interrupt): Call _Jv_ThreadUnpark().
(finish_): parkPermit and alive_flag are now in the natThread
structure.
(start): LIkewise.
(_Jv_AttachCurrentThread): Likewise.
* java/lang/Thread.java (alive_flag): Remove.
(parkPermit): Likewise.
(Thread): Don't set alive_flag.
(isAlive): Make native.
Index: java/lang/Thread.java
===================================================================
--- java/lang/Thread.java (revision 117306)
+++ java/lang/Thread.java (working copy)
@@ -135,7 +135,6 @@
private static final byte THREAD_DEAD = 0;
private static final byte THREAD_ALIVE = 1;
private static final byte THREAD_SIGNALED = 2;
- private volatile byte alive_flag;
private boolean startable_flag;
@@ -171,7 +170,6 @@
static final byte THREAD_PARK_PERMIT = 1;
static final byte THREAD_PARK_PARKED = 2;
static final byte THREAD_PARK_DEAD = 3;
- byte parkPermit;
// This describes the top-most interpreter frame for this thread.
RawData interp_frame;
@@ -395,7 +393,6 @@
data = null;
interrupt_flag = false;
- alive_flag = THREAD_DEAD;
startable_flag = true;
if (current != null)
@@ -606,10 +603,7 @@
*
* @return whether this Thread is alive
*/
- public final synchronized boolean isAlive()
- {
- return alive_flag != THREAD_DEAD;
- }
+ public final native boolean isAlive();
/**
* Tell whether this is a daemon Thread or not.
Index: java/lang/natThread.cc
===================================================================
--- java/lang/natThread.cc (revision 117306)
+++ java/lang/natThread.cc (working copy)
@@ -44,6 +44,7 @@
natThread *nt = (natThread *) _Jv_AllocBytes (sizeof (natThread));
state = JV_NEW;
+ nt->alive_flag = THREAD_DEAD;
data = (gnu::gcj::RawDataManaged *) nt;
@@ -104,32 +105,33 @@
return !_Jv_ObjectCheckMonitor (obj);
}
+jboolean
+java::lang::Thread::isAlive (void)
+{
+ natThread *nt = (natThread *) data;
+ return nt->alive_flag != (obj_addr_t)THREAD_DEAD;
+}
+
void
java::lang::Thread::interrupt (void)
{
checkAccess ();
+ natThread *nt = (natThread *) data;
+
// If a thread is in state ALIVE, we atomically set it to state
// SIGNALED and send it a signal. Once we've sent it the signal, we
// set its state back to ALIVE.
- if (__sync_bool_compare_and_swap
- (&alive_flag, Thread::THREAD_ALIVE, Thread::THREAD_SIGNALED))
+ if (compare_and_swap
+ (&nt->alive_flag, Thread::THREAD_ALIVE, Thread::THREAD_SIGNALED))
{
- natThread *nt = (natThread *) data;
-
_Jv_ThreadInterrupt (nt->thread);
- __sync_bool_compare_and_swap
- (&alive_flag, THREAD_SIGNALED, Thread::THREAD_ALIVE);
+ compare_and_swap
+ (&nt->alive_flag, THREAD_SIGNALED, Thread::THREAD_ALIVE);
// Even though we've interrupted this thread, it might still be
// parked.
- if (__sync_bool_compare_and_swap
- (&parkPermit, 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);
- }
+ _Jv_ThreadUnpark (this);
}
}
@@ -209,10 +211,10 @@
void
java::lang::Thread::finish_ ()
{
- parkPermit = THREAD_PARK_DEAD;
__sync_synchronize();
natThread *nt = (natThread *) data;
+ nt->park_permit = THREAD_PARK_DEAD;
group->removeThread (this);
#ifdef ENABLE_JVMPI
@@ -240,7 +242,7 @@
{
JvSynchronize sync (this);
- alive_flag = THREAD_DEAD;
+ nt->alive_flag = THREAD_DEAD;
state = JV_TERMINATED;
}
@@ -342,10 +344,10 @@
if (!startable_flag)
throw new IllegalThreadStateException;
- alive_flag = THREAD_ALIVE;
+ natThread *nt = (natThread *) data;
+ nt->alive_flag = THREAD_ALIVE;
startable_flag = false;
state = JV_RUNNABLE;
- natThread *nt = (natThread *) data;
_Jv_ThreadStart (this, nt->thread, (_Jv_ThreadStartFunc *) &_Jv_ThreadRun);
}
@@ -455,9 +457,9 @@
if (thread == NULL || thread->startable_flag == false)
return -1;
thread->startable_flag = false;
- thread->alive_flag = ::java::lang::Thread::THREAD_ALIVE;
- thread->state = JV_RUNNABLE;
natThread *nt = (natThread *) thread->data;
+ nt->alive_flag = ::java::lang::Thread::THREAD_ALIVE;
+ thread->state = JV_RUNNABLE;
_Jv_ThreadRegister (nt->thread);
return 0;
}
Index: include/posix-threads.h
===================================================================
--- include/posix-threads.h (revision 117306)
+++ include/posix-threads.h (working copy)
@@ -342,4 +342,7 @@
void _Jv_ThreadInterrupt (_Jv_Thread_t *data);
+void _Jv_ThreadUnpark (::java::lang::Thread *thread);
+void _Jv_ThreadPark (jboolean isAbsolute, jlong time);
+
#endif /* __JV_POSIX_THREADS__ */
Index: include/jvm.h
===================================================================
--- include/jvm.h (revision 117306)
+++ include/jvm.h (working copy)
@@ -32,6 +32,8 @@
#include <java/lang/Thread.h>
+#include <sysdep/locks.h>
+
/* Macro for possible unused arguments. */
#define MAYBE_UNUSED __attribute__((__unused__))
@@ -750,13 +752,20 @@
// the Thread class.
struct natThread
{
+ // A thread is either alive, dead, or being sent a signal; if it is
+ // being sent a signal, it is also alive. Thus, if you want to know
+ // if a thread is alive, it is sufficient to test alive_status !=
+ // THREAD_DEAD.
+ volatile obj_addr_t alive_flag;
+
// These are used to interrupt sleep and join calls. We can share a
// condition variable here since it only ever gets notified when the thread
// exits.
_Jv_Mutex_t join_mutex;
_Jv_ConditionVariable_t join_cond;
- // These are used by Unsafe.park() and Unsafe.unpark().
+ // 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;
Index: sun/misc/natUnsafe.cc
===================================================================
--- sun/misc/natUnsafe.cc (revision 117306)
+++ sun/misc/natUnsafe.cc (working copy)
@@ -1,3 +1,14 @@
+// natUnsafe.cc - Implementation of sun.misc.Unsafe native methods.
+
+/* Copyright (C) 2006
+ Free Software Foundation
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+
#include <gcj/cni.h>
#include <gcj/field.h>
#include <gcj/javaprims.h>
@@ -9,6 +20,64 @@
#include <java/lang/Thread.h>
#include <java/lang/Long.h>
+#include "sysdep/locks.h"
+
+// Use a spinlock for multi-word accesses
+class spinlock
+{
+ static volatile obj_addr_t lock;
+
+public:
+
+spinlock ()
+ {
+ while (! compare_and_swap (&lock, 0, 1));
+ }
+ ~spinlock ()
+ {
+ release_set (&lock, 0);
+ }
+};
+
+// This is a single lock that is used for all synchronized accesses if
+// the compiler can't generate inline compare-and-swap operations. In
+// most cases it'll never be used, but the i386 needs it for 64-bit
+// locked accesses and so does PPC32. It's worth building libgcj with
+// target=i486 (or above) to get the inlines.
+volatile obj_addr_t spinlock::lock;
+
+
+static inline bool
+compareAndSwap (volatile jint *addr, jint old, jint new_val)
+{
+ jboolean result = false;
+ spinlock lock;
+ if ((result = (*addr == old)))
+ *addr = new_val;
+ return result;
+}
+
+static inline bool
+compareAndSwap (volatile jlong *addr, jlong old, jlong new_val)
+{
+ jboolean result = false;
+ spinlock lock;
+ if ((result = (*addr == old)))
+ *addr = new_val;
+ return result;
+}
+
+static inline bool
+compareAndSwap (volatile jobject *addr, jobject old, jobject new_val)
+{
+ jboolean result = false;
+ spinlock lock;
+ if ((result = (*addr == old)))
+ *addr = new_val;
+ return result;
+}
+
+
jlong
sun::misc::Unsafe::objectFieldOffset (::java::lang::reflect::Field *field)
{
@@ -17,14 +86,6 @@
return fld->getOffset();
}
-jboolean
-sun::misc::Unsafe::compareAndSwapInt (jobject obj, jlong offset,
- jint expect, jint update)
-{
- jint *addr = (jint *)((char *)obj + offset);
- return __sync_bool_compare_and_swap (addr, expect, update);
-}
-
jint
sun::misc::Unsafe::arrayBaseOffset (jclass arrayClass)
{
@@ -43,12 +104,23 @@
return sizeof (void *);
}
+// These methods are used when the compiler fails to generate inline
+// versions of the compare-and-swap primitives.
+
+jboolean
+sun::misc::Unsafe::compareAndSwapInt (jobject obj, jlong offset,
+ jint expect, jint update)
+{
+ jint *addr = (jint *)((char *)obj + offset);
+ return compareAndSwap (addr, expect, update);
+}
+
jboolean
sun::misc::Unsafe::compareAndSwapLong (jobject obj, jlong offset,
jlong expect, jlong update)
{
- jlong *addr = (jlong*)((char *) obj + offset);
- return __sync_bool_compare_and_swap (addr, expect, update);
+ volatile jlong *addr = (jlong*)((char *) obj + offset);
+ return compareAndSwap (addr, expect, update);
}
jboolean
@@ -56,48 +128,52 @@
jobject expect, jobject update)
{
jobject *addr = (jobject*)((char *) obj + offset);
- return __sync_bool_compare_and_swap (addr, expect, update);
+ return compareAndSwap (addr, expect, update);
}
void
sun::misc::Unsafe::putOrderedInt (jobject obj, jlong offset, jint value)
{
- jint *addr = (jint *) ((char *) obj + offset);
+ volatile jint *addr = (jint *) ((char *) obj + offset);
*addr = value;
}
void
sun::misc::Unsafe::putOrderedLong (jobject obj, jlong offset, jlong value)
{
- jlong *addr = (jlong *) ((char *) obj + offset);
+ volatile jlong *addr = (jlong *) ((char *) obj + offset);
+ spinlock lock;
*addr = value;
}
void
sun::misc::Unsafe::putOrderedObject (jobject obj, jlong offset, jobject value)
{
- jobject *addr = (jobject *) ((char *) obj + offset);
+ volatile jobject *addr = (jobject *) ((char *) obj + offset);
*addr = value;
}
void
sun::misc::Unsafe::putIntVolatile (jobject obj, jlong offset, jint value)
{
- jint *addr = (jint *) ((char *) obj + offset);
+ write_barrier ();
+ volatile jint *addr = (jint *) ((char *) obj + offset);
*addr = value;
}
void
sun::misc::Unsafe::putLongVolatile (jobject obj, jlong offset, jlong value)
{
- jlong *addr = (jlong *) ((char *) obj + offset);
+ volatile jlong *addr = (jlong *) ((char *) obj + offset);
+ spinlock lock;
*addr = value;
}
void
sun::misc::Unsafe::putObjectVolatile (jobject obj, jlong offset, jobject value)
{
- jobject *addr = (jobject *) ((char *) obj + offset);
+ write_barrier ();
+ volatile jobject *addr = (jobject *) ((char *) obj + offset);
*addr = value;
}
@@ -114,6 +190,7 @@
sun::misc::Unsafe::putLong (jobject obj, jlong offset, jlong value)
{
jlong *addr = (jlong *) ((char *) obj + offset);
+ spinlock lock;
*addr = value;
}
@@ -127,125 +204,45 @@
jint
sun::misc::Unsafe::getIntVolatile (jobject obj, jlong offset)
{
- jint *addr = (jint *) ((char *) obj + offset);
- return *addr;
+ volatile jint *addr = (jint *) ((char *) obj + offset);
+ jint result = *addr;
+ read_barrier ();
+ return result;
}
jobject
sun::misc::Unsafe::getObjectVolatile (jobject obj, jlong offset)
{
- jobject *addr = (jobject *) ((char *) obj + offset);
- return *addr;
+ volatile jobject *addr = (jobject *) ((char *) obj + offset);
+ jobject result = *addr;
+ read_barrier ();
+ return result;
}
jlong
sun::misc::Unsafe::getLong (jobject obj, jlong offset)
{
jlong *addr = (jlong *) ((char *) obj + offset);
+ spinlock lock;
return *addr;
}
jlong
sun::misc::Unsafe::getLongVolatile (jobject obj, jlong offset)
{
- jlong *addr = (jlong *) ((char *) obj + offset);
+ volatile jlong *addr = (jlong *) ((char *) obj + offset);
+ spinlock lock;
return *addr;
}
void
sun::misc::Unsafe::unpark (::java::lang::Thread *thread)
{
- using namespace ::java::lang;
- volatile jbyte *ptr = &thread->parkPermit;
-
- /* If this thread is in state RUNNING, give it a permit and return
- immediately. */
- if (__sync_bool_compare_and_swap
- (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 (__sync_bool_compare_and_swap
- (ptr, Thread::THREAD_PARK_PARKED, Thread::THREAD_PARK_RUNNING))
- {
- natThread *nt = (natThread *) thread->data;
- pthread_mutex_lock (&nt->park_mutex);
- pthread_cond_signal (&nt->park_cond);
- pthread_mutex_unlock (&nt->park_mutex);
- }
+ _Jv_ThreadUnpark (thread);
}
void
sun::misc::Unsafe::park (jboolean isAbsolute, jlong time)
{
- using namespace ::java::lang;
- Thread *thread = Thread::currentThread();
- volatile jbyte *ptr = &thread->parkPermit;
-
- /* If we have a permit, return immediately. */
- if (__sync_bool_compare_and_swap
- (ptr, Thread::THREAD_PARK_PERMIT, Thread::THREAD_PARK_RUNNING))
- return;
-
- struct timespec ts;
- jlong millis = 0, nanos = 0;
-
- if (time)
- {
- if (isAbsolute)
- {
- millis = time;
- nanos = 0;
- }
- else
- {
- millis = java::lang::System::currentTimeMillis();
- nanos = time;
- }
-
- if (millis > 0 || nanos > 0)
- {
- // Calculate the abstime corresponding to the timeout.
- // Everything is in milliseconds.
- //
- // We use `unsigned long long' rather than jlong because our
- // caller may pass up to Long.MAX_VALUE millis. This would
- // overflow the range of a timespec.
-
- unsigned long long m = (unsigned long long)millis;
- unsigned long long seconds = m / 1000;
-
- ts.tv_sec = seconds;
- if (ts.tv_sec < 0 || (unsigned long long)ts.tv_sec != seconds)
- {
- // We treat a timeout that won't fit into a struct timespec
- // as a wait forever.
- millis = nanos = 0;
- }
- else
- {
- m %= 1000;
- ts.tv_nsec = m * 1000000 + (unsigned long long)nanos;
- }
- }
- }
-
- natThread *nt = (natThread *) thread->data;
- pthread_mutex_lock (&nt->park_mutex);
- if (__sync_bool_compare_and_swap
- (ptr, Thread::THREAD_PARK_RUNNING, Thread::THREAD_PARK_PARKED))
- {
- if (millis == 0 && nanos == 0)
- pthread_cond_wait (&nt->park_cond, &nt->park_mutex);
- else
- pthread_cond_timedwait (&nt->park_cond, &nt->park_mutex,
- &ts);
- /* 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. */
- __sync_bool_compare_and_swap
- (ptr, Thread::THREAD_PARK_PARKED, Thread::THREAD_PARK_RUNNING);
- }
- pthread_mutex_unlock (&nt->park_mutex);
+ _Jv_ThreadPark (isAbsolute, time);
}
Index: posix-threads.cc
===================================================================
--- posix-threads.cc (revision 117306)
+++ posix-threads.cc (working copy)
@@ -327,6 +327,129 @@
pthread_mutex_unlock (&data->wait_mutex);
}
+/**
+ * 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
+_Jv_ThreadUnpark (::java::lang::Thread *thread)
+{
+ using namespace ::java::lang;
+ natThread *nt = (natThread *) thread->data;
+ volatile obj_addr_t *ptr = &nt->park_permit;
+
+ /* If this thread is in state RUNNING, give it a permit and return
+ immediately. */
+ if (compare_and_swap
+ (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_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);
+ }
+}
+
+/**
+ * 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
+_Jv_ThreadPark (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;
+
+ /* If we have a permit, return immediately. */
+ if (compare_and_swap
+ (ptr, Thread::THREAD_PARK_PERMIT, Thread::THREAD_PARK_RUNNING))
+ return;
+
+ struct timespec ts;
+ jlong millis = 0, nanos = 0;
+
+ if (time)
+ {
+ if (isAbsolute)
+ {
+ millis = time;
+ nanos = 0;
+ }
+ else
+ {
+ millis = java::lang::System::currentTimeMillis();
+ nanos = time;
+ }
+
+ if (millis > 0 || nanos > 0)
+ {
+ // Calculate the abstime corresponding to the timeout.
+ // Everything is in milliseconds.
+ //
+ // We use `unsigned long long' rather than jlong because our
+ // caller may pass up to Long.MAX_VALUE millis. This would
+ // overflow the range of a timespec.
+
+ unsigned long long m = (unsigned long long)millis;
+ unsigned long long seconds = m / 1000;
+
+ ts.tv_sec = seconds;
+ if (ts.tv_sec < 0 || (unsigned long long)ts.tv_sec != seconds)
+ {
+ // We treat a timeout that won't fit into a struct timespec
+ // as a wait forever.
+ millis = nanos = 0;
+ }
+ else
+ {
+ m %= 1000;
+ ts.tv_nsec = m * 1000000 + (unsigned long long)nanos;
+ }
+ }
+ }
+
+ pthread_mutex_lock (&nt->park_mutex);
+ if (compare_and_swap
+ (ptr, Thread::THREAD_PARK_RUNNING, Thread::THREAD_PARK_PARKED))
+ {
+ if (millis == 0 && nanos == 0)
+ pthread_cond_wait (&nt->park_cond, &nt->park_mutex);
+ else
+ pthread_cond_timedwait (&nt->park_cond, &nt->park_mutex,
+ &ts);
+ /* 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
handle_intr (int)
{