? libjava/gcj/hash_sync.h Index: boehm-gc/configure =================================================================== RCS file: /cvs/gcc/gcc/boehm-gc/configure,v retrieving revision 1.27 diff -u -r1.27 configure --- configure 2000/12/30 12:18:38 1.27 +++ configure 2001/02/02 18:45:13 @@ -1549,7 +1549,7 @@ ;; esac -boehm_gc_cflags="${boehm_gc_cflags} -fno-builtin" +boehm_gc_cflags="${boehm_gc_cflags} -fno-builtin -fexceptions" BOEHM_GC_CFLAGS=${boehm_gc_cflags} Index: boehm-gc/configure.host =================================================================== RCS file: /cvs/gcc/gcc/boehm-gc/configure.host,v retrieving revision 1.2 diff -u -r1.2 configure.host --- configure.host 1999/06/30 12:43:22 1.2 +++ configure.host 2001/02/02 18:45:13 @@ -17,7 +17,7 @@ # It sets the following shell variables: # boehm_gc_cflags Special CFLAGS to use when building -boehm_gc_cflags= +boehm_gc_cflags= -fexceptions case "${target_optspace}:${host}" in yes:*) Index: boehm-gc/finalize.c =================================================================== RCS file: /cvs/gcc/gcc/boehm-gc/finalize.c,v retrieving revision 1.5 diff -u -r1.5 finalize.c --- finalize.c 2000/05/07 00:43:49 1.5 +++ finalize.c 2001/02/02 18:45:14 @@ -324,6 +324,7 @@ struct finalizable_object * curr_fo, * prev_fo; int index; struct finalizable_object *new_fo; + hdr *hhdr; DCL_LOCK_STATE; # ifdef THREADS @@ -402,6 +403,19 @@ # endif return; } + GET_HDR(base, hhdr); + if (0 == hhdr) { + /* We won't collect it, hence finalizer wouldn't be run. */ + /* This is changed for gcj, but it will be in version 6.0 of the */ + /* standard collector distribution. It costs virtually nothing */ + /* here, but it's expensive to check in the hash synchronization */ + /* code, where it matters. -HB */ +# ifdef THREADS + UNLOCK(); + ENABLE_SIGNALS(); +# endif + return; + } # ifdef THREADS new_fo = (struct finalizable_object *) GC_generic_malloc_inner(sizeof(struct finalizable_object),NORMAL); @@ -413,7 +427,7 @@ new_fo -> fo_hidden_base = (word)HIDE_POINTER(base); new_fo -> fo_fn = fn; new_fo -> fo_client_data = (ptr_t)cd; - new_fo -> fo_object_size = GC_size(base); + new_fo -> fo_object_size = hhdr -> hb_sz; new_fo -> fo_mark_proc = mp; fo_set_next(new_fo, fo_head[index]); GC_fo_entries++; Index: boehm-gc/gc_priv.h =================================================================== RCS file: /cvs/gcc/gcc/boehm-gc/gc_priv.h,v retrieving revision 1.14 diff -u -r1.14 gc_priv.h --- gc_priv.h 2000/09/04 21:46:26 1.14 +++ gc_priv.h 2001/02/02 18:45:14 @@ -890,6 +890,18 @@ # define SMALL_OBJ(bytes) ((bytes) <= WORDS_TO_BYTES(MAXOBJSZ)) # define ADD_SLOP(bytes) (bytes) # endif +# ifndef MIN_WORDS + /* MIN_WORDS is the size of the smallest allocated object. */ + /* 1 and 2 are the only valid values. */ + /* 2 must be used if: */ + /* - GC_gcj_malloc can be used for objects of size */ + /* smaller than 2 words, or */ +# if defined(GC_GCJ_SUPPORT) +# define MIN_WORDS 2 /* Smallest allocated object. */ +# else +# define MIN_WORDS 1 +# endif +# endif /* Index: boehm-gc/gcj_mlc.c =================================================================== RCS file: /cvs/gcc/gcc/boehm-gc/gcj_mlc.c,v retrieving revision 1.2 diff -u -r1.2 gcj_mlc.c --- gcj_mlc.c 2000/12/23 02:23:27 1.2 +++ gcj_mlc.c 2001/02/02 18:45:14 @@ -267,7 +267,6 @@ } else { *opp = obj_link(op); GC_words_allocd += lw; - FASTUNLOCK(); } *(void **)op = ptr_to_struct_containing_descr; UNLOCK(); Index: boehm-gc/malloc.c =================================================================== RCS file: /cvs/gcc/gcc/boehm-gc/malloc.c,v retrieving revision 1.4 diff -u -r1.4 malloc.c --- malloc.c 2000/04/19 10:09:58 1.4 +++ malloc.c 2001/02/02 18:45:14 @@ -52,7 +52,7 @@ lw = GC_size_map[lb]; # else lw = ALIGNED_WORDS(lb); - if (lw == 0) lw = 1; + if (lw == 0) lw = MIN_WORDS; # endif opp = &(kind -> ok_freelist[lw]); if( (op = *opp) == 0 ) { Index: boehm-gc/misc.c =================================================================== RCS file: /cvs/gcc/gcc/boehm-gc/misc.c,v retrieving revision 1.12 diff -u -r1.12 misc.c --- misc.c 2000/04/19 10:09:58 1.12 +++ misc.c 2001/02/02 18:45:15 @@ -111,9 +111,13 @@ GC_size_map[0] = 1; /* One word objects don't have to be 2 word aligned. */ for (i = 1; i < sizeof(word); i++) { - GC_size_map[i] = 1; + GC_size_map[i] = MIN_WORDS; } - GC_size_map[sizeof(word)] = ROUNDED_UP_WORDS(sizeof(word)); +# if MIN_WORDS > 1 + GC_size_map[sizeof(word)] = MIN_WORDS; +# else + GC_size_map[sizeof(word)] = ROUNDED_UP_WORDS(sizeof(word)); +# endif for (i = sizeof(word) + 1; i <= 8 * sizeof(word); i++) { # ifdef ALIGN_DOUBLE GC_size_map[i] = (ROUNDED_UP_WORDS(i) + 1) & (~1); @@ -202,10 +206,10 @@ */ word GC_stack_last_cleared = 0; /* GC_no when we last did this */ # ifdef THREADS -# define CLEAR_SIZE 2048 -# else -# define CLEAR_SIZE 213 +# define BIG_CLEAR_SIZE 2048 /* Clear this much now and then. */ +# define SMALL_CLEAR_SIZE 256 /* Clear this much every time. */ # endif +# define CLEAR_SIZE 213 /* Granularity for GC_clear_stack_inner */ # define DEGRADE_RATE 50 word GC_min_sp; /* Coolest stack pointer value from which we've */ @@ -262,10 +266,12 @@ { register word sp = (word)GC_approx_sp(); /* Hotter than actual sp */ # ifdef THREADS - word dummy[CLEAR_SIZE]; -# else - register word limit; + word dummy[SMALL_CLEAR_SIZE]; + unsigned random_no = 0; /* Should be more random than it is ... */ + /* Used to occasionally clear a bigger */ + /* chunk. */ # endif + register word limit; # define SLOP 400 /* Extra bytes we clear every time. This clears our own */ @@ -283,7 +289,14 @@ /* thus more junk remains accessible, thus the heap gets */ /* larger ... */ # ifdef THREADS - BZERO(dummy, CLEAR_SIZE*sizeof(word)); + if (++random_no % 13 == 0) { + limit = sp; + MAKE_HOTTER(limit, BIG_CLEAR_SIZE*sizeof(word)); + return GC_lear_stack_inner(arg, limit); + } else { + BZERO(dummy, SMALL_CLEAR_SIZE*sizeof(word)); + return arg; + } # else if (GC_gc_no > GC_stack_last_cleared) { /* Start things over, so we clear the entire stack again */ Index: boehm-gc/test.c =================================================================== RCS file: /cvs/gcc/gcc/boehm-gc/test.c,v retrieving revision 1.4 diff -u -r1.4 test.c --- test.c 2000/04/19 10:09:58 1.4 +++ test.c 2001/02/02 18:45:15 @@ -958,17 +958,17 @@ "This test program is not designed for leak detection mode\n"); (void)GC_printf0("Expect lots of problems.\n"); # endif - if (GC_size(GC_malloc(7)) != 8 + if (GC_size(GC_malloc(7)) != 8 && + GC_size(GC_malloc(7)) != MIN_WORDS * sizeof(GC_word) || GC_size(GC_malloc(15)) != 16) { (void)GC_printf0("GC_size produced unexpected results\n"); FAIL; } - if (GC_size(GC_malloc(0)) != 4 && GC_size(GC_malloc(0)) != 8) { + if (GC_size(GC_malloc(0)) != MIN_WORDS * sizeof(GC_word)) { (void)GC_printf0("GC_malloc(0) failed\n"); FAIL; } - if (GC_size(GC_malloc_uncollectable(0)) != 4 - && GC_size(GC_malloc_uncollectable(0)) != 8) { + if (GC_size(GC_malloc_uncollectable(0)) != MIN_WORDS * sizeof(GC_word)) { (void)GC_printf0("GC_malloc_uncollectable(0) failed\n"); FAIL; } Index: libjava/boehm.cc =================================================================== RCS file: /cvs/gcc/gcc/libjava/boehm.cc,v retrieving revision 1.23 diff -u -r1.23 boehm.cc --- boehm.cc 2001/01/08 23:28:55 1.23 +++ boehm.cc 2001/02/02 18:45:22 @@ -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,13 +332,16 @@ // knows this, so in that case everything else will break, too. #define GCJ_DEFAULT_DESCR MAKE_PROC(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 +#if 0 +// MOVED TO HEADER + // Allocate space for a new Java object. void * _Jv_AllocObj (jsize size, jclass klass) @@ -340,6 +349,34 @@ return GC_GCJ_MALLOC (size, klass->vtable); } +// Allocate uninitialized (except for vtable) space for a +// pointer-free (except possibly for sync_info) object. +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; +} +#endif /* 0 : MOVED TO HEADER */ + +// 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. + memset (r, 0, size); + return r; +} + // Allocate space for a new Java array. // Used only for arrays of objects. void * @@ -368,20 +405,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 +485,11 @@ _Jv_MutexUnlock (&disable_gc_mutex); } +static void * handle_out_of_memory(size_t) +{ + _Jv_ThrowNoMemory(); +} + void _Jv_InitGC (void) { @@ -484,6 +512,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 +542,27 @@ 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: libjava/configure =================================================================== RCS file: /cvs/gcc/gcc/libjava/configure,v retrieving revision 1.77 diff -u -r1.77 configure --- configure 2001/01/12 19:16:04 1.77 +++ configure 2001/02/02 18:45:24 @@ -39,6 +39,9 @@ --disable-getenv-properties don't set system properties from GCJ_PROPERTIES" ac_help="$ac_help + --enable-hash-synchronization + Store monitors in hash table" +ac_help="$ac_help --enable-libgcj-debug enable runtime debugging code" ac_help="$ac_help --enable-interpreter enable interpreter" @@ -2655,6 +2658,16 @@ EOF fi + +if test "${enable_hash_synchronization+set}" = set; then + enableval="$enable_hash_synchronization" + if test "$enable_hash_synchronization" = yes; then + LIBGCJ_JAVAFLAGS="$LIBGCJ_JAVAFLAGS -fhash-synchronization" + LIBGCJ_CXXFLAGS="$LIBGCJ_CXX_FLAGS -DJV_HASH_SYNCHRONIZATION" + echo Set LIBGCJ_JAVAFLAGS to $LIBGCJ_JAVAFLAGS + fi +fi + LIBGCJDEBUG="false" Index: libjava/configure.in =================================================================== RCS file: /cvs/gcc/gcc/libjava/configure.in,v retrieving revision 1.72 diff -u -r1.72 configure.in --- configure.in 2001/01/12 19:16:04 1.72 +++ configure.in 2001/02/02 18:45:24 @@ -64,6 +64,18 @@ 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 "$enable_hash_synchronization" = yes; then + LIBGCJ_JAVAFLAGS="$LIBGCJ_JAVAFLAGS -fhash-synchronization" + LIBGCJ_CXXFLAGS="$LIBGCJ_CXX_FLAGS -DJV_HASH_SYNCHRONIZATION" +fi + dnl See if the user has requested runtime debugging. LIBGCJDEBUG="false" AC_SUBST(LIBGCJDEBUG) Index: libjava/defineclass.cc =================================================================== RCS file: /cvs/gcc/gcc/libjava/defineclass.cc,v retrieving revision 1.13 diff -u -r1.13 defineclass.cc --- defineclass.cc 2000/10/20 23:25:57 1.13 +++ defineclass.cc 2001/02/02 18:45:24 @@ -334,8 +334,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 */ @@ -599,9 +599,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; @@ -939,7 +939,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; } @@ -1006,10 +1006,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; } @@ -1146,11 +1146,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; @@ -1209,7 +1209,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; @@ -1256,7 +1256,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: libjava/jni.cc =================================================================== RCS file: /cvs/gcc/gcc/libjava/jni.cc,v retrieving revision 1.34 diff -u -r1.34 jni.cc --- jni.cc 2000/11/01 00:18:03 1.34 +++ jni.cc 2001/02/02 18:45:25 @@ -1417,7 +1417,8 @@ { try { - return _Jv_MonitorEnter (obj); + _Jv_MonitorEnter (obj); + return 0; } catch (jthrowable t) { @@ -1431,7 +1432,8 @@ { try { - return _Jv_MonitorExit (obj); + _Jv_MonitorExit (obj); + return 0; } catch (jthrowable t) { Index: libjava/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/02/02 18:45:25 @@ -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,12 @@ _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: libjava/posix-threads.cc =================================================================== RCS file: /cvs/gcc/gcc/libjava/posix-threads.cc,v retrieving revision 1.22 diff -u -r1.22 posix-threads.cc --- posix-threads.cc 2000/12/30 12:18:39 1.22 +++ posix-threads.cc 2001/02/02 18:45:25 @@ -295,6 +295,9 @@ sigemptyset (&act.sa_mask); act.sa_flags = 0; sigaction (INTR, &act, NULL); +# if defined(JV_HASH_SYNCHRONIZATION) && defined(NEED_SELF_CACHE) + _Jv_Self_Cache_Init(); +# endif } _Jv_Thread_t * @@ -410,3 +413,29 @@ pthread_cond_wait (&daemon_cond, &daemon_mutex); pthread_mutex_unlock (&daemon_mutex); } + +#if defined(SLOW_PTHREAD_SELF) && defined(NEED_SELF_CACHE) + +// Support for pthread_self() lookup cache. + +volatile self_cache_entry self_cache[SELF_CACHE_SIZE]; + +void _Jv_Self_Cache_Init() +{ + for (unsigned i = 0; i < SELF_CACHE_SIZE; ++i) { + self_cache[i].high_sp_bits = BAD_HIGH_SP_VALUE; + } +} + +_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 && NEED_SELF_CACHE */ Index: libjava/prims.cc =================================================================== RCS file: /cvs/gcc/gcc/libjava/prims.cc,v retrieving revision 1.46 diff -u -r1.46 prims.cc --- prims.cc 2001/01/17 08:13:06 1.46 +++ prims.cc 2001/02/02 18:45:25 @@ -266,7 +266,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; @@ -326,14 +326,11 @@ throw new java::lang::NullPointerException (); } -// Allocate some unscanned memory and throw an exception if no memory. -void * -_Jv_AllocBytesChecked (jsize size) -{ - void *r = _Jv_AllocBytes (size); - if (! r) - _Jv_Throw (no_memory); - return r; +// Explicitly throw a no memory exception. +// The collector calls this when it encounters an out-of-memory condition. +void _Jv_ThrowNoMemory() +{ + _Jv_Throw (no_memory); } // Allocate a new object of class KLASS. SIZE is the size of the object @@ -345,8 +342,6 @@ _Jv_InitClass (klass); jobject obj = (jobject) _Jv_AllocObj (size, klass); - if (__builtin_expect (! obj, false)) - JvThrow (no_memory); // If this class has inherited finalize from Object, then don't // bother registering a finalizer. We know that finalize() is the @@ -374,6 +369,49 @@ 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 (); + } +#endif + + 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 (); @@ -382,6 +420,8 @@ 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 @@ -403,8 +443,6 @@ jclass klass = _Jv_GetArrayClass (elementClass, 0); obj = (jobjectArray) _Jv_AllocArray (size, klass); - if (__builtin_expect (! obj, false)) - JvThrow (no_memory); // Cast away const. jsize *lp = const_cast (&obj->length); *lp = count; @@ -439,13 +477,19 @@ jclass klass = _Jv_GetArrayClass (eltype, 0); - __JArray *arr = (__JArray*) _Jv_AllocObj (size + elsize * count, klass); - if (__builtin_expect (! arr, false)) - JvThrow (no_memory); +# ifdef JV_HASH_SYNCHRONIZATION + // Since the vatble 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); + // Note that we assume we are given zeroed memory by the allocator. +# endif // Cast away const. jsize *lp = const_cast (&arr->length); *lp = count; - // Note that we assume we are given zeroed memory by the allocator. return arr; } Index: libjava/resolve.cc =================================================================== RCS file: /cvs/gcc/gcc/libjava/resolve.cc,v retrieving revision 1.19 diff -u -r1.19 resolve.cc --- resolve.cc 2000/10/06 01:49:31 1.19 +++ resolve.cc 2001/02/02 18:45:25 @@ -584,7 +584,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); @@ -699,7 +699,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); @@ -1078,7 +1078,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, @@ -1128,8 +1128,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, @@ -1189,8 +1189,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: libjava/gcj/javaprims.h =================================================================== RCS file: /cvs/gcc/gcc/libjava/gcj/javaprims.h,v retrieving revision 1.15 diff -u -r1.15 javaprims.h --- javaprims.h 2000/12/11 07:50:52 1.15 +++ javaprims.h 2001/02/02 18:45:26 @@ -356,13 +356,23 @@ 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 "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); @@ -383,5 +393,11 @@ _Jv_ushort length; /* In bytes, of data portion, without final '\0'. */ char data[1]; /* In Utf8 format, with final '\0'. */ }; + +#ifdef JV_HASH_SYNCHRONIZATION + +#include "hash_sync.h" + +#endif /* JV_HASH_SYNCHRONIZATION */ #endif /* __JAVAPRIMS_H__ */ Index: libjava/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/02/02 18:45:26 @@ -21,4 +21,35 @@ JV_MARKARRAY_DECL; }; +// Enough stuff to inline _Jv_AllocObj. Ugly. +#include +#include +#include + +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: libjava/include/jvm.h =================================================================== RCS file: /cvs/gcc/gcc/libjava/include/jvm.h,v retrieving revision 1.31 diff -u -r1.31 jvm.h --- jvm.h 2000/10/09 01:54:50 1.31 +++ jvm.h 2001/02/02 18:45:26 @@ -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 ain 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: libjava/include/posix-threads.h =================================================================== RCS file: /cvs/gcc/gcc/libjava/include/posix-threads.h,v retrieving revision 1.16 diff -u -r1.16 posix-threads.h --- posix-threads.h 2000/12/30 12:18:39 1.16 +++ posix-threads.h 2001/02/02 18:45:26 @@ -179,6 +179,94 @@ 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. + + +#define SLOW_PTHREAD_SELF + // Add a cache for pthread_self() if we don't have the thread + // pointer in a register. + +#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; +} + +#elif 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. +# define NEED_SELF_CACHE // Need support code in posic-threads.cc +# 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 SP_HASH(sp) (((sp) >> 19) & (SELF_CACHE_SIZE-1)) + // Unsophisticated, but fast. + // Works well with anything that allocates at least + // 512KB stacks. +# 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 +} 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 = SP_HASH(sp); + volatile self_cache_entry *sce = self_cache + h; + if (sce -> high_sp_bits == sp >> LOG_THREAD_SPACING) { + return sce -> self; + } else { + return _Jv_ThreadSelf_out_of_line(sce, sp >> LOG_THREAD_SPACING); + } +} + + +#else /* 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 /* Generic _Jv_ThreadId_t */ + +#endif /* JV_HASH_SYNCHRONIZATION */ + inline _Jv_Thread_t * _Jv_ThreadCurrentData (void) { Index: libjava/java/lang/Class.h =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/lang/Class.h,v retrieving revision 1.31 diff -u -r1.31 Class.h --- Class.h 2001/01/17 10:22:32 1.31 +++ Class.h 2001/02/02 18:45:27 @@ -258,6 +258,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: libjava/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/02/02 18:45:27 @@ -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: libjava/java/lang/natObject.cc =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/lang/natObject.cc,v retrieving revision 1.13 diff -u -r1.13 natObject.cc --- natObject.cc 2000/10/06 01:49:31 1.13 +++ natObject.cc 2001/02/02 18:45:27 @@ -100,11 +100,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 +168,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 +234,7 @@ _Jv_MutexInit (&sync_mutex); } -jint +void _Jv_MonitorEnter (jobject obj) { #ifndef HANDLE_SEGV @@ -229,10 +244,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 +257,844 @@ _Jv_SyncInfo *si = (_Jv_SyncInfo *) obj->sync_info; if (__builtin_expect (_Jv_MutexUnlock (&si->mutex), false)) JvThrow (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 som 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 +#include +#include // for usleep, sysconf. +#include // for sched_yield. +#include + +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; +}; + +#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) + +// 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 (hl -> 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; + GC_register_finalizer(obj, heavy_lock_obj_finalization_proc, cd, 0, 0); + 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. + unlink_heavy(addr, light_locks + JV_SYNC_HASH(addr)); +# 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)); + + if (__builtin_expect(!hl, false)) + _Jv_ThrowNoMemory(); // The GC should do this? + 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((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; +} + +// 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) + { + mp = is_mp(); + if (mp) { + spin_limit = MP_SPINS; + yield_limit = MP_SPINS + YIELDS; + } + spin_initialized = true; + } + if (n < spin_limit) + { + 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++); +} + void -_Jv_FinalizeObject (jobject obj) +_Jv_MonitorEnter (jobject obj) { - // Ignore exceptions. From section 12.6 of the Java Language Spec. - try + 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 = 20; + + assert(!(addr & FLAGS)); +retry: + if (__builtin_expect(compare_and_swap(&(he -> address), + 0, addr),true)) { - obj->finalize (); + 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; } - catch (java::lang::Throwable *t) + address = he -> address; + if ((address & ~(HEAVY | REQUEST_CONVERSION)) == addr) { - // Ignore. + 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. + JvThrow (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); + release_set(&(he -> address), (addr | was_heavy)); + } + else + { + // Must use heavy lock. + ++ (he -> heavy_count); + release_set(&(he -> address), 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; + unsigned 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 + { + if ((address & ~(HEAVY | REQUEST_CONVERSION)) == addr) + { + // Someone holds the lightweight lock for this object, and + // it can't be us. + JvThrow (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. + assert(0 == count && 0 == he -> light_count); + 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_CondNotify(&(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) + JvThrow (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; + unsigned address; + heavy_lock *hl; + + if (__builtin_expect (timeout < 0 || nanos < 0 || nanos > 999999, false)) + JvThrow (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) + { + release_set(&(he -> address), address); + JvThrow (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) + JvThrow (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: + JvThrow (new IllegalMonitorStateException (JvNewStringLatin1 + ("current thread not owner"))); + case _JV_INTERRUPTED: + if (Thread::interrupted ()) + JvThrow (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; + unsigned 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) + { + JvThrow (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)) + JvThrow (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; + unsigned 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) + { + JvThrow (new IllegalMonitorStateException(JvNewStringLatin1 + ("current thread not owner"))); + } + result = _Jv_CondNotifyAll(&(hl->si.condition), &(hl->si.mutex)); + if (__builtin_expect (result, 0)) + JvThrow (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) +{ + JvThrow (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: libjava/java/lang/natString.cc =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/lang/natString.cc,v retrieving revision 1.16 diff -u -r1.16 natString.cc --- natString.cc 2000/12/02 00:28:44 1.16 +++ natString.cc 2001/02/02 18:45:27 @@ -297,8 +297,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: libjava/java/net/natInetAddress.cc =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/net/natInetAddress.cc,v retrieving revision 1.16 diff -u -r1.16 natInetAddress.cc --- natInetAddress.cc 2000/11/03 07:43:06 1.16 +++ natInetAddress.cc 2001/02/02 18:45:27 @@ -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 */