OSDN Git Service

* All files: Updated copyright to reflect Cygnus purchase.
[pf3gnuchains/gcc-fork.git] / libjava / posix-threads.cc
index 436588a..29fce59 100644 (file)
@@ -1,6 +1,6 @@
 // posix-threads.cc - interface between libjava and POSIX threads.
 
-/* Copyright (C) 1998, 1999  Cygnus Solutions
+/* Copyright (C) 1998, 1999  Red Hat, Inc.
 
    This file is part of libgcj.
 
@@ -18,7 +18,7 @@ details.  */
 #ifdef HAVE_BOEHM_GC
 extern "C"
 {
-#include <boehm-config.h>
+#include <gcconfig.h>
 #include <gc.h>
 };
 #endif /* HAVE_BOEHM_GC */
@@ -26,11 +26,15 @@ extern "C"
 #include <stdlib.h>
 #include <time.h>
 #include <signal.h>
+#include <errno.h>
+#include <limits.h>
 
-#include <cni.h>
+#include <gcj/cni.h>
 #include <jvm.h>
 #include <java/lang/Thread.h>
 #include <java/lang/System.h>
+#include <java/lang/Long.h>
+#include <java/lang/OutOfMemoryError.h>
 
 // This is used to implement thread startup.
 struct starter
@@ -45,6 +49,10 @@ struct starter
 // threads, so it is ok to make it a global here.
 pthread_key_t _Jv_ThreadKey;
 
+// This is the key used to map from the POSIX thread value back to the
+// _Jv_Thread_t* representing the thread.
+pthread_key_t _Jv_ThreadDataKey;
+
 // We keep a count of all non-daemon threads which are running.  When
 // this reaches zero, _Jv_ThreadWait returns.
 static pthread_mutex_t daemon_mutex;
@@ -53,7 +61,7 @@ static int non_daemon_count;
 
 // The signal to use when interrupting a thread.
 #ifdef LINUX_THREADS
-  // LinuxThreads usurps both SIGUSR1 and SIGUSR2.
+  // LinuxThreads (prior to glibc 2.1) usurps both SIGUSR1 and SIGUSR2.
 #  define INTR SIGHUP
 #else /* LINUX_THREADS */
 #  define INTR SIGUSR2
@@ -74,26 +82,89 @@ int
 _Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu,
              jlong millis, jint nanos)
 {
+  if (_Jv_PthreadCheckMonitor (mu))
+    return 1;
+
   int r;
-  pthread_mutex_t *pmu;
-#ifdef HAVE_RECURSIVE_MUTEX
-  pmu = mu;
-#else
-  pmu = &mu->mutex2;
-#endif
+  pthread_mutex_t *pmu = _Jv_PthreadGetMutex (mu);
+  struct timespec ts;
+  jlong m, m2, startTime;
+  bool done_sleeping = false;
+
   if (millis == 0 && nanos == 0)
-    r = pthread_cond_wait (cv, pmu);
+    {
+#ifdef LINUX_THREADS
+      // pthread_cond_timedwait can be interrupted by a signal on linux, while
+      // pthread_cond_wait can not. So pthread_cond_timedwait() forever.
+      m = java::lang::Long::MAX_VALUE;
+      ts.tv_sec = LONG_MAX;
+      ts.tv_nsec = 0;
+#endif
+    }
   else
     {
-      struct timespec ts;
-      unsigned long m = millis + java::lang::System::currentTimeMillis ();
+      startTime = java::lang::System::currentTimeMillis();
+      m = millis + startTime;
+      ts.tv_sec = m / 1000; 
+      ts.tv_nsec = ((m % 1000) * 1000000) + nanos; 
+    }
 
-      ts.tv_sec = m / 1000;
-      ts.tv_nsec = (m % 1000) * 1000 * 1000 + nanos;
+  java::lang::Thread *current = _Jv_ThreadCurrent();
 
-      r = pthread_cond_timedwait (cv, pmu, &ts);
+  do
+    {
+      r = EINTR;
+      // Check to ensure the thread hasn't already been interrupted.
+      if (!(current->isInterrupted ()))
+        {
+#ifdef LINUX_THREADS   
+         // FIXME: in theory, interrupt() could be called on this thread
+         // between the test above and the wait below, resulting in the 
+         // interupt() call failing. I don't see a way to fix this 
+         // without significant changes to the implementation.
+         r = pthread_cond_timedwait (cv, pmu, &ts);
+#else
+         if (millis == 0 && nanos == 0)
+           r = pthread_cond_wait (cv, pmu);
+         else    
+           r = pthread_cond_timedwait (cv, pmu, &ts);    
+#endif
+       }
+      
+      if (r == EINTR)
+       {
+         /* We were interrupted by a signal.  Either this is
+            because we were interrupted intentionally (i.e. by
+            Thread.interrupt()) or by the GC if it is
+            signal-based.  */
+         if (current->isInterrupted ())
+           {
+             r = 0;
+              done_sleeping = true;
+            }
+         else
+            {
+             /* We were woken up by the GC or another signal.  */
+             m2 = java::lang::System::currentTimeMillis ();
+             if (m2 >= m)
+               {
+                 r = 0;
+                 done_sleeping = true;
+               }
+           }
+       }
+      else if (r == ETIMEDOUT)
+       {
+         /* A timeout is a normal result.  */
+         r = 0;
+         done_sleeping = true;
+       }
+      else
+       done_sleeping = true;
     }
-  return r;
+  while (! done_sleeping);
+
+  return r != 0;
 }
 
 #ifndef RECURSIVE_MUTEX_IS_DEFAULT
