mirror of git://gcc.gnu.org/git/gcc.git
re PR libgcj/8995 (race cases in interpreter)
2008-09-17 Andrew Haley <aph@redhat.com> PR libgcj/8995: * defineclass.cc (_Jv_ClassReader::handleCodeAttribute): Initialize thread_count. * include/java-interp.h (_Jv_InterpMethod::thread_count): New field. (_Jv_InterpMethod::rewrite_insn_mutex): New mutex. (_Jv_InterpFrame:: _Jv_InterpFrame): Pass frame_type. * interpret.cc (ThreadCountAdjuster): New class. (_Jv_InterpMethod::thread_count): New field. (_Jv_InitInterpreter): Initialize rewrite_insn_mutex. Increment and decrement thread_count field in methods. * interpret-run.cc (REWRITE_INSN): Check thread_count <= 1. (REWRITE_INSN): Likewise. Declare a ThreadCountAdjuster. * java/lang/reflect/natVMProxy.cc (run_proxy): Initialize frame type as frame_proxy. From-SVN: r140593
This commit is contained in:
parent
5c0466b537
commit
f0580031a7
|
@ -1,3 +1,24 @@
|
||||||
|
2008-09-17 Andrew Haley <aph@redhat.com>
|
||||||
|
|
||||||
|
PR libgcj/8995:
|
||||||
|
|
||||||
|
* defineclass.cc (_Jv_ClassReader::handleCodeAttribute):
|
||||||
|
Initialize thread_count.
|
||||||
|
* include/java-interp.h (_Jv_InterpMethod::thread_count): New
|
||||||
|
field.
|
||||||
|
(_Jv_InterpMethod::rewrite_insn_mutex): New mutex.
|
||||||
|
(_Jv_InterpFrame:: _Jv_InterpFrame): Pass frame_type.
|
||||||
|
* interpret.cc
|
||||||
|
(ThreadCountAdjuster): New class.
|
||||||
|
(_Jv_InterpMethod::thread_count): New field.
|
||||||
|
(_Jv_InitInterpreter): Initialize rewrite_insn_mutex.
|
||||||
|
Increment and decrement thread_count field in methods.
|
||||||
|
* interpret-run.cc (REWRITE_INSN): Check thread_count <= 1.
|
||||||
|
(REWRITE_INSN): Likewise.
|
||||||
|
Declare a ThreadCountAdjuster.
|
||||||
|
* java/lang/reflect/natVMProxy.cc (run_proxy): Initialize frame
|
||||||
|
type as frame_proxy.
|
||||||
|
|
||||||
2008-09-05 David Daney <ddaney@avtrex.com>
|
2008-09-05 David Daney <ddaney@avtrex.com>
|
||||||
|
|
||||||
* configure.ac (reduced-reflection): New AC_ARG_ENABLE.
|
* configure.ac (reduced-reflection): New AC_ARG_ENABLE.
|
||||||
|
|
|
@ -1682,7 +1682,9 @@ void _Jv_ClassReader::handleCodeAttribute
|
||||||
method->prepared = NULL;
|
method->prepared = NULL;
|
||||||
method->line_table_len = 0;
|
method->line_table_len = 0;
|
||||||
method->line_table = NULL;
|
method->line_table = NULL;
|
||||||
|
#ifdef DIRECT_THREADED
|
||||||
|
method->thread_count = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
// grab the byte code!
|
// grab the byte code!
|
||||||
memcpy ((void*) method->bytecode (),
|
memcpy ((void*) method->bytecode (),
|
||||||
|
|
|
@ -175,6 +175,17 @@ class _Jv_InterpMethod : public _Jv_MethodBase
|
||||||
static pc_t breakpoint_insn;
|
static pc_t breakpoint_insn;
|
||||||
#ifdef DIRECT_THREADED
|
#ifdef DIRECT_THREADED
|
||||||
static insn_slot bp_insn_slot;
|
static insn_slot bp_insn_slot;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Mutex to prevent a data race between threads when rewriting
|
||||||
|
// instructions. See interpret-run.cc for an explanation of its use.
|
||||||
|
static _Jv_Mutex_t rewrite_insn_mutex;
|
||||||
|
|
||||||
|
// The count of threads executing this method.
|
||||||
|
long thread_count;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
#else
|
#else
|
||||||
static unsigned char bp_insn_opcode;
|
static unsigned char bp_insn_opcode;
|
||||||
#endif
|
#endif
|
||||||
|
@ -455,9 +466,10 @@ public:
|
||||||
jobject obj_ptr;
|
jobject obj_ptr;
|
||||||
|
|
||||||
_Jv_InterpFrame (void *meth, java::lang::Thread *thr, jclass proxyCls = NULL,
|
_Jv_InterpFrame (void *meth, java::lang::Thread *thr, jclass proxyCls = NULL,
|
||||||
pc_t *pc = NULL)
|
pc_t *pc = NULL,
|
||||||
|
_Jv_FrameType frame_type = frame_interpreter)
|
||||||
: _Jv_Frame (reinterpret_cast<_Jv_MethodBase *> (meth), thr,
|
: _Jv_Frame (reinterpret_cast<_Jv_MethodBase *> (meth), thr,
|
||||||
frame_interpreter)
|
frame_type)
|
||||||
{
|
{
|
||||||
next_interp = (_Jv_InterpFrame *) thr->interp_frame;
|
next_interp = (_Jv_InterpFrame *) thr->interp_frame;
|
||||||
proxyClass = proxyCls;
|
proxyClass = proxyCls;
|
||||||
|
@ -501,6 +513,76 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef DIRECT_THREADED
|
||||||
|
// This class increments and decrements the thread_count field in an
|
||||||
|
// interpreted method. On entry to the interpreter a
|
||||||
|
// ThreadCountAdjuster is created when increments the thread_count in
|
||||||
|
// the current method and uses the next_interp field in the frame to
|
||||||
|
// find the previous method and decrement its thread_count.
|
||||||
|
class ThreadCountAdjuster
|
||||||
|
{
|
||||||
|
|
||||||
|
// A class used to handle the rewrite_insn_mutex while we're
|
||||||
|
// adjusting the thread_count in a method. Unlocking the mutex in a
|
||||||
|
// destructor ensures that it's unlocked even if (for example) a
|
||||||
|
// segfault occurs in the critical section.
|
||||||
|
class MutexLock
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
_Jv_Mutex_t *mutex;
|
||||||
|
public:
|
||||||
|
MutexLock (_Jv_Mutex_t *m)
|
||||||
|
{
|
||||||
|
mutex = m;
|
||||||
|
_Jv_MutexLock (mutex);
|
||||||
|
}
|
||||||
|
~MutexLock ()
|
||||||
|
{
|
||||||
|
_Jv_MutexUnlock (mutex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_Jv_InterpMethod *method;
|
||||||
|
_Jv_InterpMethod *next_method;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
ThreadCountAdjuster (_Jv_InterpMethod *m, _Jv_InterpFrame *fr)
|
||||||
|
{
|
||||||
|
MutexLock lock (&::_Jv_InterpMethod::rewrite_insn_mutex);
|
||||||
|
|
||||||
|
method = m;
|
||||||
|
next_method = NULL;
|
||||||
|
|
||||||
|
_Jv_InterpFrame *next_interp = fr->next_interp;
|
||||||
|
|
||||||
|
// Record the fact that we're executing this method and that
|
||||||
|
// we're no longer executing the method that called us.
|
||||||
|
method->thread_count++;
|
||||||
|
|
||||||
|
if (next_interp && next_interp->frame_type == frame_interpreter)
|
||||||
|
{
|
||||||
|
next_method
|
||||||
|
= reinterpret_cast<_Jv_InterpMethod *> (next_interp->meth);
|
||||||
|
next_method->thread_count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~ThreadCountAdjuster ()
|
||||||
|
{
|
||||||
|
MutexLock lock (&::_Jv_InterpMethod::rewrite_insn_mutex);
|
||||||
|
|
||||||
|
// We're going to return to the method that called us, so bump its
|
||||||
|
// thread_count and decrement our own.
|
||||||
|
|
||||||
|
method->thread_count--;
|
||||||
|
|
||||||
|
if (next_method)
|
||||||
|
next_method->thread_count++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // DIRECT_THREADED
|
||||||
|
|
||||||
#endif /* INTERPRETER */
|
#endif /* INTERPRETER */
|
||||||
|
|
||||||
#endif /* __JAVA_INTERP_H__ */
|
#endif /* __JAVA_INTERP_H__ */
|
||||||
|
|
|
@ -29,6 +29,10 @@ details. */
|
||||||
_Jv_InterpFrame frame_desc (meth, thread);
|
_Jv_InterpFrame frame_desc (meth, thread);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef DIRECT_THREADED
|
||||||
|
ThreadCountAdjuster adj (meth, &frame_desc);
|
||||||
|
#endif // DIRECT_THREADED
|
||||||
|
|
||||||
_Jv_word stack[meth->max_stack];
|
_Jv_word stack[meth->max_stack];
|
||||||
_Jv_word *sp = stack;
|
_Jv_word *sp = stack;
|
||||||
|
|
||||||
|
@ -361,20 +365,29 @@ details. */
|
||||||
} \
|
} \
|
||||||
while (0)
|
while (0)
|
||||||
|
|
||||||
|
// We fail to rewrite a breakpoint if there is another thread
|
||||||
|
// currently executing this method. This is a bug, but there's
|
||||||
|
// nothing else we can do that doesn't cause a data race.
|
||||||
#undef REWRITE_INSN
|
#undef REWRITE_INSN
|
||||||
#define REWRITE_INSN(INSN,SLOT,VALUE) \
|
#define REWRITE_INSN(INSN,SLOT,VALUE) \
|
||||||
do { \
|
do \
|
||||||
if (pc[-2].insn == breakpoint_insn->insn) \
|
{ \
|
||||||
{ \
|
_Jv_MutexLock (&rewrite_insn_mutex); \
|
||||||
using namespace ::gnu::gcj::jvmti; \
|
if (meth->thread_count <= 1) \
|
||||||
jlocation location = meth->insn_index (pc - 2); \
|
{ \
|
||||||
_Jv_RewriteBreakpointInsn (meth->self, location, (pc_t) INSN); \
|
if (pc[-2].insn == breakpoint_insn->insn) \
|
||||||
} \
|
{ \
|
||||||
else \
|
using namespace ::gnu::gcj::jvmti; \
|
||||||
pc[-2].insn = INSN; \
|
jlocation location = meth->insn_index (pc - 2); \
|
||||||
|
_Jv_RewriteBreakpointInsn (meth->self, location, (pc_t) INSN); \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
pc[-2].insn = INSN; \
|
||||||
\
|
\
|
||||||
pc[-1].SLOT = VALUE; \
|
pc[-1].SLOT = VALUE; \
|
||||||
} \
|
} \
|
||||||
|
_Jv_MutexUnlock (&rewrite_insn_mutex); \
|
||||||
|
} \
|
||||||
while (0)
|
while (0)
|
||||||
|
|
||||||
#undef INTERP_REPORT_EXCEPTION
|
#undef INTERP_REPORT_EXCEPTION
|
||||||
|
@ -383,23 +396,23 @@ details. */
|
||||||
#undef NEXT_INSN
|
#undef NEXT_INSN
|
||||||
#define NEXT_INSN goto *((pc++)->insn)
|
#define NEXT_INSN goto *((pc++)->insn)
|
||||||
|
|
||||||
// REWRITE_INSN does nothing.
|
|
||||||
//
|
|
||||||
// Rewriting a multi-word instruction in the presence of multiple
|
// Rewriting a multi-word instruction in the presence of multiple
|
||||||
// threads leads to a data race if a thread reads part of an
|
// threads is a data race if a thread reads part of an instruction
|
||||||
// instruction while some other thread is rewriting that instruction.
|
// while some other thread is rewriting that instruction. We detect
|
||||||
// For example, an invokespecial instruction may be rewritten to
|
// more than one thread executing a method and don't rewrite the
|
||||||
// invokespecial_resolved and its operand changed from an index to a
|
// instruction. A thread entering a method blocks on
|
||||||
// pointer while another thread is executing invokespecial. This
|
// rewrite_insn_mutex until the write is complete.
|
||||||
// other thread then reads the pointer that is now the operand of
|
#define REWRITE_INSN(INSN,SLOT,VALUE) \
|
||||||
// invokespecial_resolved and tries to use it as an index.
|
do { \
|
||||||
//
|
_Jv_MutexLock (&rewrite_insn_mutex); \
|
||||||
// Fixing this requires either spinlocks, a more elaborate data
|
if (meth->thread_count <= 1) \
|
||||||
// structure, or even per-thread allocated pages. It's clear from the
|
{ \
|
||||||
// locking in meth->compile below that the presence of multiple
|
pc[-2].insn = INSN; \
|
||||||
// threads was contemplated when this code was written, but the full
|
pc[-1].SLOT = VALUE; \
|
||||||
// consequences were not fully appreciated.
|
} \
|
||||||
#define REWRITE_INSN(INSN,SLOT,VALUE)
|
_Jv_MutexUnlock (&rewrite_insn_mutex); \
|
||||||
|
} \
|
||||||
|
while (0)
|
||||||
|
|
||||||
#undef INTERP_REPORT_EXCEPTION
|
#undef INTERP_REPORT_EXCEPTION
|
||||||
#define INTERP_REPORT_EXCEPTION(Jthrowable) /* not needed when not debugging */
|
#define INTERP_REPORT_EXCEPTION(Jthrowable) /* not needed when not debugging */
|
||||||
|
|
|
@ -78,10 +78,15 @@ static void find_catch_location (jthrowable, jthread, jmethodID *, jlong *);
|
||||||
// the Class monitor as user code in another thread could hold it.
|
// the Class monitor as user code in another thread could hold it.
|
||||||
static _Jv_Mutex_t compile_mutex;
|
static _Jv_Mutex_t compile_mutex;
|
||||||
|
|
||||||
|
// See class ThreadCountAdjuster and REWRITE_INSN for how this is
|
||||||
|
// used.
|
||||||
|
_Jv_Mutex_t _Jv_InterpMethod::rewrite_insn_mutex;
|
||||||
|
|
||||||
void
|
void
|
||||||
_Jv_InitInterpreter()
|
_Jv_InitInterpreter()
|
||||||
{
|
{
|
||||||
_Jv_MutexInit (&compile_mutex);
|
_Jv_MutexInit (&compile_mutex);
|
||||||
|
_Jv_MutexInit (&_Jv_InterpMethod::rewrite_insn_mutex);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
void _Jv_InitInterpreter() {}
|
void _Jv_InitInterpreter() {}
|
||||||
|
|
|
@ -350,7 +350,8 @@ run_proxy (ffi_cif *cif,
|
||||||
// than about Proxy.class itself. FRAME_DESC has a destructor so it
|
// than about Proxy.class itself. FRAME_DESC has a destructor so it
|
||||||
// cleans up automatically when this proxy invocation returns.
|
// cleans up automatically when this proxy invocation returns.
|
||||||
Thread *thread = Thread::currentThread();
|
Thread *thread = Thread::currentThread();
|
||||||
_Jv_InterpFrame frame_desc (self->self, thread, proxyClass);
|
_Jv_InterpFrame frame_desc (self->self, thread, proxyClass,
|
||||||
|
NULL, frame_proxy);
|
||||||
|
|
||||||
// The method to invoke is saved in $Proxy0.m[method_index].
|
// The method to invoke is saved in $Proxy0.m[method_index].
|
||||||
// FIXME: We could somewhat improve efficiency by storing a pointer
|
// FIXME: We could somewhat improve efficiency by storing a pointer
|
||||||
|
|
Loading…
Reference in New Issue