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]

Patch: hash synchronization


At long last I am going to check in the hash synchronization patch.

`Hash synchronization' is when we keep the per-object synchronization
information in a global hash table instead of spending one word per
object for the reference.  This patch also includes other related
things:

* _Jv_MonitorEnter and _Jv_MonitorExit now return `void' and throw an
  exception if something goes wrong

* Thin locks are used on some platforms.  This provides a substantial
  performance improvement.

* Pointer free object allocations are now possible
  (I believe we still want to change the compiler to emit a different
  allocation call in this case, as well as in the case of no finalizer)

* A faster replacement for pthread_self() is available on some
  platforms

You'll see my name on part of the ChangeLog but don't be fooled: I
merely wrote some configury code and changed some of the indentation.
All the real work here is thank to Hans Boehm.  Hans, thanks a lot for
doing this!  I think this is a very important and significant patch.
(BTW, Hans: due to my changes this is a bit different from the patch
you sent me.  Hopefully this won't make updating too hard for you.)

I've set things up so that hash synchronization is enabled by default
on platforms where it works (and is believed to be a win -- x86 and
IA-64).

As this is a rather large and very complex patch, it is possible that
there will be problems with it.  Please report them.

I am only checking this in on the trunk.  Or, rather, I will check it
in as soon as my current (post-update) build finishes.  I have run the
entire test suite before with this patch with no regressions.


One somewhat ugly detail is the existence and placement of
libgcj-config.h.  This header, or the moral equivalent, is needed
because the new define JV_HASH_SYNCHRONIZATION affects the layout of
Object.  As such this define must be visible to all CNI-using
programs.

Currently I've installed this in $(includedir).  This is wrong because
the header is platform-dependent.

What are our options?  One option would be to use something like
pkg-config.  Another option, since our library is part of the
compiler, would be to install the system-dependent header into
$(exec_prefix)/$(target)/include/.  I think this would do the right
thing.  However I wanted to ask to see if anybody knows whether there
is a drawback to doing that.

In the meantime I have left it in includedir as it won't hurt us in
the short term.


2001-05-23  Tom Tromey  <tromey@redhat.com>

	* posix-threads.cc (_Jv_self_cache): Renamed from self_cache.
	* gcj/Makefile.in: Rebuilt.
	* gcj/Makefile.am (gcj_HEADERS): Added libgcj-config.h.
	* gcj/javaprims.h: Include gcj/libgcj-config.h.
	* gcj/libgcj-config.h.in: New file.
	* libgcj.spec.in (*jc1): Added @HASH_SYNC_SPEC@.
	* configure: Rebuilt.
	* configure.in: Enable hash synchronization by default on some
	platforms.
	(HASH_SYNC_SPEC): New subst.
	(AC_CONFIG_HEADER): Added gcj/libgcj-config.h.
	Correctly use `test -z' instead of `test -n' in a couple places.
	(JV_HASH_SYNCHRONIZATION): Use AC_DEFINE; don't add to
	LIBGCJ_CXXFLAGS.
	* configure.host (enable_java_net_default): Initialize.
	(enable_hash_synchronization_default): New variable.

2001-05-23  Hans Boehm <Hans_Boehm@hp.com>

	* boehm.cc (_Jv_MarkObj): Don't mark sync_info when hash
	synchronization in use.
	(_Jv_MarkArray): Likewise.
	(_Jv_AllocBytes): Don't check return result.
	(handle_out_of_memory): New function.
	(_Jv_InitGC): Set GC_oom_fn.
	(trace_one_vtable): New global.
	(_Jv_AllocTraceOne): New function.
	* configure.in: Added --enable-hash-synchronization.
	* defineclass.cc, prims.cc, resolve.cc, java/lang/natString.cc,
	java/net/natInetAddress.cc: Remove _Jv_AllocBytesChecked.
	* nogc.cc (_Jv_AllocObj): Throw out-of-memory.
	(_Jv_AllocArray): Likewise.
	(_Jv_AllocBytes): Likewise.
	(_Jv_AllocPtrFreeObject): New function.
	(_Jv_AllocTraceOne): Likewise.
	* posix-threads.cc (_Jv_ThreadRegister): Handle slow
	pthread_self().
	(self_cache): New global.
	(_Jv_ThreadSelf_out_of_line): New function.
	* prims.cc (_Jv_AllocBytesChecked): Removed.
	(_Jv_ThrowNoMemory): New function.
	(_Jv_AllocObject): Don't check for null return from allocator.
	(_Jv_NewObjectArray): Likewise.
	(_Jv_AllocPtrFreeObject): New function.
	(_Jv_NewPrimArray): Allocate pointer-free object if possible.
	* include/javaprims.h (_Jv_AllocPtrFreeObject): Declare.
	(_Jv_MonitorEnter, _Jv_MonitorExit): Don't return value.
	* include/boehm-gc.h (_Jv_AllocObj): Define.
	(_Jv_AllocPtrFreeObj): Define.
	* include/jvm.h (_Jv_AllocPtrFreeObj): Declare.
	(_Jv_ThrowNoMemory): Declare.
	(_Jv_AllocTraceOne): Declare.
	(_Jv_AllocBytesChecked): Removed.
	* include/posix-threads.h (_Jv_MutexInit, _Jv_MutexLock,
	_Jv_MutexUnlock): Handle LOCK_DEBUG.
	(_Jv_ThreadSelf): Handle case where system pthread_self() is
	slow.
	* java/lang/Class.h (Class): Declare _Jv_AllocPtrFreeObj as
	friend.
	* java/lang/Object.h (sync_info): Conditional upon presence of
	hash synchronization.
	* java/lang/natObject.cc: Much new code to handle thin locks and
	hash synchronization.
	* java/lang/natString.cc (_Jv_AllocString): Allocate pointer-free
	object if possible.


Tom

Index: boehm.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/boehm.cc,v
retrieving revision 1.24
diff -u -r1.24 boehm.cc
--- boehm.cc	2001/05/21 08:37:04	1.24
+++ boehm.cc	2001/05/24 01:06:05
@@ -1,6 +1,6 @@
 // boehm.cc - interface between libjava and Boehm GC.
 
-/* Copyright (C) 1998, 1999, 2000  Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000, 2001  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -97,10 +97,13 @@
   if (__builtin_expect (! dt || !(dt -> get_finalizer()), false))
     return mark_stack_ptr;
   jclass klass = dt->clas;
+  ptr_t p;
 
-  // Every object has a sync_info pointer.
-  ptr_t p = (ptr_t) obj->sync_info;
-  MAYBE_MARK (p, mark_stack_ptr, mark_stack_limit, obj, o1label);
+# ifndef JV_HASH_SYNCHRONIZATION
+    // Every object has a sync_info pointer.
+    p = (ptr_t) obj->sync_info;
+    MAYBE_MARK (p, mark_stack_ptr, mark_stack_limit, obj, o1label);
+# endif
   // Mark the object's class.
   p = (ptr_t) klass;
   MAYBE_MARK (p, mark_stack_ptr, mark_stack_limit, obj, o2label);
@@ -300,10 +303,13 @@
   if (__builtin_expect (! dt || !(dt -> get_finalizer()), false))
     return mark_stack_ptr;
   jclass klass = dt->clas;
+  ptr_t p;
 
-  // Every object has a sync_info pointer.
-  ptr_t p = (ptr_t) array->sync_info;
-  MAYBE_MARK (p, mark_stack_ptr, mark_stack_limit, array, e1label);
+# ifndef JV_HASH_SYNCHRONIZATION
+    // Every object has a sync_info pointer.
+    p = (ptr_t) array->sync_info;
+    MAYBE_MARK (p, mark_stack_ptr, mark_stack_limit, array, e1label);
+# endif
   // Mark the object's class.
   p = (ptr_t) klass;
   MAYBE_MARK (p, mark_stack_ptr, mark_stack_limit, obj, o2label);
@@ -326,18 +332,24 @@
 // knows this, so in that case everything else will break, too.
 #define GCJ_DEFAULT_DESCR GC_MAKE_PROC(GC_GCJ_RESERVED_MARK_PROC_INDEX,0)
 void *
-_Jv_BuildGCDescr(jclass klass)
+_Jv_BuildGCDescr(jclass)
 {
   /* FIXME: We should really look at the class and build the descriptor. */
   return (void *)(GCJ_DEFAULT_DESCR);
 }
 #endif
 
-// Allocate space for a new Java object.
+// Allocate some space that is known to be pointer-free.
 void *
-_Jv_AllocObj (jsize size, jclass klass)
+_Jv_AllocBytes (jsize size)
 {
-  return GC_GCJ_MALLOC (size, klass->vtable);
+  void *r = GC_MALLOC_ATOMIC (size);
+  // We have to explicitly zero memory here, as the GC doesn't
+  // guarantee that PTRFREE allocations are zeroed.  Note that we
+  // don't have to do this for other allocation types because we set
+  // the `ok_init' flag in the type descriptor.
+  memset (r, 0, size);
+  return r;
 }
 
 // Allocate space for a new Java array.
@@ -368,20 +380,6 @@
   return obj;
 }
 
-// Allocate some space that is known to be pointer-free.
-void *
-_Jv_AllocBytes (jsize size)
-{
-  void *r = GC_MALLOC_ATOMIC (size);
-  // We have to explicitly zero memory here, as the GC doesn't
-  // guarantee that PTRFREE allocations are zeroed.  Note that we
-  // don't have to do this for other allocation types because we set
-  // the `ok_init' flag in the type descriptor.
-  if (__builtin_expect (r != NULL, !NULL))
-    memset (r, 0, size);
-  return r;
-}
-
 static void
 call_finalizer (GC_PTR obj, GC_PTR client_data)
 {
@@ -462,6 +460,11 @@
   _Jv_MutexUnlock (&disable_gc_mutex); 
 }
 
+static void * handle_out_of_memory(size_t)
+{
+  _Jv_ThrowNoMemory();
+}
+
 void
 _Jv_InitGC (void)
 {
@@ -484,6 +487,10 @@
   // stash in the class vtable.
   GC_init_gcj_malloc (0, (void *) _Jv_MarkObj);  
 
+  // Cause an out of memory error to be thrown from the allocators,
+  // instead of returning 0.  This is cheaper than checking on allocation.
+  GC_oom_fn = handle_out_of_memory;
+
   LOCK ();
   GC_java_finalization = 1;
 
@@ -510,6 +517,28 @@
   UNLOCK ();
   ENABLE_SIGNALS ();
 }
+
+#ifdef JV_HASH_SYNCHRONIZATION
+// Allocate an object with a fake vtable pointer, which causes only
+// the first field (beyond the fake vtable pointer) to be traced.
+// Eventually this should probably be generalized.
+
+static _Jv_VTable trace_one_vtable = {
+    0, 			// class pointer
+    (void *)(2 * sizeof(void *)),
+			// descriptor; scan 2 words incl. vtable ptr.
+			// Least significant bits must be zero to
+			// identify this as a lenght descriptor
+    {0}			// First method
+};
+
+void *
+_Jv_AllocTraceOne (jsize size /* includes vtable slot */) 
+{
+  return GC_GCJ_MALLOC (size, &trace_one_vtable);
+}
+
+#endif /* JV_HASH_SYNCHRONIZATION */
 
 #if 0
 void
