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