libgcj/1458: pthread thread cancellation problem
green@cygnus.com
green@cygnus.com
Wed Dec 20 12:28:00 GMT 2000
>Number: 1458
>Category: libgcj
>Synopsis: pthread thread cancellation problem
>Confidential: no
>Severity: critical
>Priority: medium
>Responsible: tromey
>State: closed
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Wed Dec 20 12:20:11 PST 2000
>Closed-Date: Fri Nov 05 12:46:01 PST 1999
>Last-Modified: Fri Nov 5 12:50:00 PST 1999
>Originator: Anthony Green
>Release: All
>Organization:
>Environment:
IA-32 Linux
>Description:
One problem with pc range table exception handling is that it can't
walk the stack backwards over code that wasn't built with the
exception handling flag.
Linux pthread libraries aren't typically compiled with exception
support - which is a real shame. I don't really understand why glibc
isn't built with this. In any case, libgcj implements Thread.stop by
cancelling the thread. A waiting thread that is cancelled ends up
calling back into libjava/posix-threads.cc's throw_cleanup routing,
which calls _Jv_Throw. The throw fails of course, because the stack
walker can't make it past the pthread library code. libgcc.a will
simply abort in this case.
The solutions as I see them are:
- compile libpthread with -fexceptions, or
- find some way to generate frame unwind info from compiled code, or
- find some other way around the library code.
I've appended a patch for discussion. It implements "some other way
around the library code". I'm not very happy with it though.
The basic idea is to longjmp around the pthread library code, back
into code we can throw from. The problem here is the overhead. The
linux threads implementation specifies different `cancellation
including many system calls.
This patch only makes waiting safe.
>How-To-Repeat:
>Fix:
1999-10-31 Anthony Green <green@cygnus.com>
* include/posix-threads.h (_Jv_Thread_t): Add jump buffer field.
* posix-threads.cc (throw_cleanup): Longjmp out of thread cleanup
code before throwing.
(_Jv_CondWait): Use setjmp to recover from cancelled thread.
Index: libjava/posix-threads.cc
===================================================================
RCS file: /cvs/java/libgcj/libjava/posix-threads.cc,v
retrieving revision 1.10
diff -u -r1.10 posix-threads.cc
--- posix-threads.cc 1999/09/10 22:03:05 1.10
+++ posix-threads.cc 1999/11/01 02:20:33
@@ -90,8 +90,14 @@
jlong m, m2, startTime;
bool done_sleeping = false;
+ _Jv_Thread_t *td = _Jv_ThreadCurrentData ();
+
if (millis == 0 && nanos == 0)
- r = pthread_cond_wait (cv, pmu);
+ {
+ if (setjmp (td->recover) == 1)
+ _Jv_Throw ((java::lang::Throwable *) td->exception);
+ pthread_cond_wait (cv, pmu);
+ }
else
{
startTime = java::lang::System::currentTimeMillis();
@@ -102,7 +108,9 @@
ts.tv_sec = m / 1000;
ts.tv_nsec = ((m % 1000) * 1000000) + nanos;
- r = pthread_cond_timedwait (cv, pmu, &ts);
+ if (setjmp (td->recover) == 1)
+ _Jv_Throw ((java::lang::Throwable *) td->exception);
+ pthread_cond_timedwait (cv, pmu, &ts);
if (r == EINTR)
{
@@ -217,7 +225,9 @@
}
else
{
- // Try to acquire the lock.
+ _Jv_Thread_t *td = _Jv_ThreadCurrentData ();
+ if (setjmp (td->recover) == 1)
+ _Jv_Throw ((java::lang::Throwable *) td->exception);
pthread_cond_wait (&mu->cond, &mu->mutex);
}
}
@@ -314,7 +324,7 @@
throw_cleanup (void *data)
{
_Jv_Thread_t *td = (_Jv_Thread_t *) data;
- _Jv_Throw ((java::lang::Throwable *) td->exception);
+ longjmp (td->recover, 0);
}
void
Index: libjava/include/posix-threads.h
===================================================================
RCS file: /cvs/java/libgcj/libjava/include/posix-threads.h,v
retrieving revision 1.8
diff -u -r1.8 posix-threads.h
--- posix-threads.h 1999/09/21 23:01:23 1.8
+++ posix-threads.h 1999/11/01 02:20:33
@@ -19,6 +19,7 @@
#include <pthread.h>
#include <sched.h>
+#include <setjmp.h>
#if defined (HAVE_PTHREAD_MUTEXATTR_SETTYPE) || defined (HAVE_PTHREAD_MUTEXATT\
R_SETKIND_NP)
# define HAVE_RECURSIVE_MUTEX 1
@@ -79,9 +80,17 @@
// Exception we want to throw when cancelled.
void *exception;
+
+ // Jump buffer for use during thread cancellation.
+ //
+ // PC range style exceptions can't be thrown over library code that
+ // doesn't have range table info. Typical pthread installations
+ // aren't compiled with this information. A jump buffer is used to
+ // skip back out of the pthread library and into an area we know we
+ // can throw exceptions from.
+ jmp_buf recover;
} _Jv_Thread_t;
typedef void _Jv_ThreadStartFunc (java::lang::Thread *);
-
// This convenience function is used to return the POSIX mutex
// corresponding to our mutex.
>Release-Note:
>Audit-Trail:
Formerly PR libgcj/78
State-Changed-From-To: open->analyzed
State-Changed-By: green
State-Changed-When: Tue Nov 2 10:56:55 1999
State-Changed-Why:
Now I'm thinking we should remove Thread.stop and friends.
JDK 1.1 stongly recommends not using them, and they're
deprecated in JDK 1.2. After speaking with Ulrich Drepper,
I think it's almost impossible to support them safely with
the existing glibc and gcc technology. Eventually the plan
is to add structured EH to gcc, and then use EH in glibc.
Until then, we should really avoid throwing, or longjmp'ing
out of callbacks. The current implementation is broken in
an even more subtle way. Cancelling the thread will invoke
various registered callbacks. One of the callbacks does
the throw (or longjmp). Another registered callback is
from the GC, where it deregisters interest in the thread.
However, the cancelled thread can catch the exception and
continue running! Maybe we should just remove this
functionality before it causes more problems for people.
From: green@cygnus.com
To: green@cygnus.com, java-gnats@sourceware.cygnus.com, tromey@cygnus.com
Cc:
Subject: Re: libgcj/78
Date: 2 Nov 1999 18:56:55 -0000
Synopsis: pthread thread cancellation problem
State-Changed-From-To: open->analyzed
State-Changed-By: green
State-Changed-When: Tue Nov 2 10:56:55 1999
State-Changed-Why:
Now I'm thinking we should remove Thread.stop and friends.
JDK 1.1 stongly recommends not using them, and they're
deprecated in JDK 1.2. After speaking with Ulrich Drepper,
I think it's almost impossible to support them safely with
the existing glibc and gcc technology. Eventually the plan
is to add structured EH to gcc, and then use EH in glibc.
Until then, we should really avoid throwing, or longjmp'ing
out of callbacks. The current implementation is broken in
an even more subtle way. Cancelling the thread will invoke
various registered callbacks. One of the callbacks does
the throw (or longjmp). Another registered callback is
from the GC, where it deregisters interest in the thread.
However, the cancelled thread can catch the exception and
continue running! Maybe we should just remove this
functionality before it causes more problems for people.
http://sourceware.cygnus.com/cgi-bin/gnatsweb.pl?cmd=view&database=java&pr=78
State-Changed-From-To: analyzed->closed
State-Changed-By: tromey
State-Changed-When: Fri Nov 5 12:46:01 1999
State-Changed-Why:
I fixed this by removing Thread.stop and Thread.destroy
From: tromey@cygnus.com
To: green@cygnus.com, java-gnats@sourceware.cygnus.com, tromey@cygnus.com
Cc:
Subject: Re: libgcj/78
Date: 5 Nov 1999 20:46:01 -0000
Synopsis: pthread thread cancellation problem
State-Changed-From-To: analyzed->closed
State-Changed-By: tromey
State-Changed-When: Fri Nov 5 12:46:01 1999
State-Changed-Why:
I fixed this by removing Thread.stop and Thread.destroy
http://sourceware.cygnus.com/cgi-bin/gnatsweb.pl?cmd=view&database=java&pr=78
>Unformatted:
More information about the Gcc-prs
mailing list