Index: configure.host
===================================================================
RCS file: /cvs/gcc/gcc/libjava/configure.host,v
retrieving revision 1.19
diff -u -r1.19 configure.host
--- configure.host	2001/05/21 16:59:41	1.19
+++ configure.host	2001/05/24 01:06:09
@@ -24,6 +24,8 @@
 libgcj_cxxflags=
 libgcj_javaflags=
 libgcj_interpreter=
+enable_java_net_default=yes
+enable_hash_synchronization_default=no
 
 case "${target_optspace}:${host}" in
   yes:*)
@@ -63,6 +65,7 @@
 	libgcj_cxxflags="-D__NO_MATH_INLINES"
 	libgcj_cflags="-D__NO_MATH_INLINES"
 	DIVIDESPEC=-fno-use-divide-subroutine
+	enable_hash_synchronization_default=yes
 	;;
   alpha*-*)
 	libgcj_flags="${libgcj_flags} -mieee"
@@ -76,6 +79,7 @@
   ia64-*)
         libgcj_flags="${libgcj_flags} -funwind-tables"
 	libgcj_interpreter=yes
+	enable_hash_synchronization_default=yes
 	;;
 esac
 
Index: configure.in
===================================================================
RCS file: /cvs/gcc/gcc/libjava/configure.in,v
retrieving revision 1.85
diff -u -r1.85 configure.in
--- configure.in	2001/05/23 17:13:26	1.85
+++ configure.in	2001/05/24 01:06:10
@@ -16,7 +16,7 @@
 
 LIBGCJ_CONFIGURE(.)
 
-AM_CONFIG_HEADER(include/config.h)
+AM_CONFIG_HEADER(include/config.h gcj/libgcj-config.h)
 
 # Only use libltdl for native builds.
 if test -z "${with_cross_host}"; then
@@ -57,13 +57,32 @@
                           don't set system properties from GCJ_PROPERTIES])
 
 dnl Whether GCJ_PROPERTIES is used depends on the target.
-if test -n "$enable_getenv_properties"; then
+if test -z "$enable_getenv_properties"; then
    enable_getenv_properties=${enable_getenv_properties_default-yes}
 fi
 if test "$enable_getenv_properties" = no; then
    AC_DEFINE(DISABLE_GETENV_PROPERTIES)
 fi
 
+
+dnl Should we use hashtable-based synchronization?
+dnl Currently works only for Linux X86/ia64
+dnl Typically faster and more space-efficient
+AC_ARG_ENABLE(hash-synchronization,
+[  --enable-hash-synchronization
+                          Use global hash table for monitor locks])
+
+if test -z "$enable_hash_synchronization"; then
+   enable_hash_synchronization=$enable_hash_synchronization_default
+fi
+HASH_SYNC_SPEC=
+if test "$enable_hash_synchronization" = yes; then
+   HASH_SYNC_SPEC=-fhash-synchronization
+   AC_DEFINE(JV_HASH_SYNCHRONIZATION)
+fi
+AC_SUBST(HASH_SYNC_SPEC)
+
+
 dnl See if the user has requested runtime debugging.
 LIBGCJDEBUG="false"
 AC_SUBST(LIBGCJDEBUG)
@@ -150,7 +169,7 @@
 [  --disable-java-net      disable java.net])
 
 dnl Whether java.net is built by default can depend on the target.
-if test -n "$enable_java_net"; then
+if test -z "$enable_java_net"; then
    enable_java_net=${enable_java_net_default-yes}
 fi
 if test "$enable_java_net" = no; then