@@ -119,7 +190,10 @@ _Jv_MutexInit (_Jv_Mutex_t *mu)
   val = &attr;
 #endif
 
-  pthread_mutex_init (mu, val);
+  pthread_mutex_init (_Jv_PthreadGetMutex (mu), val);
+#ifdef PTHREAD_MUTEX_IS_STRUCT
+  mu->count = 0;
+#endif
 
 #if defined (HAVE_PTHREAD_MUTEXATTR_SETTYPE) || defined (HAVE_PTHREAD_MUTEXATTR_SETKIND_NP)
   pthread_mutexattr_destroy (&attr);
@@ -212,6 +286,7 @@ void
 _Jv_InitThreads (void)
 {
   pthread_key_create (&_Jv_ThreadKey, NULL);
+  pthread_key_create (&_Jv_ThreadDataKey, NULL);
   pthread_mutex_init (&daemon_mutex, NULL);
   pthread_cond_init (&daemon_cond, 0);
   non_daemon_count = 0;
@@ -222,22 +297,13 @@ _Jv_InitThreads (void)
   sigemptyset (&act.sa_mask);
   act.sa_flags = 0;
   sigaction (INTR, &act, NULL);
-
-  // Arrange for SIGINT to be blocked to all threads.  It is only
-  // deliverable to the master thread.
-  sigset_t mask;
-  sigemptyset (&mask);
-  sigaddset (&mask, SIGINT);
-  pthread_sigmask (SIG_BLOCK, &mask, NULL);
 }
 
 void
 _Jv_ThreadInitData (_Jv_Thread_t **data, java::lang::Thread *)
 {
   _Jv_Thread_t *info = new _Jv_Thread_t;
-
   info->flags = 0;
-  info->exception = NULL;
 
   // FIXME register a finalizer for INFO here.
   // FIXME also must mark INFO somehow.
@@ -257,26 +323,6 @@ _Jv_ThreadSetPriority (_Jv_Thread_t *data, jint prio)
     }
 }
 
-
-// This is called as a cleanup handler when a thread is exiting.  We
-// use it to throw the requested exception.  It's entirely possible
-// that this approach is doomed to failure, in which case we'll need
-// to adopt some alternate.  For instance, use a signal to implement
-// _Jv_ThreadCancel.
-static void
-throw_cleanup (void *data)
-{
-  _Jv_Thread_t *td = (_Jv_Thread_t *) data;
-  _Jv_Throw ((java::lang::Throwable *) td->exception);
-}
-
-void
-_Jv_ThreadCancel (_Jv_Thread_t *data, void *error)
-{
-  data->exception = error;
-  pthread_cancel (data->thread);
-}
-
 // This function is called when a thread is started.  We don't arrange
 // to call the `run' method directly, because this function must
 // return a value.
@@ -285,10 +331,9 @@ really_start (void *x)
 {
   struct starter *info = (struct starter *) x;
 
-  pthread_cleanup_push (throw_cleanup, info->data);
   pthread_setspecific (_Jv_ThreadKey, info->object);
+  pthread_setspecific (_Jv_ThreadDataKey, info->data);
   info->method (info->object);
-  pthread_cleanup_pop (0);
 
   if (! (info->data->flags & FLAG_DAEMON))
     {
@@ -333,20 +378,20 @@ _Jv_ThreadStart (java::lang::Thread *thread, _Jv_Thread_t *data,
     }
   else
     data->flags |= FLAG_DAEMON;
-  pthread_create (&data->thread, &attr, really_start, (void *) info);
-
+  int r = pthread_create (&data->thread, &attr, really_start, (void *) info);
+  
   pthread_attr_destroy (&attr);
+
+  if (r)
+    {
+      const char* msg = "Cannot create additional threads";
+      JvThrow (new java::lang::OutOfMemoryError (JvNewStringUTF (msg)));
+    }
 }
 
 void
 _Jv_ThreadWait (void)
 {
-  // Arrange for SIGINT to be delivered to the master thread.
-  sigset_t mask;
-  sigemptyset (&mask);
-  sigaddset (&mask, SIGINT);
-  pthread_sigmask (SIG_UNBLOCK, &mask, NULL);
-
   pthread_mutex_lock (&daemon_mutex);
   if (non_daemon_count)
     pthread_cond_wait (&daemon_cond, &daemon_mutex);