This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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)
 {


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]