From 87796e8e8e59e72ddad923f9d3892f1288467f48 Mon Sep 17 00:00:00 2001 From: Andrew Haley Date: Fri, 22 Aug 2008 16:57:11 +0000 Subject: [PATCH] re PR libgcj/8995 (race cases in interpreter) 2008-08-22 Andrew Haley PR libgcj/8995: * interpret-run.cc (REWRITE_INSN): Null this macro. * include/jvm.h (class _Jv_Linker): Declare resolve_mutex, init. (read_cpool_entry, write_cpool_entry): New functions. * link.cc (_Jv_Linker::resolve_mutex): new. (_Jv_Linker::init): New function. (_Jv_Linker::resolve_pool_entry): Use {read,write}_cpool_entry to ensure atomic access to constant pool entries. From-SVN: r139494 --- libjava/ChangeLog | 13 +++++++ libjava/include/jvm.h | 24 +++++++++++++ libjava/interpret-run.cc | 24 +++++++++---- libjava/link.cc | 76 +++++++++++++++++++++++++++------------- 4 files changed, 106 insertions(+), 31 deletions(-) diff --git a/libjava/ChangeLog b/libjava/ChangeLog index 8141abdcdc1..2495390016d 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,16 @@ +2008-08-22 Andrew Haley + + PR libgcj/8995: + + * interpret-run.cc (REWRITE_INSN): Null this macro. + + * include/jvm.h (class _Jv_Linker): Declare resolve_mutex, init. + (read_cpool_entry, write_cpool_entry): New functions. + * link.cc (_Jv_Linker::resolve_mutex): new. + (_Jv_Linker::init): New function. + (_Jv_Linker::resolve_pool_entry): Use {read,write}_cpool_entry + to ensure atomic access to constant pool entries. + 2008-08-05 Matthias Klose PR libgcj/31890 diff --git a/libjava/include/jvm.h b/libjava/include/jvm.h index 84847548f01..76f8120cd22 100644 --- a/libjava/include/jvm.h +++ b/libjava/include/jvm.h @@ -307,6 +307,9 @@ private: s = signature; } + static _Jv_Mutex_t resolve_mutex; + static void init (void) __attribute__((constructor)); + public: static bool has_field_p (jclass, _Jv_Utf8Const *); @@ -324,6 +327,27 @@ public: _Jv_Utf8Const *, bool check_perms = true); static void layout_vtable_methods(jclass); + + static jbyte read_cpool_entry (_Jv_word *data, + const _Jv_Constants *const pool, + int index) + { + _Jv_MutexLock (&resolve_mutex); + jbyte tags = pool->tags[index]; + *data = pool->data[index]; + _Jv_MutexUnlock (&resolve_mutex); + return tags; + } + + static void write_cpool_entry (_Jv_word data, jbyte tags, + _Jv_Constants *pool, + int index) + { + _Jv_MutexLock (&resolve_mutex); + pool->data[index] = data; + pool->tags[index] = tags; + _Jv_MutexUnlock (&resolve_mutex); + } }; /* Type of pointer used as finalizer. */ diff --git a/libjava/interpret-run.cc b/libjava/interpret-run.cc index f858c971e0b..2934b9b8956 100644 --- a/libjava/interpret-run.cc +++ b/libjava/interpret-run.cc @@ -382,12 +382,24 @@ details. */ #else // !DEBUG #undef NEXT_INSN #define NEXT_INSN goto *((pc++)->insn) -#define REWRITE_INSN(INSN,SLOT,VALUE) \ - do { \ - pc[-2].insn = INSN; \ - pc[-1].SLOT = VALUE; \ - } \ - while (0) + +// REWRITE_INSN does nothing. +// +// Rewriting a multi-word instruction in the presence of multiple +// threads leads to a data race if a thread reads part of an +// instruction while some other thread is rewriting that instruction. +// For example, an invokespecial instruction may be rewritten to +// invokespecial_resolved and its operand changed from an index to a +// pointer while another thread is executing invokespecial. This +// other thread then reads the pointer that is now the operand of +// invokespecial_resolved and tries to use it as an index. +// +// Fixing this requires either spinlocks, a more elaborate data +// structure, or even per-thread allocated pages. It's clear from the +// locking in meth->compile below that the presence of multiple +// threads was contemplated when this code was written, but the full +// consequences were not fully appreciated. +#define REWRITE_INSN(INSN,SLOT,VALUE) #undef INTERP_REPORT_EXCEPTION #define INTERP_REPORT_EXCEPTION(Jthrowable) /* not needed when not debugging */ diff --git a/libjava/link.cc b/libjava/link.cc index db836a35104..9db09ef57d8 100644 --- a/libjava/link.cc +++ b/libjava/link.cc @@ -362,6 +362,19 @@ _Jv_Linker::resolve_method_entry (jclass klass, jclass &found_class, return the_method; } +_Jv_Mutex_t _Jv_Linker::resolve_mutex; + +void +_Jv_Linker::init (void) +{ + _Jv_MutexInit (&_Jv_Linker::resolve_mutex); +} + +// Locking in resolve_pool_entry is somewhat subtle. Constant +// resolution is idempotent, so it doesn't matter if two threads +// resolve the same entry. However, it is important that we always +// write the resolved flag and the data together, atomically. It is +// also important that we read them atomically. _Jv_word _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy) { @@ -369,6 +382,10 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy) if (GC_base (klass) && klass->constants.data && ! GC_base (klass->constants.data)) + // If a class is heap-allocated but the constant pool is not this + // is a "new ABI" class, i.e. one where the initial constant pool + // is in the read-only data section of an object file. Copy the + // initial constant pool from there to a new heap-allocated pool. { jsize count = klass->constants.size; if (count) @@ -384,14 +401,18 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy) _Jv_Constants *pool = &klass->constants; - if ((pool->tags[index] & JV_CONSTANT_ResolvedFlag) != 0) - return pool->data[index]; + jbyte tags; + _Jv_word data; + tags = read_cpool_entry (&data, pool, index); - switch (pool->tags[index] & ~JV_CONSTANT_LazyFlag) + if ((tags & JV_CONSTANT_ResolvedFlag) != 0) + return data; + + switch (tags & ~JV_CONSTANT_LazyFlag) { case JV_CONSTANT_Class: { - _Jv_Utf8Const *name = pool->data[index].utf8; + _Jv_Utf8Const *name = data.utf8; jclass found; if (name->first() == '[') @@ -410,8 +431,8 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy) { found = _Jv_NewClass(name, NULL, NULL); found->state = JV_STATE_PHANTOM; - pool->tags[index] |= JV_CONSTANT_ResolvedFlag; - pool->data[index].clazz = found; + tags |= JV_CONSTANT_ResolvedFlag; + data.clazz = found; break; } else @@ -429,8 +450,8 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy) || (_Jv_ClassNameSamePackage (check->name, klass->name))) { - pool->data[index].clazz = found; - pool->tags[index] |= JV_CONSTANT_ResolvedFlag; + data.clazz = found; + tags |= JV_CONSTANT_ResolvedFlag; } else { @@ -446,16 +467,16 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy) case JV_CONSTANT_String: { jstring str; - str = _Jv_NewStringUtf8Const (pool->data[index].utf8); - pool->data[index].o = str; - pool->tags[index] |= JV_CONSTANT_ResolvedFlag; + str = _Jv_NewStringUtf8Const (data.utf8); + data.o = str; + tags |= JV_CONSTANT_ResolvedFlag; } break; case JV_CONSTANT_Fieldref: { _Jv_ushort class_index, name_and_type_index; - _Jv_loadIndexes (&pool->data[index], + _Jv_loadIndexes (&data, class_index, name_and_type_index); jclass owner = (resolve_pool_entry (klass, class_index, true)).clazz; @@ -485,8 +506,8 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy) // Initialize the field's declaring class, not its qualifying // class. _Jv_InitClass (found_class); - pool->data[index].field = the_field; - pool->tags[index] |= JV_CONSTANT_ResolvedFlag; + data.field = the_field; + tags |= JV_CONSTANT_ResolvedFlag; } break; @@ -494,7 +515,7 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy) case JV_CONSTANT_InterfaceMethodref: { _Jv_ushort class_index, name_and_type_index; - _Jv_loadIndexes (&pool->data[index], + _Jv_loadIndexes (&data, class_index, name_and_type_index); @@ -503,18 +524,21 @@ _Jv_Linker::resolve_pool_entry (jclass klass, int index, bool lazy) the_method = resolve_method_entry (klass, found_class, class_index, name_and_type_index, true, - pool->tags[index] == JV_CONSTANT_InterfaceMethodref); + tags == JV_CONSTANT_InterfaceMethodref); - pool->data[index].rmethod + data.rmethod = klass->engine->resolve_method(the_method, found_class, ((the_method->accflags & Modifier::STATIC) != 0)); - pool->tags[index] |= JV_CONSTANT_ResolvedFlag; + tags |= JV_CONSTANT_ResolvedFlag; } break; } - return pool->data[index]; + + write_cpool_entry (data, tags, pool, index); + + return data; } // This function is used to lazily locate superclasses and @@ -1701,13 +1725,15 @@ _Jv_Linker::ensure_class_linked (jclass klass) // Resolve the remaining constant pool entries. for (int index = 1; index < pool->size; ++index) { - if (pool->tags[index] == JV_CONSTANT_String) - { - jstring str; + jbyte tags; + _Jv_word data; - str = _Jv_NewStringUtf8Const (pool->data[index].utf8); - pool->data[index].o = str; - pool->tags[index] |= JV_CONSTANT_ResolvedFlag; + tags = read_cpool_entry (&data, pool, index); + if (tags == JV_CONSTANT_String) + { + data.o = _Jv_NewStringUtf8Const (data.utf8); + tags |= JV_CONSTANT_ResolvedFlag; + write_cpool_entry (data, tags, pool, index); } }