Index: defineclass.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/defineclass.cc,v
retrieving revision 1.16
diff -u -r1.16 defineclass.cc
--- defineclass.cc	2001/03/26 07:05:31	1.16
+++ defineclass.cc	2001/05/24 01:06:11
@@ -360,8 +360,8 @@
 
 void _Jv_ClassReader::read_constpool ()
 {
-  tags    = (unsigned char*) _Jv_AllocBytesChecked (pool_count);
-  offsets = (unsigned int *) _Jv_AllocBytesChecked (sizeof (int)
+  tags    = (unsigned char*) _Jv_AllocBytes (pool_count);
+  offsets = (unsigned int *) _Jv_AllocBytes (sizeof (int)
 						    * pool_count) ;
 
   /** first, we scan the constant pool, collecting tags and offsets */
@@ -625,9 +625,9 @@
   /** now, we actually define the class' constant pool */
 
   // the pool is scanned explicitly by the collector
-  jbyte *pool_tags = (jbyte*) _Jv_AllocBytesChecked (pool_count);
+  jbyte *pool_tags = (jbyte*) _Jv_AllocBytes (pool_count);
   _Jv_word *pool_data
-    = (_Jv_word*) _Jv_AllocBytesChecked (pool_count * sizeof (_Jv_word));
+    = (_Jv_word*) _Jv_AllocBytes (pool_count * sizeof (_Jv_word));
   
   def->constants.tags = pool_tags;
   def->constants.data = pool_data;
@@ -965,7 +965,7 @@
 
 void _Jv_ClassReader::handleInterfacesBegin (int count)
 {
-  def->interfaces = (jclass*) _Jv_AllocBytesChecked (count*sizeof (jclass));
+  def->interfaces = (jclass*) _Jv_AllocBytes (count*sizeof (jclass));
   def->interface_count = count;
 }
 
@@ -1032,10 +1032,10 @@
 void _Jv_ClassReader::handleFieldsBegin (int count)
 {
   def->fields = (_Jv_Field*) 
-    _Jv_AllocBytesChecked (count * sizeof (_Jv_Field));
+    _Jv_AllocBytes (count * sizeof (_Jv_Field));
   def->field_count = count;
   def->field_initializers = (_Jv_ushort*)
-    _Jv_AllocBytesChecked (count * sizeof (_Jv_ushort));
+    _Jv_AllocBytes (count * sizeof (_Jv_ushort));
   for (int i = 0; i < count; i++)
     def->field_initializers[i] = (_Jv_ushort) 0;
 }
@@ -1172,11 +1172,11 @@
 _Jv_ClassReader::handleMethodsBegin (int count)
 {
   def->methods = (_Jv_Method*)
-    _Jv_AllocBytesChecked (sizeof (_Jv_Method)*count);
+    _Jv_AllocBytes (sizeof (_Jv_Method)*count);
 
   def->interpreted_methods
-    = (_Jv_MethodBase **) _Jv_AllocBytesChecked (sizeof (_Jv_MethodBase *)
-						 * count);
+    = (_Jv_MethodBase **) _Jv_AllocBytes (sizeof (_Jv_MethodBase *)
+					  * count);
 
   for (int i = 0; i < count; i++)
     def->interpreted_methods[i] = 0;
@@ -1235,7 +1235,7 @@
 {
   int size = _Jv_InterpMethod::size (exc_table_length, code_length);
   _Jv_InterpMethod *method = 
-    (_Jv_InterpMethod*) (_Jv_AllocBytesChecked (size));
+    (_Jv_InterpMethod*) (_Jv_AllocBytes (size));
 
   method->max_stack      = max_stack;
   method->max_locals     = max_locals;
@@ -1282,7 +1282,7 @@
 	  else
 	    {
 	      _Jv_JNIMethod *m = (_Jv_JNIMethod *)
-		_Jv_AllocBytesChecked (sizeof (_Jv_JNIMethod));
+		_Jv_AllocBytes (sizeof (_Jv_JNIMethod));
 	      m->defining_class = def;
 	      m->self = method;
 	      m->function = NULL;
Index: libgcj.spec.in
===================================================================
RCS file: /cvs/gcc/gcc/libjava/libgcj.spec.in,v
retrieving revision 1.16
diff -u -r1.16 libgcj.spec.in
--- libgcj.spec.in	2001/05/01 17:45:08	1.16
+++ libgcj.spec.in	2001/05/24 01:06:11
@@ -6,7 +6,7 @@
 %rename lib liborig
 *lib: -lgcj -lm @GCSPEC@ @THREADSPEC@ @ZLIBSPEC@ @SYSTEMSPEC@ %(libgcc) %(liborig)
 
-*jc1: @DIVIDESPEC@ @JC1GCSPEC@ @EXCEPTIONSPEC@
+*jc1: @HASH_SYNC_SPEC@ @DIVIDESPEC@ @JC1GCSPEC@ @EXCEPTIONSPEC@
 
 #
 # On some systems we force in a data_start symbol so that the GC will work
Index: nogc.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/nogc.cc,v
retrieving revision 1.8
diff -u -r1.8 nogc.cc
--- nogc.cc	2000/12/30 12:18:38	1.8
+++ nogc.cc	2001/05/24 01:06:11
@@ -1,6 +1,6 @@
 // nogc.cc - Implement null garbage collector.
 
-/* Copyright (C) 1998, 1999, 2000  Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000, 2001  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -32,15 +32,27 @@
 {
   total += size;
   void *obj = calloc (size, 1);
+  if (!obj) _Jv_ThrowNoMemory();
   *((_Jv_VTable **) obj) = klass->vtable;
   return obj;
 }
 
 void *
+_Jv_AllocPtrFreeObj (jsize size, jclass klass)
+{
+  total += size;
+  ptr_t obj = malloc (size, 1);
+  if (!obj) _Jv_ThrowNoMemory();
+  *((_Jv_VTable **) obj) = klass->vtable;
+  return obj;
+}
+
+void *
 _Jv_AllocArray (jsize size, jclass klass)
 {
   total += size;
   void *obj = calloc (size, 1);
+  if (!obj) _Jv_ThrowNoMemory();
   *((_Jv_VTable **) obj) = klass->vtable;
   return obj;
 }
@@ -49,7 +61,9 @@
 _Jv_AllocBytes (jsize size)
 {
   total += size;
-  return calloc (size, 1);
+  ptr_t obj = calloc (size, 1);
+  if (!obj) _Jv_ThrowNoMemory();
+  return obj;
 }
 
 void
@@ -111,3 +125,13 @@
 _Jv_InitGC (void)
 {
 }
+
+#ifdef JV_HASH_SYNCHRONIZATION
+void *
+_Jv_AllocTraceOne (jsize size /* includes vtable slot */) 
+{
+  ptr_t obj = calloc(size, 1);
+  if (!obj) _Jv_ThrowNoMemory();
+  return result;
+}
+#endif /* JV_HASH_SYNCHRONIZATION */
Index: posix-threads.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/posix-threads.cc,v
retrieving revision 1.26
diff -u -r1.26 posix-threads.cc
--- posix-threads.cc	2001/05/22 06:47:48	1.26
+++ posix-threads.cc	2001/05/24 01:06:11
@@ -1,6 +1,6 @@
 // posix-threads.cc - interface between libjava and POSIX threads.
 
-/* Copyright (C) 1998, 1999, 2000  Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000, 2001  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -336,6 +336,22 @@
   // is called. Since it may need to be accessed from the new thread, work 
   // around the potential race here by explicitly setting it again.
   data->thread = pthread_self ();
+
+# ifdef SLOW_PTHREAD_SELF
+    // Clear all self cache slots that might be needed by this thread.
+    int dummy;
+    int low_index = SC_INDEX(&dummy) + SC_CLEAR_MIN;
+    int high_index = SC_INDEX(&dummy) + SC_CLEAR_MAX;
+    for (int i = low_index; i <= high_index; ++i) 
+      {
+        int current_index = i;
+	if (current_index < 0)
+	  current_index += SELF_CACHE_SIZE;
+	if (current_index >= SELF_CACHE_SIZE)
+	  current_index -= SELF_CACHE_SIZE;
+	_Jv_self_cache[current_index].high_sp_bits = BAD_HIGH_SP_VALUE;
+      }
+# endif
 }
 
 void
@@ -356,7 +372,7 @@
   _Jv_ThreadRegister (info->data);
 
   info->method (info->data->thread_obj);
-  
+
   if (! (info->data->flags & FLAG_DAEMON))
     {
       pthread_mutex_lock (&daemon_mutex);
@@ -365,7 +381,7 @@
 	pthread_cond_signal (&daemon_cond);
       pthread_mutex_unlock (&daemon_mutex);
     }
-  
+
   return NULL;
 }
 
@@ -418,3 +434,23 @@
     pthread_cond_wait (&daemon_cond, &daemon_mutex);
   pthread_mutex_unlock (&daemon_mutex);
 }
+
+#if defined(SLOW_PTHREAD_SELF)
+
+// Support for pthread_self() lookup cache.
+
+volatile self_cache_entry _Jv_self_cache[SELF_CACHE_SIZE];
+
+
+_Jv_ThreadId_t
+_Jv_ThreadSelf_out_of_line(volatile self_cache_entry *sce, size_t high_sp_bits)
+{
+  pthread_t self = pthread_self();
+  // The ordering between the following writes matters.
+  // On Alpha, we probably need a memory barrier in the middle.
+  sce -> high_sp_bits = high_sp_bits;
+  sce -> self = self;
+  return self;
+}
+
+#endif /* SLOW_PTHREAD_SELF */
Index: prims.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/prims.cc,v
retrieving revision 1.51
diff -u -r1.51 prims.cc
--- prims.cc	2001/05/22 06:47:48	1.51
+++ prims.cc	2001/05/24 01:06:12
@@ -221,11 +221,11 @@
   ptr = (unsigned char*) str;
   limit = ptr + len;
   str_length = 0;
-  for (; ptr < limit; str_length++) {
-    if (UTF8_GET (ptr, limit) < 0) {
-      return (-1);
+  for (; ptr < limit; str_length++)
+    {
+      if (UTF8_GET (ptr, limit) < 0)
+	return (-1);
     }
-  }
   return (str_length);
 }
 
@@ -271,7 +271,7 @@
   jint len = _Jv_GetStringUTFLength (string);
 
   Utf8Const* m = (Utf8Const*)
-    _Jv_AllocBytesChecked (sizeof(Utf8Const) + len + 1);
+    _Jv_AllocBytes (sizeof(Utf8Const) + len + 1);
 
   m->hash = hash;
   m->length = len;
@@ -331,14 +331,11 @@
   throw new java::lang::NullPointerException;
 }
 
-// Allocate some unscanned memory and throw an exception if no memory.
-void *
-_Jv_AllocBytesChecked (jsize size)
+// Explicitly throw a no memory exception.
+// The collector calls this when it encounters an out-of-memory condition.
+void _Jv_ThrowNoMemory()
 {
-  void *r = _Jv_AllocBytes (size);
-  if (! r)
-    throw no_memory;
-  return r;
+  _Jv_Throw (no_memory);
 }
 
 // Allocate a new object of class KLASS.  SIZE is the size of the object
@@ -350,8 +347,6 @@
   _Jv_InitClass (klass);
 
   jobject obj = (jobject) _Jv_AllocObj (size, klass);
-  if (__builtin_expect (! obj, false))
-    throw no_memory;
 
   // If this class has inherited finalize from Object, then don't
   // bother registering a finalizer.  We know that finalize() is the
@@ -379,6 +374,14 @@
       event.u.obj_alloc.size = size;
       event.u.obj_alloc.obj_id = (jobjectID) obj;
 
+      // FIXME:  This doesn't look right for the Boehm GC.  A GC may
+      // already be in progress.  _Jv_DisableGC () doesn't wait for it.
+      // More importantly, I don't see the need for disabling GC, since we
+      // blatantly have a pointer to obj on our stack, ensuring that the
+      // object can't be collected.  Even for a nonconservative collector,
+      // it appears to me that this must be true, since we are about to
+      // return obj. Isn't this whole approach way too intrusive for
+      // a useful profiling interface?			- HB
       _Jv_DisableGC ();
       (*_Jv_JVMPI_Notify_OBJECT_ALLOC) (&event);
       _Jv_EnableGC ();
@@ -388,6 +391,43 @@
   return obj;
 }
 
+// A version of the above that assumes the object contains no pointers,
+// and requires no finalization.  This can't happen if we need pointers
+// to locks.
+#ifdef JV_HASH_SYNCHRONIZATION
+jobject
+_Jv_AllocPtrFreeObject (jclass klass, jint size)
+{
+  _Jv_InitClass (klass);
+
+  jobject obj = (jobject) _Jv_AllocPtrFreeObj (size, klass);
+
+#ifdef ENABLE_JVMPI
+  // Service JVMPI request.
+
+  if (__builtin_expect (_Jv_JVMPI_Notify_OBJECT_ALLOC != 0, false))
+    {
+      JVMPI_Event event;
+
+      event.event_type = JVMPI_EVENT_OBJECT_ALLOC;
+      event.env_id = NULL;
+      event.u.obj_alloc.arena_id = 0;
+      event.u.obj_alloc.class_id = (jobjectID) klass;
+      event.u.obj_alloc.is_array = 0;
+      event.u.obj_alloc.size = size;
+      event.u.obj_alloc.obj_id = (jobjectID) obj;
+
+      _Jv_DisableGC ();
+      (*_Jv_JVMPI_Notify_OBJECT_ALLOC) (&event);
+      _Jv_EnableGC ();
+    }
+#endif
+
+  return obj;
+}
+#endif /* JV_HASH_SYNCHRONIZATION */
+
+
 // Allocate a new array of Java objects.  Each object is of type
 // `elementClass'.  `init' is used to initialize each slot in the
 // array.
@@ -408,8 +448,6 @@
   jclass klass = _Jv_GetArrayClass (elementClass, 0);
 
   obj = (jobjectArray) _Jv_AllocArray (size, klass);
-  if (__builtin_expect (! obj, false))
-    throw no_memory;
   // Cast away const.
   jsize *lp = const_cast<jsize *> (&obj->length);
   *lp = count;
@@ -444,13 +482,19 @@
 
   jclass klass = _Jv_GetArrayClass (eltype, 0);
 
+# ifdef JV_HASH_SYNCHRONIZATION
+  // Since the vtable is always statically allocated,
+  // these are completely pointerfree!  Make sure the GC doesn't touch them.
+  __JArray *arr =
+    (__JArray*) _Jv_AllocPtrFreeObj (size + elsize * count, klass);
+  memset((char *)arr + size, 0, elsize * count);
+# else
   __JArray *arr = (__JArray*) _Jv_AllocObj (size + elsize * count, klass);
-  if (__builtin_expect (! arr, false))
-    throw no_memory;
+  // Note that we assume we are given zeroed memory by the allocator.
+# endif
   // Cast away const.
   jsize *lp = const_cast<jsize *> (&arr->length);
   *lp = count;
-  // Note that we assume we are given zeroed memory by the allocator.
 
   return arr;
 }
Index: resolve.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/resolve.cc,v
retrieving revision 1.21
diff -u -r1.21 resolve.cc
--- resolve.cc	2001/03/26 07:05:31	1.21
+++ resolve.cc	2001/05/24 01:06:13
@@ -583,7 +583,7 @@
   // allocate static memory
   if (static_size != 0)
     {
-      char *static_data = (char*)_Jv_AllocBytesChecked (static_size);
+      char *static_data = (char*)_Jv_AllocBytes (static_size);
 
       memset (static_data, 0, static_size);
 
@@ -697,7 +697,7 @@
 
   /* allocate vtable structure */
   _Jv_VTable *vtable = (_Jv_VTable*) 
-    _Jv_AllocBytesChecked (sizeof (_Jv_VTable) 
+    _Jv_AllocBytes (sizeof (_Jv_VTable) 
 			   + (sizeof (void*) * (vtable_count)));
   vtable->clas = clz;
   vtable->gc_descr = _Jv_BuildGCDescr(clz);
@@ -1076,7 +1076,7 @@
   int arg_count = count_arguments (self->signature, staticp);
 
   ncode_closure *closure =
-    (ncode_closure*)_Jv_AllocBytesChecked (sizeof (ncode_closure)
+    (ncode_closure*)_Jv_AllocBytes (sizeof (ncode_closure)
 					+ arg_count * sizeof (ffi_type*));
 
   init_cif (self->signature,
@@ -1126,8 +1126,8 @@
   int arg_count = count_arguments (self->signature, staticp);
 
   ncode_closure *closure =
-    (ncode_closure*)_Jv_AllocBytesChecked (sizeof (ncode_closure)
-					+ arg_count * sizeof (ffi_type*));
+    (ncode_closure*)_Jv_AllocBytes (sizeof (ncode_closure)
+				    + arg_count * sizeof (ffi_type*));
 
   ffi_type *rtype;
   init_cif (self->signature,
@@ -1187,8 +1187,8 @@
   int arg_count = count_arguments (method->signature, staticp);
 
   _Jv_ResolvedMethod* result = (_Jv_ResolvedMethod*)
-    _Jv_AllocBytesChecked (sizeof (_Jv_ResolvedMethod)
-			   + arg_count*sizeof (ffi_type*));
+    _Jv_AllocBytes (sizeof (_Jv_ResolvedMethod)
+		    + arg_count*sizeof (ffi_type*));
 
   result->stack_item_count
     = init_cif (method->signature,
Index: gcj/Makefile.am
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gcj/Makefile.am,v
retrieving revision 1.2
diff -u -r1.2 Makefile.am
--- Makefile.am	2001/04/04 23:38:52	1.2
+++ Makefile.am	2001/05/24 01:06:13
@@ -3,4 +3,4 @@
 AUTOMAKE_OPTIONS = foreign
 
 gcjdir = $(includedir)/gcj
-gcj_HEADERS = array.h cni.h field.h javaprims.h method.h
+gcj_HEADERS = array.h cni.h field.h javaprims.h method.h libgcj-config.h
Index: gcj/javaprims.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gcj/javaprims.h,v
retrieving revision 1.21
diff -u -r1.21 javaprims.h
--- javaprims.h	2001/05/22 06:47:48	1.21
+++ javaprims.h	2001/05/24 01:06:13
@@ -1,6 +1,6 @@
 // javaprims.h - Main external header file for libgcj.  -*- c++ -*-
 
-/* Copyright (C) 1998, 1999, 2000  Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000, 2001  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -14,6 +14,8 @@
 // Force C++ compiler to use Java-style exceptions.
 #pragma GCC java_exceptions
 
+#include <gcj/libgcj-config.h>
+
 // FIXME: this is a hack until we get a proper gcjh.
 // It is needed to work around system header files that define TRUE
 // and FALSE.
@@ -367,14 +369,24 @@
 typedef struct _Jv_Method *jmethodID;
 
 extern "C" jobject _Jv_AllocObject (jclass, jint) __attribute__((__malloc__));
+#ifdef JV_HASH_SYNCHRONIZATION
+  extern "C" jobject _Jv_AllocPtrFreeObject (jclass, jint)
+  			    __attribute__((__malloc__));
+#else
+  // Collector still needs to scan sync_info
+  static inline jobject _Jv_AllocPtrFreeObject (jclass klass, jint sz)
+  {
+    return _Jv_AllocObject(klass, sz);
+  }
+#endif
 extern "C" jboolean _Jv_IsInstanceOf(jobject, jclass);
 extern "C" jstring _Jv_AllocString(jsize) __attribute__((__malloc__));
 extern "C" jstring _Jv_NewString (const jchar*, jsize)
   __attribute__((__malloc__));
 extern jint _Jv_FormatInt (jchar* bufend, jint num);
 extern "C" jchar* _Jv_GetStringChars (jstring str);
-extern "C" jint _Jv_MonitorEnter (jobject);
-extern "C" jint _Jv_MonitorExit (jobject);
+extern "C" void _Jv_MonitorEnter (jobject);
+extern "C" void _Jv_MonitorExit (jobject);
 extern "C" jstring _Jv_NewStringLatin1(const char*, jsize)
   __attribute__((__malloc__));
 extern "C" jsize _Jv_GetStringUTFLength (jstring);
@@ -399,5 +411,6 @@
   _Jv_ushort length;	/* In bytes, of data portion, without final '\0'. */
   char data[1];		/* In Utf8 format, with final '\0'. */
 };
+
 
 #endif /* __JAVAPRIMS_H__ */
Index: gcj/libgcj-config.h.in
===================================================================
RCS file: libgcj-config.h.in
diff -N libgcj-config.h.in
--- /dev/null	Tue May  5 13:32:27 1998
+++ libgcj-config.h.in	Wed May 23 18:06:13 2001
@@ -0,0 +1,5 @@
+/* The header derived from this file is installed, so this file should
+   only contain defines which are named safely.  */
+
+/* Define if hash synchronization is in use.  */
+#undef JV_HASH_SYNCHRONIZATION
Index: include/boehm-gc.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/include/boehm-gc.h,v
retrieving revision 1.3
diff -u -r1.3 boehm-gc.h
--- boehm-gc.h	2000/03/07 19:55:25	1.3
+++ boehm-gc.h	2001/05/24 01:06:14
@@ -21,4 +21,35 @@
   JV_MARKARRAY_DECL;
 };
 
+// Enough stuff to inline _Jv_AllocObj.  Ugly.
+#include <gcj/javaprims.h>
+#include <java/lang/Class.h>
+#include <string.h>
+
+extern "C" void * GC_gcj_malloc(size_t, void *);
+extern "C" void * GC_malloc_atomic(size_t);
+
+inline void *
+_Jv_AllocObj (jsize size, jclass klass)
+{
+  // This should call GC_GCJ_MALLOC, but that would involve
+  // including gc.h.
+  return GC_gcj_malloc (size, klass->vtable);
+}
+
+inline void *
+_Jv_AllocPtrFreeObj (jsize size, jclass klass)
+{
+#ifdef JV_HASH_SYNCHRONIZATION
+  void * obj = GC_malloc_atomic(size);
+  *((_Jv_VTable **) obj) = klass->vtable;
+#else
+  void * obj = GC_gcj_malloc(size, klass->vtable);
+#endif
+  return obj;
+}
+
+// _Jv_AllocBytes (jsize size) should go here, too.  But clients don't
+// usually include this header.
+
 #endif /* __JV_BOEHM_GC__ */
Index: include/jvm.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/include/jvm.h,v
retrieving revision 1.34
diff -u -r1.34 jvm.h
--- jvm.h	2001/05/18 06:29:11	1.34
+++ jvm.h	2001/05/24 01:06:14
@@ -107,10 +107,18 @@
 
 /* Allocate space for a new Java object.  */
 void *_Jv_AllocObj (jsize size, jclass cl) __attribute__((__malloc__));
+/* Allocate space for a potentially uninitialized pointer-free object.
+   Interesting only with JV_HASH_SYNCHRONIZATION.  */
+void *_Jv_AllocPtrFreeObj (jsize size, jclass cl) __attribute__((__malloc__));
 /* Allocate space for an array of Java objects.  */
 void *_Jv_AllocArray (jsize size, jclass cl) __attribute__((__malloc__));
 /* Allocate space that is known to be pointer-free.  */
 void *_Jv_AllocBytes (jsize size) __attribute__((__malloc__));
+/* Explicitly throw an out-of-memory exception.	*/
+void _Jv_ThrowNoMemory();
+/* Allocate an object with a single pointer.  The first word is reserved
+   for the GC, and the second word is the traced pointer.  */
+void *_Jv_AllocTraceOne (jsize size /* incl. reserved slot */);
 /* Initialize the GC.  */
 void _Jv_InitGC (void);
 /* Register a finalizer.  */
@@ -156,9 +164,6 @@
    number which can optionally have "k" or "m" appended and calls
    _Jv_GCSetMaximumHeapSize.  */
 void _Jv_SetMaximumHeapSize (const char *arg);
-
-/* Allocate some unscanned bytes.  Throw exception if out of memory.  */
-void *_Jv_AllocBytesChecked (jsize size) __attribute__((__malloc__));
 
 extern "C" void JvRunMain (jclass klass, int argc, const char **argv);
 void _Jv_RunMain (const char* name, int argc, const char **argv, bool is_jar);
Index: include/posix-threads.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/include/posix-threads.h,v
retrieving revision 1.18
diff -u -r1.18 posix-threads.h
--- posix-threads.h	2001/05/22 06:47:48	1.18
+++ posix-threads.h	2001/05/24 01:06:15
@@ -1,7 +1,7 @@
 // -*- c++ -*-
 // posix-threads.h - Defines for using POSIX threads.
 
-/* Copyright (C) 1998, 1999  Free Software Foundation
+/* Copyright (C) 1998, 1999, 2001  Free Software Foundation
 
    This file is part of libgcj.
 
@@ -106,10 +106,21 @@
 // Mutexes.
 //
 
+#ifdef LOCK_DEBUG
+# include <stdio.h>
+#endif
+
 inline void
 _Jv_MutexInit (_Jv_Mutex_t *mu)
 {
+# ifdef LOCK_DEBUG /* Assumes Linuxthreads */
+  pthread_mutexattr_t attr;
+  pthread_mutexattr_init(&attr);
+  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+  pthread_mutex_init (&mu->mutex, &attr);
+# else
   pthread_mutex_init (&mu->mutex, 0);
+# endif
 
   mu->count = 0;
   mu->owner = 0;
@@ -125,7 +136,16 @@
     }
   else
     {
-      pthread_mutex_lock (&mu->mutex);
+#     ifdef LOCK_DEBUG
+	int result = pthread_mutex_lock (&mu->mutex);
+	if (0 != result)
+	  {
+	    fprintf(stderr, "Pthread_mutex_lock returned %d\n", result);
+	    for (;;) {}
+	  }
+#     else
+        pthread_mutex_lock (&mu->mutex);
+#     endif
       mu->count = 1;
       mu->owner = self;
     }
@@ -136,14 +156,29 @@
 _Jv_MutexUnlock (_Jv_Mutex_t *mu)
 {
   if (_Jv_PthreadCheckMonitor (mu))
-    return 1;
+    {
+#     ifdef LOCK_DEBUG
+	fprintf(stderr, "_Jv_MutexUnlock: Not owner\n");
+	for (;;) {}
+#     endif
+      return 1;
+    }
     
   mu->count--;
 
   if (mu->count == 0)
     {
       mu->owner = 0;
-      pthread_mutex_unlock (&mu->mutex);
+#     ifdef LOCK_DEBUG
+	int result = pthread_mutex_unlock (&mu->mutex);
+	if (0 != result)
+	  {
+	    fprintf(stderr, "Pthread_mutex_unlock returned %d\n", result);
+	    for (;;) {}
+	  }
+#     else
+        pthread_mutex_unlock (&mu->mutex);
+#     endif
     }
   return 0;
 }
@@ -178,6 +213,126 @@
   extern pthread_key_t _Jv_ThreadKey;
   return (java::lang::Thread *) pthread_getspecific (_Jv_ThreadKey);
 }
+
+#ifdef JV_HASH_SYNCHRONIZATION
+// Should be specialized to just load the "current thread" register
+// on platforms that support it.   Speed is of the essence.  The value
+// of the descriptor is not, so long as there is a one-to-one correspondence
+// to threads.
+
+
+#ifdef __i386__
+
+#define SLOW_PTHREAD_SELF
+	// Add a cache for pthread_self() if we don't have the thread
+	// pointer in a register.
+
+#endif  /* __i386__ */
+
+#ifdef __ia64__
+
+typedef size_t _Jv_ThreadId_t;
+
+register size_t _Jv_self __asm__("r13");
+	// For linux_threads this is really a pointer to its thread data
+	// structure.  We treat it as opaque.  That should also work
+	// on other operating systems that follow the ABI standard.
+
+// This should become the prototype for machines that maintain a thread
+// pointer in a register.
+inline _Jv_ThreadId_t
+_Jv_ThreadSelf (void)
+{
+  return _Jv_self;
+}
+
+#define JV_SELF_DEFINED
+
+#endif /* __ia64__ */
+
+#if defined(SLOW_PTHREAD_SELF)
+
+typedef pthread_t _Jv_ThreadId_t;
+
+// E.g. on X86 Linux, pthread_self() is too slow for our purpose.
+// Instead we maintain a cache based on the current sp value.
+// This is similar to what's done for thread local allocation in the
+// GC, only far simpler.
+// This code should probably go away when Linux/X86 starts using a
+// segment register to hold the thread id.
+# define LOG_THREAD_SPACING 12
+			// If two thread pointer values are closer than
+			// 1 << LOG_THREAD_SPACING, we assume they belong
+			// to the same thread.
+# define SELF_CACHE_SIZE 1024
+# define SC_INDEX(sp) (((unsigned long)(sp) >> 19) & (SELF_CACHE_SIZE-1))
+		        // Mapping from sp value to cache index.
+			// Note that this is not in any real sense a hash
+			// function, since we need to be able to clear
+			// all possibly matching slots on thread startup.
+			// Thus all entries that might correspond to
+			// a given thread are intentionally contiguous.
+			// Works well with anything that allocates at least
+			// 512KB stacks.
+# define SC_CLEAR_MIN (-16)	// When starting a new thread, we clear
+# define SC_CLEAR_MAX 0		// all self cache entries between
+				// SC_INDEX(sp)+SC_CLEAR_MIN and
+				// SC_INDEX(sp)+SC_CLEAR_MAX to ensure
+				// we never see stale values.  The
+				// current values assume a downward
+				// growing stack of size <= 7.5 MB.
+# define BAD_HIGH_SP_VALUE ((size_t)(-1))
+
+extern volatile
+struct self_cache_entry {
+  size_t high_sp_bits;	// sp value >> LOG_THREAD_SPACING
+  pthread_t self;	// Corresponding thread
+} _Jv_self_cache[];
+
+void _Jv_Self_Cache_Init();
+
+_Jv_ThreadId_t
+_Jv_ThreadSelf_out_of_line(volatile self_cache_entry *sce,
+			   size_t high_sp_bits);
+  
+inline _Jv_ThreadId_t
+_Jv_ThreadSelf (void)
+{
+  int dummy;
+  size_t sp = (size_t)(&dummy);
+  unsigned h = SC_INDEX(sp);
+  volatile self_cache_entry *sce = _Jv_self_cache + h;
+  pthread_t candidate_self = sce -> self;  // Read must precede following one.
+  // Read barrier goes here, if needed.
+  if (sce -> high_sp_bits == sp >> LOG_THREAD_SPACING)
+    {
+      // The sce -> self value we read must be valid.  An intervening
+      // cache replacement by another thread would have first replaced
+      // high_sp_bits by something else, and it can't possibly change
+      // back without our intervention.
+      return candidate_self;
+    }
+  else
+    return _Jv_ThreadSelf_out_of_line(sce, sp >> LOG_THREAD_SPACING);
+}
+
+#define JV_SELF_DEFINED
+
+#endif /* SLOW_PTHREAD_SELF */
+
+#ifndef JV_SELF_DEFINED /* If all else fails, call pthread_self directly */
+
+typedef pthread_t _Jv_ThreadId_t;
+
+inline _Jv_ThreadId_t
+_Jv_ThreadSelf (void)
+{
+  return pthread_self();
+}
+
+#endif /* !JV_SELF_DEFINED */
+
+#endif /* JV_HASH_SYNCHRONIZATION */
 
 inline _Jv_Thread_t *
 _Jv_ThreadCurrentData (void)
Index: java/lang/Class.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/Class.h,v
retrieving revision 1.33
diff -u -r1.33 Class.h
--- Class.h	2001/05/06 13:42:11	1.33
+++ Class.h	2001/05/24 01:06:15
@@ -267,6 +267,7 @@
 
   friend jobject _Jv_AllocObject (jclass, jint);
   friend void *_Jv_AllocObj (jint, jclass);
+  friend void *_Jv_AllocPtrFreeObj (jint, jclass);
   friend void *_Jv_AllocArray (jint, jclass);
 
   friend jobject _Jv_JNI_ToReflectedField (_Jv_JNIEnv *, jclass, jfieldID,
Index: java/lang/Object.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/Object.h,v
retrieving revision 1.8
diff -u -r1.8 Object.h
--- Object.h	2001/01/17 10:22:32	1.8
+++ Object.h	2001/05/24 01:06:15
@@ -42,8 +42,8 @@
   void wait (void);
   void wait (jlong timeout);
 
-  friend jint _Jv_MonitorEnter (jobject obj);
-  friend jint _Jv_MonitorExit (jobject obj);
+  friend void _Jv_MonitorEnter (jobject obj);
+  friend void _Jv_MonitorExit (jobject obj);
   friend void _Jv_InitializeSyncMutex (void);
   friend void _Jv_FinalizeObject (jobject obj);
 
@@ -63,10 +63,12 @@
   // This does not actually refer to a Java object.  Instead it is a
   // placeholder for a piece of internal data (the synchronization
   // information).
-  jobject sync_info;
+# ifndef JV_HASH_SYNCHRONIZATION
+    jobject sync_info;
+# endif
 
-  // Initialize the sync_info field.
-  void sync_init (void);
+    // Initialize the sync_info field.  Not called with JV_HASH_SYNCHRONIZATION.
+    void sync_init (void);
 };
 
 #endif /* __JAVA_LANG_OBJECT_H__ */
Index: java/lang/natObject.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/natObject.cc,v
retrieving revision 1.14
diff -u -r1.14 natObject.cc
--- natObject.cc	2001/03/26 07:05:32	1.14
+++ natObject.cc	2001/05/24 01:06:16
@@ -28,6 +28,10 @@
 #include <java/lang/Cloneable.h>
 #include <java/lang/Thread.h>
 
+#ifdef LOCK_DEBUG
+#  include <stdio.h>
+#endif
+
 
 
 // This is used to represent synchronization information.
@@ -100,11 +104,26 @@
   return r;
 }
 
+void
+_Jv_FinalizeObject (jobject obj)
+{
+  // Ignore exceptions.  From section 12.6 of the Java Language Spec.
+  try
+    {
+      obj->finalize ();
+    }
+  catch (java::lang::Throwable *t)
+    {
+      // Ignore.
+    }
+}
+
 
 //
 // Synchronization code.
 //
 
+#ifndef JV_HASH_SYNCHRONIZATION
 // This global is used to make sure that only one thread sets an
 // object's `sync_info' field.
 static _Jv_Mutex_t sync_mutex;
@@ -153,7 +172,7 @@
       // been finalized.  So if we just reinitialize the old one,
       // we'll never be able to (re-)destroy the mutex and/or
       // condition variable.
-      si = (_Jv_SyncInfo *) _Jv_AllocBytesChecked (sizeof (_Jv_SyncInfo));
+      si = (_Jv_SyncInfo *) _Jv_AllocBytes (sizeof (_Jv_SyncInfo));
       _Jv_MutexInit (&si->mutex);
       _Jv_CondInit (&si->condition);
 #if defined (_Jv_HaveCondDestroy) || defined (_Jv_HaveMutexDestroy)
@@ -219,7 +238,7 @@
   _Jv_MutexInit (&sync_mutex);
 }
 
-jint
+void
 _Jv_MonitorEnter (jobject obj)
 {
 #ifndef HANDLE_SEGV
@@ -229,10 +248,12 @@
   if (__builtin_expect (INIT_NEEDED (obj), false))
     obj->sync_init ();
   _Jv_SyncInfo *si = (_Jv_SyncInfo *) obj->sync_info;
-  return _Jv_MutexLock (&si->mutex);
+  _Jv_MutexLock (&si->mutex);
+  // FIXME: In the Windows case, this can return a nonzero error code.
+  // We should turn that into some exception ...
 }
 
-jint
+void
 _Jv_MonitorExit (jobject obj)
 {
   JvAssert (obj);
@@ -240,19 +261,928 @@
   _Jv_SyncInfo *si = (_Jv_SyncInfo *) obj->sync_info;
   if (__builtin_expect (_Jv_MutexUnlock (&si->mutex), false))
     throw new java::lang::IllegalMonitorStateException;
-  return 0;
 }
 
+#else /* JV_HASH_SYNCHRONIZATION */
+
+// FIXME: We shouldn't be calling GC_register_finalizer directly.
+#ifndef HAVE_BOEHM_GC
+# error Hash synchronization currently requires boehm-gc
+// That's actually a bit of a lie: It should also work with the null GC,
+// probably even better than the alternative.
+// To really support alternate GCs here, we would need to widen the
+// interface to finalization, since we sometimes have to register a
+// second finalizer for an object that already has one.
+// We might also want to move the GC interface to a .h file, since
+// the number of procedure call levels involved in some of these
+// operations is already ridiculous, and would become worse if we
+// went through the proper intermediaries.
+#else
+# include "gc.h"
+#endif
+
+// What follows currenly assumes a Linux-like platform.
+// Some of it specifically assumes X86 or IA64 Linux, though that
+// should be easily fixable.
+
+// A Java monitor implemention based on a table of locks.
+// Each entry in the table describes
+// locks held for objects that hash to that location.
+// This started out as a reimplementation of the technique used in SGIs JVM,
+// for which we obtained permission from SGI.
+// But in fact, this ended up quite different, though some ideas are
+// still shared with the original.
+// It was also influenced by some of the published IBM work,
+// though it also differs in many ways from that.
+// We could speed this up if we had a way to atomically update
+// an entire cache entry, i.e. 2 contiguous words of memory.
+// That would usually be the case with a 32 bit ABI on a 64 bit processor.
+// But we don't currently go out of our way to target those.
+// I don't know how to do much better with a N bit ABI on a processor
+// that can atomically update only N bits at a time.
+// Author: Hans-J. Boehm  (Hans_Boehm@hp.com, boehm@acm.org)
+
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>	// for usleep, sysconf.
+#include <sched.h>	// for sched_yield.
+#include <gcj/javaprims.h>
+
+typedef size_t obj_addr_t;	/* Integer type big enough for object	*/
+				/* address.				*/
+
+// The following should move to some standard place. Linux-threads
+// already defines roughly these, as do more recent versions of boehm-gc.
+// The problem is that neither exports them.
+
+#if defined(__GNUC__) && defined(__i386__)
+  // Atomically replace *addr by new_val if it was initially equal to old.
+  // Return true if the comparison succeeded.
+  // Assumed to have acquire semantics, i.e. later memory operations
+  // cannot execute before the compare_and_swap finishes.
+  inline static bool
+  compare_and_swap(volatile obj_addr_t *addr,
+		  				obj_addr_t old,
+						obj_addr_t new_val) 
+  {
+    char result;
+    __asm__ __volatile__("lock; cmpxchgl %2, %0; setz %1"
+	    	: "=m"(*(addr)), "=q"(result)
+		: "r" (new_val), "0"(*(addr)), "a"(old) : "memory");
+    return (bool) result;
+  }
+
+  // Set *addr to new_val with release semantics, i.e. making sure
+  // that prior loads and stores complete before this
+  // assignment.
+  // On X86, the hardware shouldn't reorder reads and writes,
+  // so we just have to convince gcc not to do it either.
+  inline static void
+  release_set(volatile obj_addr_t *addr, obj_addr_t new_val)
+  {
+    __asm__ __volatile__(" " : : : "memory");
+    *(addr) = new_val;
+  }
+
+  // Compare_and_swap with release semantics instead of acquire semantics.
+  // On many architecture, the operation makes both guarantees, so the
+  // implementation can be the same.
+  inline static bool
+  compare_and_swap_release(volatile obj_addr_t *addr,
+		  				       obj_addr_t old,
+						       obj_addr_t new_val)
+  {
+    return compare_and_swap(addr, old, new_val);
+  }
+#endif
+
+#if defined(__GNUC__) && defined(__ia64__) && SIZEOF_VOID_P == 8
+  inline static bool
+  compare_and_swap(volatile obj_addr_t *addr,
+	 				        obj_addr_t old,
+						obj_addr_t new_val) 
+  {
+    unsigned long oldval;
+    __asm__ __volatile__("mov ar.ccv=%4 ;; cmpxchg8.acq %0=%1,%2,ar.ccv"
+		: "=r"(oldval), "=m"(*addr)
+		: "r"(new_val), "1"(*addr), "r"(old) : "memory");
+    return (oldval == old);
+  }
+
+  // The fact that *addr is volatile should cause the compiler to
+  // automatically generate an st8.rel.
+  inline static void
+  release_set(volatile obj_addr_t *addr, obj_addr_t new_val)
+  {
+    __asm__ __volatile__(" " : : : "memory");
+    *(addr) = new_val;
+  }
+
+  inline static bool
+  compare_and_swap_release(volatile obj_addr_t *addr,
+	 				               obj_addr_t old,
+						       obj_addr_t new_val) 
+  {
+    unsigned long oldval;
+    __asm__ __volatile__("mov ar.ccv=%4 ;; cmpxchg8.rel %0=%1,%2,ar.ccv"
+		: "=r"(oldval), "=m"(*addr)
+		: "r"(new_val), "1"(*addr), "r"(old) : "memory");
+    return (oldval == old);
+  }
+#endif
+
+// Try to determine whether we are on a multiprocessor, i.e. whether
+// spinning may be profitable.
+// This should really use a suitable autoconf macro.
+// False is the conservative answer, though the right one is much better.
+static bool
+is_mp()
+{
+  long nprocs = sysconf(_SC_NPROCESSORS_ONLN);
+  return (nprocs > 1);
+}
+
+// A call to keep_live(p) forces p to be accessible to the GC
+// at this point.
+inline static void
+keep_live(obj_addr_t p)
+{
+    __asm__ __volatile__("" : : "rm"(p) : "memory");
+}
+
+
+// Each hash table entry holds a single preallocated "lightweight" lock.
+// In addition, it holds a chain of "heavyweight" locks.  Lightweight
+// locks do not support Object.wait(), and are converted to heavyweight
+// status in response to contention.  Unlike the SGI scheme, both
+// ligtweight and heavyweight locks in one hash entry can be simultaneously
+// in use.  (The SGI scheme requires that we be able to acquire a heavyweight
+// lock on behalf of another thread, and can thus convert a lock we don't
+// hold to heavyweight status.  Here we don't insist on that, and thus
+// let the original holder of the lighweight lock keep it.)
+
+struct heavy_lock {
+  void * reserved_for_gc;
+  struct heavy_lock *next;	// Hash chain link.
+				// The only field traced by GC.
+  obj_addr_t address;		// Object to which this lock corresponds.
+				// Should not be traced by GC.
+  _Jv_SyncInfo si;
+  // The remaining fields save prior finalization info for
+  // the object, which we needed to replace in order to arrange
+  // for cleanup of the lock structure.
+  GC_finalization_proc old_finalization_proc;
+  void * old_client_data;
+};
+
+#ifdef LOCK_DEBUG
 void
-_Jv_FinalizeObject (jobject obj)
+print_hl_list(heavy_lock *hl)
 {
-  // Ignore exceptions.  From section 12.6 of the Java Language Spec.
-  try
+    heavy_lock *p = hl;
+    for (; 0 != p; p = p->next)
+      fprintf (stderr, "(hl = %p, addr = %p)", p, (void *)(p -> address));
+}
+#endif /* LOCK_DEBUG */
+
+#if defined (_Jv_HaveCondDestroy) || defined (_Jv_HaveMutexDestroy)
+// If we have to run a destructor for a sync_info member, then this
+// function is registered as a finalizer for the sync_info.
+static void
+heavy_lock_finalization_proc (jobject obj)
+{
+  heavy_lock *hl = (heavy_lock *) obj;
+#if defined (_Jv_HaveCondDestroy)
+  _Jv_CondDestroy (&hl->si.condition);
+#endif
+#if defined (_Jv_HaveMutexDestroy)
+  _Jv_MutexDestroy (&hl->si.mutex);
+#endif
+  hl->si.init = false;
+}
+#endif /* defined (_Jv_HaveCondDestroy) || defined (_Jv_HaveMutexDestroy) */
+
+// We convert the lock back to lightweight status when
+// we exit, so that a single contention episode doesn't doom the lock
+// forever.  But we also need to make sure that lock structures for dead
+// objects are eventually reclaimed.  We do that in a an additional
+// finalizer on the underlying object.
+// Note that if the corresponding object is dead, it is safe to drop
+// the heavy_lock structure from its list.  It is not necessarily
+// safe to deallocate it, since the unlock code could still be running.
+
+struct hash_entry {
+  volatile obj_addr_t address;	// Address of object for which lightweight
+  				// k is held.
+				// We assume the 3 low order bits are zero.
+				// With the Boehm collector and bitmap
+				// allocation, objects of size 4 bytes are
+				// broken anyway.  Thus this is primarily
+				// a constraint on statically allocated
+				// objects used for synchronization.
+				// This allows us to use the low order
+  				// bits as follows:
+#   define LOCKED 	1 	// This hash entry is locked, and its
+  				// state may be invalid.
+  				// The lock protects both the hash_entry
+  				// itself (except for the light_count
+  				// and light_thr_id fields, which
+  				// are protected by the lightweight
+  				// lock itself), and any heavy_monitor
+  				// structures attached to it.
+#   define HEAVY	2	// There may be heavyweight locks
+				// associated with this cache entry.
+				// The lightweight entry is still valid,
+  				// if the leading bits of the address
+  				// field are nonzero.
+ 				// Set if heavy_count is > 0 .
+  				// Stored redundantly so a single
+  				// compare-and-swap works in the easy case.
+#   define REQUEST_CONVERSION 4 // The lightweight lock is held.  But
+  				// one or more other threads have tried
+  				// to acquire the lock, and hence request
+  				// conversion to heavyweight status.
+#   define FLAGS (LOCKED | HEAVY | REQUEST_CONVERSION)
+  volatile _Jv_ThreadId_t light_thr_id;
+				// Thr_id of holder of lightweight lock.
+  				// Only updated by lightweight lock holder.
+				// Must be recognizably invalid if the
+				// lightweight lock is not held.
+#   define INVALID_THREAD_ID 0  // Works for Linux?
+				// If zero doesn't work, we have to
+				// initialize lock table.
+  volatile unsigned short light_count;
+				// Number of times the lightweight lock
+  				// is held minus one.  Zero if lightweight
+  				// lock is not held.
+  unsigned short heavy_count; 	// Total number of times heavyweight locks
+  				// associated with this hash entry are held
+  				// or waiting to be acquired.
+  				// Threads in wait() are included eventhough
+  				// they have temporarily released the lock.
+  struct heavy_lock * heavy_locks;
+  				// Chain of heavy locks.  Protected
+  				// by lockbit for he.  Locks may
+  				// remain allocated here even if HEAVY
+  				// is not set and heavy_count is 0.
+  				// If a lightweight and hevyweight lock
+  				// correspond to the same address, the
+  				// lightweight lock is the right one.
+};
+
+#ifndef JV_SYNC_TABLE_SZ
+# define JV_SYNC_TABLE_SZ 1024
+#endif
+
+hash_entry light_locks[JV_SYNC_TABLE_SZ];
+
+#define JV_SYNC_HASH(p) (((long)p ^ ((long)p >> 10)) % JV_SYNC_TABLE_SZ)
+
+#ifdef LOCK_DEBUG
+  void print_he(hash_entry *he)
+  {
+     fprintf(stderr, "lock hash entry = %p, index = %d, address = 0x%lx\n"
+		     "\tlight_thr_id = 0x%lx, light_count = %d, "
+		     "heavy_count = %d\n\theavy_locks:", he,
+		     he - light_locks, he -> address, he -> light_thr_id,
+		     he -> light_count, he -> heavy_count);
+     print_hl_list(he -> heavy_locks);
+     fprintf(stderr, "\n");
+  }
+#endif /* LOCK_DEBUG */
+
+// Wait for roughly 2^n units, touching as little memory as possible.
+static void
+spin(unsigned n)
+{
+  const unsigned MP_SPINS = 10;
+  const unsigned YIELDS = 4;
+  const unsigned SPINS_PER_UNIT = 30;
+  const unsigned MIN_SLEEP_USECS = 2001; // Shorter times spin under Linux.
+  const unsigned MAX_SLEEP_USECS = 200000;
+  static unsigned spin_limit = 0;
+  static unsigned yield_limit = YIELDS;
+  static bool mp = false;
+  static bool spin_initialized = false;
+
+  if (!spin_initialized)
     {
-      obj->finalize ();
+      mp = is_mp();
+      if (mp)
+	{
+	  spin_limit = MP_SPINS;
+	  yield_limit = MP_SPINS + YIELDS;
+	}
+      spin_initialized = true;
     }
-  catch (java::lang::Throwable *t)
+  if (n < spin_limit)
     {
-      // Ignore.
+      unsigned i = SPINS_PER_UNIT << n;
+      for (; i > 0; --i)
+        __asm__ __volatile__("");
+    }
+  else if (n < yield_limit)
+    {
+      sched_yield();
+    }
+  else
+    {
+      unsigned duration = MIN_SLEEP_USECS << (n - yield_limit);
+      if (n >= 15 + yield_limit || duration > MAX_SLEEP_USECS)
+	duration = MAX_SLEEP_USECS;
+      usleep(duration);
+    }
+}
+
+// Wait for a hash entry to become unlocked.
+static void
+wait_unlocked (hash_entry *he)
+{
+  unsigned i = 0;
+  while (he -> address & LOCKED)
+    spin (i++);
+}
+
+// Return the heavy lock for addr if it was already allocated.
+// The client passes in the appropriate hash_entry.
+// We hold the lock for he.
+static inline heavy_lock *
+find_heavy (obj_addr_t addr, hash_entry *he)
+{
+  heavy_lock *hl = he -> heavy_locks;
+  while (hl != 0 && hl -> address != addr) hl = hl -> next;
+  return hl;
+}
+
+// Unlink the heavy lock for the given address from its hash table chain.
+// Dies miserably and conspicuously if it's not there, since that should
+// be impossible.
+static inline void
+unlink_heavy (obj_addr_t addr, hash_entry *he)
+{
+  heavy_lock **currentp = &(he -> heavy_locks);
+  while ((*currentp) -> address != addr)
+    currentp = &((*currentp) -> next);
+  *currentp = (*currentp) -> next;
+}
+
+// Finalization procedure for objects that have associated heavy-weight
+// locks.  This may replace the real finalization procedure.
+static void
+heavy_lock_obj_finalization_proc (void *obj, void *cd)
+{
+  heavy_lock *hl = (heavy_lock *)cd;
+  obj_addr_t addr = (obj_addr_t)obj;
+  GC_finalization_proc old_finalization_proc = hl -> old_finalization_proc;
+  void * old_client_data = hl -> old_client_data;
+
+  if (old_finalization_proc != 0)
+    {
+      // We still need to run a real finalizer.  In an idealized
+      // world, in which people write thread-safe finalizers, that is
+      // likely to require synchronization.  Thus we reregister
+      // ourselves as the only finalizer, and simply run the real one.
+      // Thus we don't clean up the lock yet, but we're likely to do so
+      // on the next GC cycle.
+      hl -> old_finalization_proc = 0;
+      hl -> old_client_data = 0;
+#     ifdef HAVE_BOEHM_GC
+        GC_REGISTER_FINALIZER_NO_ORDER(obj, heavy_lock_obj_finalization_proc, cd, 0, 0);
+#     endif
+      old_finalization_proc(obj, old_client_data);
+    }
+  else
+    {
+      // The object is really dead, although it's conceivable that
+      // some thread may still be in the process of releasing the
+      // heavy lock.  Unlink it and, if necessary, register a finalizer
+      // to distroy sync_info.
+      hash_entry *he = light_locks + JV_SYNC_HASH(addr);
+      obj_addr_t address = (he -> address & ~LOCKED);
+      while (!compare_and_swap(&(he -> address), address, address | LOCKED ))
+	{
+	  // Hash table entry is currently locked.  We can't safely touch
+	  // touch the list of heavy locks.  
+	  wait_unlocked(he);
+	  address = (he -> address & ~LOCKED);
+	}
+      unlink_heavy(addr, light_locks + JV_SYNC_HASH(addr));
+      release_set(&(he -> address), address);
+#     if defined (_Jv_HaveCondDestroy) || defined (_Jv_HaveMutexDestroy)
+        // Register a finalizer, yet again.
+          hl->si.init = true;
+          _Jv_RegisterFinalizer (hl, heavy_lock_finalization_proc);
+#     endif
+    }
+}
+
+// Allocate a new heavy lock for addr, returning its address.
+// Assumes we already have the hash_entry locked, and there
+// is currently no lightweight or allocated lock for addr.
+// We register a finalizer for addr, which is responsible for
+// removing the heavy lock when addr goes away, in addition
+// to the responsibilities of any prior finalizer.
+static heavy_lock *
+alloc_heavy(obj_addr_t addr, hash_entry *he)
+{
+  heavy_lock * hl = (heavy_lock *) _Jv_AllocTraceOne(sizeof (heavy_lock));
+  
+  hl -> address = addr;
+  _Jv_MutexInit (&(hl -> si.mutex));
+  _Jv_CondInit (&(hl -> si.condition));
+# if defined (_Jv_HaveCondDestroy) || defined (_Jv_HaveMutexDestroy)
+    si->init = true;  // needed ?
+# endif
+  hl -> next = he -> heavy_locks;
+  he -> heavy_locks = hl;
+  // FIXME: The only call that cheats and goes directly to the GC interface.
+# ifdef HAVE_BOEHM_GC
+    GC_REGISTER_FINALIZER_NO_ORDER(
+		    	  (void *)addr, heavy_lock_obj_finalization_proc,
+			  hl, &hl->old_finalization_proc,
+			  &hl->old_client_data);
+# endif /* HAVE_BOEHM_GC */
+  return hl;
+}
+
+// Return the heavy lock for addr, allocating if necessary.
+// Assumes we have the cache entry locked, and there is no lightweight
+// lock for addr.
+static heavy_lock *
+get_heavy(obj_addr_t addr, hash_entry *he)
+{
+  heavy_lock *hl = find_heavy(addr, he);
+  if (0 == hl)
+    hl = alloc_heavy(addr, he);
+  return hl;
+}
+
+void
+_Jv_MonitorEnter (jobject obj)
+{
+  obj_addr_t addr = (obj_addr_t)obj;
+  obj_addr_t address;
+  unsigned hash = JV_SYNC_HASH(addr);
+  hash_entry * he = light_locks + hash;
+  _Jv_ThreadId_t self = _Jv_ThreadSelf();
+  unsigned count;
+  const unsigned N_SPINS = 18;
+
+  assert(!(addr & FLAGS));
+retry:
+  if (__builtin_expect(compare_and_swap(&(he -> address),
+					0, addr),true))
+    {
+      assert(he -> light_thr_id == INVALID_THREAD_ID);
+      assert(he -> light_count == 0);
+      he -> light_thr_id = self;
+      // Count fields are set correctly.  Heavy_count was also zero,
+      // but can change asynchronously.
+      // This path is hopefully both fast and the most common.
+      return;
     }
+  address = he -> address;
+  if ((address & ~(HEAVY | REQUEST_CONVERSION)) == addr)
+    {
+      if (he -> light_thr_id == self)
+	{
+	  // We hold the lightweight lock, and it's for the right
+	  // address.
+	  count = he -> light_count;
+	  if (count == USHRT_MAX)
+	    {
+	      // I think most JVMs don't check for this.
+	      // But I'm not convinced I couldn't turn this into a security
+	      // hole, even with a 32 bit counter.
+	      throw new java::lang::IllegalMonitorStateException(
+		JvNewStringLatin1("maximum monitor nesting level exceeded")); 
+	    }
+	  he -> light_count = count + 1;
+	  return;
+	}
+      else
+	{
+	  // Lightweight lock is held, but by somone else.
+          // Spin a few times.  This avoids turning this into a heavyweight
+    	  // lock if the current holder is about to release it.
+          for (unsigned int i = 0; i < N_SPINS; ++i)
+	    {
+	      if ((he -> address & ~LOCKED) != (address & ~LOCKED)) goto retry;
+	      spin(i);
+            }
+	  address &= ~LOCKED;
+	  if (!compare_and_swap(&(he -> address), address, address | LOCKED ))
+	    {
+	      wait_unlocked(he);      
+	      goto retry;
+	    }
+	  heavy_lock *hl = get_heavy(addr, he);
+	  ++ (he -> heavy_count);
+	  // The hl lock acquisition can't block for long, since it can
+	  // only be held by other threads waiting for conversion, and
+	  // they, like us, drop it quickly without blocking.
+	  _Jv_MutexLock(&(hl->si.mutex));
+	  assert(he -> address == address | LOCKED );
+	  release_set(&(he -> address), (address | REQUEST_CONVERSION | HEAVY));
+				// release lock on he
+	  while ((he -> address & ~FLAGS) == (address & ~FLAGS))
+	    {
+	      // Once converted, the lock has to retain heavyweight
+	      // status, since heavy_count > 0 . 
+	      _Jv_CondWait (&(hl->si.condition), &(hl->si.mutex), 0, 0);
+	    }
+	  keep_live(addr);
+		// Guarantee that hl doesn't get unlinked by finalizer.
+		// This is only an issue if the client fails to release
+		// the lock, which is unlikely.
+	  assert(he -> address & HEAVY);
+	  // Lock has been converted, we hold the heavyweight lock,
+	  // heavy_count has been incremented.
+	  return;
+        }
+    }
+  obj_addr_t was_heavy = (address & HEAVY);
+  address &= ~LOCKED;
+  if (!compare_and_swap(&(he -> address), address, (address | LOCKED )))
+    {
+      wait_unlocked(he);
+      goto retry;
+    }
+  if ((address & ~(HEAVY | REQUEST_CONVERSION)) == 0)
+    {
+      // Either was_heavy is true, or something changed out from under us,
+      // since the initial test for 0 failed.
+      assert(!(address & REQUEST_CONVERSION));
+	// Can't convert a nonexistent lightweight lock.
+      heavy_lock *hl;
+      hl = (was_heavy? find_heavy(addr, he) : 0);
+      if (0 == hl)
+        {
+	  // It is OK to use the lighweight lock, since either the
+	  // heavyweight lock does not exist, or none of the
+	  // heavyweight locks currently exist.  Future threads
+	  // trying to acquire the lock will see the lightweight
+	  // one first and use that.
+	  he -> light_thr_id = self;  // OK, since nobody else can hold
+				      // light lock or do this at the same time.
+	  assert(he -> light_count == 0);
+	  assert(was_heavy == (he -> address & HEAVY));
+	  release_set(&(he -> address), (addr | was_heavy));
+        }
+      else
+	{
+	  // Must use heavy lock.
+	  ++ (he -> heavy_count);
+	  assert(0 == (address & ~HEAVY));
+          release_set(&(he -> address), HEAVY);
+          _Jv_MutexLock(&(hl->si.mutex));
+	  keep_live(addr);
+        }
+      return;
+    }
+  // Lightweight lock is held, but does not correspond to this object.
+  // We hold the lock on the hash entry, and he -> address can't
+  // change from under us.  Neither can the chain of heavy locks.
+    {
+      assert(0 == he -> heavy_count || (address & HEAVY));
+      heavy_lock *hl = get_heavy(addr, he);
+      ++ (he -> heavy_count);
+      release_set(&(he -> address), address | HEAVY);
+      _Jv_MutexLock(&(hl->si.mutex));
+      keep_live(addr);
+    }
 }
+
+
+void
+_Jv_MonitorExit (jobject obj)
+{
+  obj_addr_t addr = (obj_addr_t)obj;
+  _Jv_ThreadId_t self = _Jv_ThreadSelf();
+  unsigned hash = JV_SYNC_HASH(addr);
+  hash_entry * he = light_locks + hash;
+  _Jv_ThreadId_t light_thr_id;
+  unsigned count;
+  obj_addr_t address;
+
+retry:
+  light_thr_id = he -> light_thr_id;
+  // Unfortunately, it turns out we always need to read the address
+  // first.  Even if we are going to update it with compare_and_swap,
+  // we need to reset light_thr_id, and that's not safe unless we know
+  // know that we hold the lock.
+  address = he -> address;
+  // First the (relatively) fast cases:
+  if (__builtin_expect(light_thr_id == self, true))
+    {
+      count = he -> light_count;
+      if (__builtin_expect((address & ~HEAVY) == addr, true))
+	{
+          if (count != 0)
+            {
+	      // We held the lightweight lock all along.  Thus the values
+	      // we saw for light_thr_id and light_count must have been valid. 
+	      he -> light_count = count - 1;
+	      return;
+            }
+	  else
+	    {
+	      // We hold the lightweight lock once.
+	      he -> light_thr_id = INVALID_THREAD_ID;
+              if (compare_and_swap_release(&(he -> address), address,
+					   address & HEAVY))
+	        return;
+	      else
+		{
+	          he -> light_thr_id = light_thr_id; // Undo prior damage.
+	          goto retry;
+	        }
+            }
+        }
+      // else lock is not for this address, conversion is requested,
+      // or the lock bit in the address field is set.
+    }
+  else
+    {
+      if ((address & ~(HEAVY | REQUEST_CONVERSION)) == addr)
+	{
+#	  ifdef LOCK_DEBUG
+	    fprintf(stderr, "Lightweight lock held by other thread\n\t"
+			    "light_thr_id = 0x%lx, self = 0x%lx, "
+			    "address = 0x%lx, pid = %d\n",
+			    light_thr_id, self, address, getpid());
+	    print_he(he);
+	    for(;;) {}
+#	  endif
+	  // Someone holds the lightweight lock for this object, and
+	  // it can't be us.
+	  throw new java::lang::IllegalMonitorStateException(
+			JvNewStringLatin1("current thread not owner"));
+        }
+      else
+	count = he -> light_count;
+    }
+  if (address & LOCKED)
+    {
+      wait_unlocked(he);
+      goto retry;
+    }
+  // Now the unlikely cases.
+  // We do know that:
+  // - Address is set, and doesn't contain the LOCKED bit.
+  // - If address refers to the same object as addr, then he -> light_thr_id
+  //   refers to this thread, and count is valid.
+  // - The case in which we held the lightweight lock has been
+  //   completely handled, except for the REQUEST_CONVERSION case.
+  //   
+  if ((address & ~FLAGS) == addr)
+    {
+      // The lightweight lock is assigned to this object.
+      // Thus we must be in the REQUEST_CONVERSION case.
+      if (0 != count)
+        {
+	  // Defer conversion until we exit completely.
+	  he -> light_count = count - 1;
+	  return;
+        }
+      assert(he -> light_thr_id == self);
+      assert(address & REQUEST_CONVERSION);
+      // Conversion requested
+      // Convert now.
+      if (!compare_and_swap(&(he -> address), address, address | LOCKED))
+	goto retry;
+      heavy_lock *hl = find_heavy(addr, he);
+      assert (0 != hl);
+		// Requestor created it.
+      he -> light_count = 0;
+      assert(he -> heavy_count > 0);
+	  	// was incremented by requestor.
+      _Jv_MutexLock(&(hl->si.mutex));
+	// Release the he lock after acquiring the mutex.
+	// Otherwise we can accidentally
+	// notify a thread that has already seen a heavyweight
+	// lock.
+      he -> light_thr_id = INVALID_THREAD_ID;
+      release_set(&(he -> address), HEAVY);
+	  	// lightweight lock now unused.
+      _Jv_CondNotifyAll(&(hl->si.condition), &(hl->si.mutex));
+      _Jv_MutexUnlock(&(hl->si.mutex));
+      // heavy_count was already incremented by original requestor.
+      keep_live(addr);
+      return;
+    }
+  // lightweight lock not for this object.
+  assert(!(address & LOCKED));
+  assert((address & ~FLAGS) != addr);
+  if (!compare_and_swap(&(he -> address), address, address | LOCKED))
+	goto retry;
+  heavy_lock *hl = find_heavy(addr, he);
+  if (NULL == hl)
+    {
+#     ifdef LOCK_DEBUG
+	fprintf(stderr, "Failed to find heavyweight lock for addr 0x%lx"
+			" pid = %d\n", addr, getpid());
+	print_he(he);
+	for(;;) {}
+#     endif
+      throw new java::lang::IllegalMonitorStateException(
+			JvNewStringLatin1("current thread not owner"));
+    }
+  assert(address & HEAVY);
+  count = he -> heavy_count;
+  assert(count > 0);
+  --count;
+  if (0 == count) address &= ~HEAVY;
+  he -> heavy_count = count;
+  release_set(&(he -> address), address);
+    				// release lock bit, preserving
+				// REQUEST_CONVERSION
+    				// and object address.
+  _Jv_MutexUnlock(&(hl->si.mutex));
+  			// Unlock after releasing the lock bit, so that
+  			// we don't switch to another thread prematurely.
+  keep_live(addr);
+}     
+
+// The rest of these are moderately thin veneers on _Jv_Cond ops.
+// The current version of Notify might be able to make the pthread
+// call AFTER releasing the lock, thus saving some context switches??
+
+void
+java::lang::Object::wait (jlong timeout, jint nanos)
+{
+  obj_addr_t addr = (obj_addr_t)this;
+  _Jv_ThreadId_t self = _Jv_ThreadSelf();
+  unsigned hash = JV_SYNC_HASH(addr);
+  hash_entry * he = light_locks + hash;
+  unsigned count;
+  obj_addr_t address;
+  heavy_lock *hl;
+    
+  if (__builtin_expect (timeout < 0 || nanos < 0 || nanos > 999999, false))
+    throw new IllegalArgumentException;
+retry:
+  address = he -> address;
+  address &= ~LOCKED;
+  if (!compare_and_swap(&(he -> address), address, address | LOCKED))
+    {
+      wait_unlocked(he);
+      goto retry;
+    }
+  // address does not have the lock bit set.  We hold the lock on he.
+  if ((address & ~FLAGS) == addr)
+    {
+      // Convert to heavyweight.
+	if (he -> light_thr_id != self)
+	  {
+#	    ifdef LOCK_DEBUG
+	      fprintf(stderr, "Found wrong lightweight lock owner in wait "
+			      "address = 0x%lx pid = %d\n", address, getpid());
+	      print_he(he);
+	      for(;;) {}
+#	    endif
+	    release_set(&(he -> address), address);
+	    throw new IllegalMonitorStateException (JvNewStringLatin1 
+                          ("current thread not owner"));
+	  }
+	count = he -> light_count;
+	hl = get_heavy(addr, he);
+	he -> light_count = 0;
+	he -> heavy_count += count + 1;
+	for (unsigned i = 0; i <= count; ++i)
+	  _Jv_MutexLock(&(hl->si.mutex));
+	// Again release the he lock after acquiring the mutex.
+        he -> light_thr_id = INVALID_THREAD_ID;
+	release_set(&(he -> address), HEAVY);  // lightweight lock now unused.
+	if (address & REQUEST_CONVERSION)
+	  _Jv_CondNotify (&(hl->si.condition), &(hl->si.mutex));
+    }
+  else /* We should hold the heavyweight lock. */
+    {
+      hl = find_heavy(addr, he);
+      release_set(&(he -> address), address);
+      if (0 == hl)
+	{
+#	  ifdef LOCK_DEBUG
+	    fprintf(stderr, "Couldn't find heavy lock in wait "
+		 	    "addr = 0x%lx pid = %d\n", addr, getpid());
+	    print_he(he);
+	    for(;;) {}
+#	  endif
+	  throw new IllegalMonitorStateException (JvNewStringLatin1 
+                          ("current thread not owner"));
+	}
+      assert(address & HEAVY);
+    }
+  switch (_Jv_CondWait (&(hl->si.condition), &(hl->si.mutex), timeout, nanos))
+    {
+      case _JV_NOT_OWNER:
+	throw new IllegalMonitorStateException (JvNewStringLatin1 
+                          ("current thread not owner"));        
+      case _JV_INTERRUPTED:
+	if (Thread::interrupted ())
+	  throw new InterruptedException;        
+    }
+}
+
+void
+java::lang::Object::notify (void)
+{
+  obj_addr_t addr = (obj_addr_t)this;
+  _Jv_ThreadId_t self = _Jv_ThreadSelf();
+  unsigned hash = JV_SYNC_HASH(addr);
+  hash_entry * he = light_locks + hash;
+  heavy_lock *hl;
+  obj_addr_t address;
+  int result;
+
+retry:
+  address = ((he -> address) & ~LOCKED);
+  if (!compare_and_swap(&(he -> address), address, address | LOCKED))
+    {
+      wait_unlocked(he);
+      goto retry;
+    }
+  if ((address & ~FLAGS) == addr && he -> light_thr_id == self)
+    {
+      // We hold lightweight lock.  Since it has not
+      // been inflated, there are no waiters.
+      release_set(&(he -> address), address);	// unlock
+      return;
+    }
+  hl = find_heavy(addr, he);
+  // Hl can't disappear since we point to the underlying object.
+  // It's important that we release the lock bit before the notify, since
+  // otherwise we will try to wake up thee target while we still hold the
+  // bit.  This results in lock bit contention, which we don't handle
+  // terribly well.
+  release_set(&(he -> address), address); // unlock
+  if (0 == hl)
+    {
+      throw new IllegalMonitorStateException(JvNewStringLatin1 
+                                              ("current thread not owner"));
+      return;
+    }
+  result = _Jv_CondNotify(&(hl->si.condition), &(hl->si.mutex));
+  keep_live(addr);
+  if (__builtin_expect (result, 0))
+    throw new IllegalMonitorStateException(JvNewStringLatin1 
+                                              ("current thread not owner"));
+}
+
+void
+java::lang::Object::notifyAll (void)
+{
+  obj_addr_t addr = (obj_addr_t)this;
+  _Jv_ThreadId_t self = _Jv_ThreadSelf();
+  unsigned hash = JV_SYNC_HASH(addr);
+  hash_entry * he = light_locks + hash;
+  heavy_lock *hl;
+  obj_addr_t address;
+  int result;
+
+retry:
+  address = (he -> address) & ~LOCKED;
+  if (!compare_and_swap(&(he -> address), address, address | LOCKED))
+    {
+      wait_unlocked(he);
+      goto retry;
+    }
+  hl = find_heavy(addr, he);
+  if ((address & ~FLAGS) == addr && he -> light_thr_id == self)
+    {
+      // We hold lightweight lock.  Since it has not
+      // been inflated, there are no waiters.
+      release_set(&(he -> address), address);	// unlock
+      return;
+    }
+  release_set(&(he -> address), address); // unlock
+  if (0 == hl)
+    {
+      throw new IllegalMonitorStateException(JvNewStringLatin1 
+                                              ("current thread not owner"));
+    }
+  result = _Jv_CondNotifyAll(&(hl->si.condition), &(hl->si.mutex));
+  if (__builtin_expect (result, 0))
+    throw new IllegalMonitorStateException(JvNewStringLatin1 
+                                              ("current thread not owner"));
+}
+
+// This is declared in Java code and in Object.h.
+// It should never be called with JV_HASH_SYNCHRONIZATION
+void
+java::lang::Object::sync_init (void)
+{
+  throw new IllegalMonitorStateException(JvNewStringLatin1 
+                                              ("internal error: sync_init"));
+}
+
+// This is called on startup and declared in Object.h.
+// For now we just make it a no-op.
+void
+_Jv_InitializeSyncMutex (void)
+{
+}
+
+#endif /* JV_HASH_SYNCHRONIZATION */
+
Index: java/lang/natString.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/natString.cc,v
retrieving revision 1.21
diff -u -r1.21 natString.cc
--- natString.cc	2001/05/22 04:38:35	1.21
+++ natString.cc	2001/05/24 01:06:17
@@ -121,8 +121,7 @@
   if (strhash == NULL)
     {
       strhash_size = 1024;
-      strhash = (jstring *) _Jv_AllocBytesChecked (strhash_size
-						   * sizeof (jstring));
+      strhash = (jstring *) _Jv_AllocBytes (strhash_size * sizeof (jstring));
       memset (strhash, 0, strhash_size * sizeof (jstring));
     }
   else
@@ -130,8 +129,7 @@
       int i = strhash_size;
       jstring* ptr = strhash + i;
       int nsize = strhash_size * 2;
-      jstring *next = (jstring *) _Jv_AllocBytesChecked (nsize
-							 * sizeof (jstring));
+      jstring *next = (jstring *) _Jv_AllocBytes (nsize * sizeof (jstring));
       memset (next, 0, nsize * sizeof (jstring));
 
       while (--i >= 0)
@@ -392,8 +390,18 @@
 {
   jsize sz = sizeof(java::lang::String) + len * sizeof(jchar);
 
-  jstring obj = (jstring) JvAllocObject(&StringClass, sz);
-
+  // We assert that for strings allocated this way, the data field
+  // will always point to the object itself.  Thus there is no reason
+  // for the garbage collector to scan any of it.
+  // Furthermore, we're about to overwrite the string data, so
+  // initialization of the object is not an issue.
+#ifdef ENABLE_JVMPI
+  jstring obj = (jstring) _Jv_AllocPtrFreeObject(&StringClass, sz);
+#else
+  // Class needs no initialization, and there is no finalizer, so
+  // we can go directly to the collector's allocator interface.
+  jstring obj = (jstring) _Jv_AllocPtrFreeObj(&StringClass, sz);
+#endif
   obj->data = obj;
   obj->boffset = sizeof(java::lang::String);
   obj->count = len;
Index: java/net/natInetAddress.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/net/natInetAddress.cc,v
retrieving revision 1.17
diff -u -r1.17 natInetAddress.cc
--- natInetAddress.cc	2001/04/25 20:27:06	1.17
+++ natInetAddress.cc	2001/05/24 01:06:17
@@ -95,7 +95,7 @@
   if (len < 100)
     hostname = buf;
   else
-    hostname = (char*) _Jv_AllocBytesChecked (len+1);
+    hostname = (char*) _Jv_AllocBytes (len+1);
   JvGetStringUTFRegion (host, 0, host->length(), hostname);
   buf[len] = '\0';
   char* bytes = NULL;
@@ -180,7 +180,7 @@
       if (len < 100)
 	hostname = buf;
       else
-	hostname = (char*) _Jv_AllocBytesChecked (len+1);
+	hostname = (char*) _Jv_AllocBytes (len+1);
       JvGetStringUTFRegion (host, 0, host->length(), hostname);
       buf[len] = '\0';
 #ifdef HAVE_GETHOSTBYNAME_R
@@ -201,7 +201,7 @@
 	  if (! ok && herr == ERANGE)
 	    {
 	      size_r *= 2;
-	      buffer_r = (char *) _Jv_AllocBytesChecked (size_r);
+	      buffer_r = (char *) _Jv_AllocBytes (size_r);
 	    }
 	  else
 #endif /* HAVE_STRUCT_HOSTENT_DATA */
@@ -255,7 +255,7 @@
 	  if (! ok && herr == ERANGE)
 	    {
 	      size_r *= 2;
-	      buffer_r = (char *) _Jv_AllocBytesChecked (size_r);
+	      buffer_r = (char *) _Jv_AllocBytes (size_r);
 	    }
 	  else 
 #endif /* HAVE_STRUCT_HOSTENT_DATA */


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