aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Haley <aph@redhat.com>2006-10-06 17:00:37 +0000
committerAndrew Haley <aph@redhat.com>2006-10-06 17:00:37 +0000
commit59e1980bc05264cbe73d28338c0073b46a2480bd (patch)
treea5964a6312073cce8c132b12ee5c35058ea37c1f
parentad3ffe3a0c647a91985ad8714584ecb584f56bfd (diff)
2006-10-06 Andrew Haley <aph@redhat.com>
* builtins.c (compareAndSwapInt_builtin): Check that we really do have a compare_and_swap builtin. (compareAndSwapLong_builtin): Likewise. (compareAndSwapObject_builtin): Likewise. 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. git-svn-id: https://gcc.gnu.org/svn/gcc/branches/gcj-eclipse@117510 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/java/ChangeLog7
-rw-r--r--gcc/java/builtins.c99
-rw-r--r--libjava/ChangeLog31
-rw-r--r--libjava/include/jvm.h11
-rw-r--r--libjava/include/posix-threads.h3
-rw-r--r--libjava/java/lang/Thread.java8
-rw-r--r--libjava/java/lang/natThread.cc40
-rw-r--r--libjava/posix-threads.cc123
-rw-r--r--libjava/sun/misc/natUnsafe.cc217
9 files changed, 362 insertions, 177 deletions
diff --git a/gcc/java/ChangeLog b/gcc/java/ChangeLog
index 14b0cbfc3d5..c063ab5e79e 100644
--- a/gcc/java/ChangeLog
+++ b/gcc/java/ChangeLog
@@ -1,3 +1,10 @@
+2006-10-06 Andrew Haley <aph@redhat.com>
+
+ * builtins.c (compareAndSwapInt_builtin): Check that we really do
+ have a compare_and_swap builtin.
+ (compareAndSwapLong_builtin): Likewise.
+ (compareAndSwapObject_builtin): Likewise.
+
2006-10-04 Andrew Haley <aph@redhat.com>
* builtins.c (java_builtins): Add compareAndSwapInt,
diff --git a/gcc/java/builtins.c b/gcc/java/builtins.c
index fe27f04c9da..d8238415d42 100644
--- a/gcc/java/builtins.c
+++ b/gcc/java/builtins.c
@@ -36,7 +36,10 @@ The Free Software Foundation is independent of Sun Microsystems, Inc. */
#include "java-tree.h"
#include <stdarg.h>
#include "convert.h"
-
+#include "rtl.h"
+#include "insn-codes.h"
+#include "expr.h"
+#include "optabs.h"
static tree max_builtin (tree, tree);
static tree min_builtin (tree, tree);
@@ -299,56 +302,75 @@ static tree
compareAndSwapInt_builtin (tree method_return_type ATTRIBUTE_UNUSED,
tree method_arguments)
{
- tree newarglist, addr, stmt;
- UNMARSHAL5 (method_arguments);
+ enum machine_mode mode = TYPE_MODE (int_type_node);
+ if (sync_compare_and_swap_cc[mode] != CODE_FOR_nothing
+ || sync_compare_and_swap[mode] != CODE_FOR_nothing)
+ {
+ tree newarglist, addr, stmt;
+ UNMARSHAL5 (method_arguments);
- addr = build_addr_sum (int_type_node, obj_arg, offset_arg);
+ addr = build_addr_sum (int_type_node, obj_arg, offset_arg);
- newarglist
- = build_arglist_for_builtin (addr, expected_arg, value_arg, NULL_TREE);
- stmt = (build_function_call_expr
- (built_in_decls[BUILT_IN_BOOL_COMPARE_AND_SWAP_4],
- newarglist));
+ newarglist
+ = build_arglist_for_builtin (addr, expected_arg, value_arg, NULL_TREE);
+ stmt = (build_function_call_expr
+ (built_in_decls[BUILT_IN_BOOL_COMPARE_AND_SWAP_4],
+ newarglist));
- return build_check_this (stmt, this_arg);
+ return build_check_this (stmt, this_arg);
+ }
+ return NULL_TREE;
}
static tree
compareAndSwapLong_builtin (tree method_return_type ATTRIBUTE_UNUSED,
tree method_arguments)
{
- tree newarglist, addr, stmt;
- UNMARSHAL5 (method_arguments);
+ enum machine_mode mode = TYPE_MODE (long_type_node);
+ if (sync_compare_and_swap_cc[mode] != CODE_FOR_nothing
+ || sync_compare_and_swap[mode] != CODE_FOR_nothing)
+ {
+ tree newarglist, addr, stmt;
+ UNMARSHAL5 (method_arguments);
- addr = build_addr_sum (long_type_node, obj_arg, offset_arg);
+ addr = build_addr_sum (long_type_node, obj_arg, offset_arg);
- newarglist
- = build_arglist_for_builtin (addr, expected_arg, value_arg, NULL_TREE);
- stmt = (build_function_call_expr
- (built_in_decls[BUILT_IN_BOOL_COMPARE_AND_SWAP_8],
- newarglist));
+ newarglist
+ = build_arglist_for_builtin (addr, expected_arg, value_arg, NULL_TREE);
+ stmt = (build_function_call_expr
+ (built_in_decls[BUILT_IN_BOOL_COMPARE_AND_SWAP_8],
+ newarglist));
- return build_check_this (stmt, this_arg);
+ return build_check_this (stmt, this_arg);
+ }
+ return NULL_TREE;
}
-
static tree
compareAndSwapObject_builtin (tree method_return_type ATTRIBUTE_UNUSED,
tree method_arguments)
{
- tree newarglist, addr, stmt;
- UNMARSHAL5 (method_arguments);
-
- addr = build_addr_sum (value_type, obj_arg, offset_arg);
-
- newarglist
- = build_arglist_for_builtin (addr, expected_arg, value_arg, NULL_TREE);
- stmt = (build_function_call_expr
- (built_in_decls[POINTER_SIZE == 32
- ? BUILT_IN_BOOL_COMPARE_AND_SWAP_4
- : BUILT_IN_BOOL_COMPARE_AND_SWAP_8],
- newarglist));
-
- return build_check_this (stmt, this_arg);
+ enum machine_mode mode = TYPE_MODE (ptr_type_node);
+ if (sync_compare_and_swap_cc[mode] != CODE_FOR_nothing
+ || sync_compare_and_swap[mode] != CODE_FOR_nothing)
+ {
+ tree newarglist, addr, stmt;
+ UNMARSHAL5 (method_arguments);
+
+ int builtin = (POINTER_SIZE == 32
+ ? BUILT_IN_BOOL_COMPARE_AND_SWAP_4
+ : BUILT_IN_BOOL_COMPARE_AND_SWAP_8);
+
+ addr = build_addr_sum (value_type, obj_arg, offset_arg);
+
+ newarglist
+ = build_arglist_for_builtin (addr, expected_arg, value_arg, NULL_TREE);
+ stmt = (build_function_call_expr
+ (built_in_decls[builtin],
+ newarglist));
+
+ return build_check_this (stmt, this_arg);
+ }
+ return NULL_TREE;
}
static tree
@@ -366,7 +388,7 @@ putVolatile_builtin (tree method_return_type ATTRIBUTE_UNUSED,
newarglist = NULL_TREE;
stmt = (build_function_call_expr
(built_in_decls[BUILT_IN_SYNCHRONIZE],
- newarglist));
+ newarglist));
modify_stmt = fold_build2 (MODIFY_EXPR, value_type,
build_java_indirect_ref (value_type, addr,
flag_check_references),
@@ -392,7 +414,7 @@ getVolatile_builtin (tree method_return_type ATTRIBUTE_UNUSED,
newarglist = NULL_TREE;
stmt = (build_function_call_expr
(built_in_decls[BUILT_IN_SYNCHRONIZE],
- newarglist));
+ newarglist));
tmp = build_decl (VAR_DECL, NULL, method_return_type);
DECL_IGNORED_P (tmp) = 1;
@@ -525,7 +547,6 @@ initialize_builtins (void)
boolean_ftype_boolean_boolean,
"__builtin_expect",
BUILTIN_CONST | BUILTIN_NOTHROW);
-
define_builtin (BUILT_IN_BOOL_COMPARE_AND_SWAP_4,
"__sync_bool_compare_and_swap_4",
build_function_type_list (boolean_type_node,
@@ -533,7 +554,6 @@ initialize_builtins (void)
build_pointer_type (int_type_node),
int_type_node, NULL_TREE),
"__sync_bool_compare_and_swap_4", 0);
-
define_builtin (BUILT_IN_BOOL_COMPARE_AND_SWAP_8,
"__sync_bool_compare_and_swap_8",
build_function_type_list (boolean_type_node,
@@ -541,11 +561,10 @@ initialize_builtins (void)
build_pointer_type (long_type_node),
int_type_node, NULL_TREE),
"__sync_bool_compare_and_swap_8", 0);
-
define_builtin (BUILT_IN_SYNCHRONIZE, "__sync_synchronize",
build_function_type (void_type_node, void_list_node),
"__sync_synchronize", BUILTIN_NOTHROW);
-
+
build_common_builtin_nodes ();
}
diff --git a/libjava/ChangeLog b/libjava/ChangeLog
index fe33b0a4bde..dc223a938e4 100644
--- a/libjava/ChangeLog
+++ b/libjava/ChangeLog
@@ -1,3 +1,34 @@
+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.
+
2006-09-13 Andrew Haley <aph@redhat.com>
* Makefile.am: Add sun/reflect/natReflection.cc.
diff --git a/libjava/include/jvm.h b/libjava/include/jvm.h
index 74407bd7401..4f860133d5c 100644
--- a/libjava/include/jvm.h
+++ b/libjava/include/jvm.h
@@ -32,6 +32,8 @@ details. */
#include <java/lang/Thread.h>
+#include <sysdep/locks.h>
+
/* Macro for possible unused arguments. */
#define MAYBE_UNUSED __attribute__((__unused__))
@@ -750,13 +752,20 @@ public:
// 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;
diff --git a/libjava/include/posix-threads.h b/libjava/include/posix-threads.h
index 86a021e4553..2cc30160c12 100644
--- a/libjava/include/posix-threads.h
+++ b/libjava/include/posix-threads.h
@@ -342,4 +342,7 @@ void _Jv_ThreadWait (void);
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__ */
diff --git a/libjava/java/lang/Thread.java b/libjava/java/lang/Thread.java
index e7865caac26..e030aeaec27 100644
--- a/libjava/java/lang/Thread.java
+++ b/libjava/java/lang/Thread.java
@@ -135,7 +135,6 @@ public class Thread implements Runnable
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 @@ public class Thread implements Runnable
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 @@ public class Thread implements Runnable
data = null;
interrupt_flag = false;
- alive_flag = THREAD_DEAD;
startable_flag = true;
if (current != null)
@@ -606,10 +603,7 @@ public class Thread implements Runnable
*
* @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.
diff --git a/libjava/java/lang/natThread.cc b/libjava/java/lang/natThread.cc
index 8ea31ef9421..637f8a2884e 100644
--- a/libjava/java/lang/natThread.cc
+++ b/libjava/java/lang/natThread.cc
@@ -44,6 +44,7 @@ java::lang::Thread::initialize_native (void)
natThread *nt = (natThread *) _Jv_AllocBytes (sizeof (natThread));
state = JV_NEW;
+ nt->alive_flag = THREAD_DEAD;
data = (gnu::gcj::RawDataManaged *) nt;
@@ -104,32 +105,33 @@ java::lang::Thread::holdsLock (jobject obj)
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 @@ java::lang::Thread::sleep (jlong millis, jint nanos)
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 @@ java::lang::Thread::finish_ ()
{
JvSynchronize sync (this);
- alive_flag = THREAD_DEAD;
+ nt->alive_flag = THREAD_DEAD;
state = JV_TERMINATED;
}
@@ -342,10 +344,10 @@ java::lang::Thread::start (void)
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 @@ _Jv_AttachCurrentThread(java::lang::Thread* thread)
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;
}
diff --git a/libjava/posix-threads.cc b/libjava/posix-threads.cc
index db21c93a823..19a7631d64a 100644
--- a/libjava/posix-threads.cc
+++ b/libjava/posix-threads.cc
@@ -327,6 +327,129 @@ _Jv_ThreadInterrupt (_Jv_Thread_t *data)
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)
{
diff --git a/libjava/sun/misc/natUnsafe.cc b/libjava/sun/misc/natUnsafe.cc
index 6f88226ad98..b30f275ad9b 100644
--- a/libjava/sun/misc/natUnsafe.cc
+++ b/libjava/sun/misc/natUnsafe.cc
@@ -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 @@ sun::misc::Unsafe::objectFieldOffset (::java::lang::reflect::Field *field)
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 @@ sun::misc::Unsafe::arrayIndexScale (jclass arrayClass)
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 @@ sun::misc::Unsafe::compareAndSwapObject (jobject obj, jlong offset,
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 @@ void
sun::misc::Unsafe::putLong (jobject obj, jlong offset, jlong value)
{
jlong *addr = (jlong *) ((char *) obj + offset);
+ spinlock lock;
*addr = value;
}
@@ -127,125 +204,45 @@ sun::misc::Unsafe::putObject (jobject obj, jlong offset, jobject value)
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);
}