mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			360 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			360 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			C++
		
	
	
	
// posix-threads.cc - interface between libjava and POSIX threads.
 | 
						||
 | 
						||
/* Copyright (C) 1998, 1999  Cygnus Solutions
 | 
						||
 | 
						||
   This file is part of libgcj.
 | 
						||
 | 
						||
This software is copyrighted work licensed under the terms of the
 | 
						||
Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
 | 
						||
details.  */
 | 
						||
 | 
						||
// TO DO:
 | 
						||
// * Document signal handling limitations
 | 
						||
 | 
						||
#include <config.h>
 | 
						||
 | 
						||
// If we're using the Boehm GC, then we need to override some of the
 | 
						||
// thread primitives.  This is fairly gross.
 | 
						||
#ifdef HAVE_BOEHM_GC
 | 
						||
extern "C"
 | 
						||
{
 | 
						||
#include <boehm-config.h>
 | 
						||
#include <gc.h>
 | 
						||
};
 | 
						||
#endif /* HAVE_BOEHM_GC */
 | 
						||
 | 
						||
#include <stdlib.h>
 | 
						||
#include <time.h>
 | 
						||
#include <signal.h>
 | 
						||
 | 
						||
#include <cni.h>
 | 
						||
#include <jvm.h>
 | 
						||
#include <java/lang/Thread.h>
 | 
						||
#include <java/lang/System.h>
 | 
						||
 | 
						||
// This is used to implement thread startup.
 | 
						||
struct starter
 | 
						||
{
 | 
						||
  _Jv_ThreadStartFunc *method;
 | 
						||
  java::lang::Thread *object;
 | 
						||
  _Jv_Thread_t *data;
 | 
						||
};
 | 
						||
 | 
						||
// This is the key used to map from the POSIX thread value back to the
 | 
						||
// Java object representing the thread.  The key is global to all
 | 
						||
// threads, so it is ok to make it a global here.
 | 
						||
pthread_key_t _Jv_ThreadKey;
 | 
						||
 | 
						||
// 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;
 | 
						||
static pthread_cond_t daemon_cond;
 | 
						||
static int non_daemon_count;
 | 
						||
 | 
						||
// The signal to use when interrupting a thread.
 | 
						||
#ifdef LINUX_THREADS
 | 
						||
  // LinuxThreads usurps both SIGUSR1 and SIGUSR2.
 | 
						||
#  define INTR SIGHUP
 | 
						||
#else /* LINUX_THREADS */
 | 
						||
#  define INTR SIGUSR2
 | 
						||
#endif /* LINUX_THREADS */
 | 
						||
 | 
						||
//
 | 
						||
// These are the flags that can appear in _Jv_Thread_t.
 | 
						||
//
 | 
						||
 | 
						||
// Thread started.
 | 
						||
#define FLAG_START   0x01
 | 
						||
// Thread is daemon.
 | 
						||
#define FLAG_DAEMON  0x02
 | 
						||
 | 
						||
 | 
						||
 | 
						||
int
 | 
						||
_Jv_CondWait (_Jv_ConditionVariable_t *cv, _Jv_Mutex_t *mu,
 | 
						||
	      jlong millis, jint nanos)
 | 
						||
{
 | 
						||
  int r;
 | 
						||
  pthread_mutex_t *pmu;
 | 
						||
#ifdef HAVE_RECURSIVE_MUTEX
 | 
						||
  pmu = mu;
 | 
						||
#else
 | 
						||
  pmu = &mu->mutex2;
 | 
						||
#endif
 | 
						||
  if (millis == 0 && nanos == 0)
 | 
						||
    r = pthread_cond_wait (cv, pmu);
 | 
						||
  else
 | 
						||
    {
 | 
						||
      struct timespec ts; 
 | 
						||
      jlong m = millis + java::lang::System::currentTimeMillis (); 
 | 
						||
      ts.tv_sec = m / 1000; 
 | 
						||
      ts.tv_nsec = ((m % 1000) * 1000000) + nanos; 
 | 
						||
             
 | 
						||
      r = pthread_cond_timedwait (cv, pmu, &ts);
 | 
						||
    }
 | 
						||
  return r;
 | 
						||
}
 | 
						||
 | 
						||
#ifndef RECURSIVE_MUTEX_IS_DEFAULT
 | 
						||
 | 
						||
void
 | 
						||
_Jv_MutexInit (_Jv_Mutex_t *mu)
 | 
						||
{
 | 
						||
#ifdef HAVE_RECURSIVE_MUTEX
 | 
						||
  pthread_mutexattr_t *val = NULL;
 | 
						||
 | 
						||
#if defined (HAVE_PTHREAD_MUTEXATTR_SETTYPE)
 | 
						||
  pthread_mutexattr_t attr;
 | 
						||
 | 
						||
  // If this is slow, then allocate it statically and only initialize
 | 
						||
  // it once.
 | 
						||
  pthread_mutexattr_init (&attr);
 | 
						||
  pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
 | 
						||
  val = &attr;
 | 
						||
#elif defined (HAVE_PTHREAD_MUTEXATTR_SETKIND_NP)
 | 
						||
  pthread_mutexattr_t attr;
 | 
						||
  pthread_mutexattr_init (&attr);
 | 
						||
  pthread_mutexattr_setkind_np (&attr, PTHREAD_MUTEX_RECURSIVE_NP);
 | 
						||
  val = &attr;
 | 
						||
#endif
 | 
						||
 | 
						||
  pthread_mutex_init (mu, val);
 | 
						||
 | 
						||
#if defined (HAVE_PTHREAD_MUTEXATTR_SETTYPE) || defined (HAVE_PTHREAD_MUTEXATTR_SETKIND_NP)
 | 
						||
  pthread_mutexattr_destroy (&attr);
 | 
						||
#endif
 | 
						||
 | 
						||
#else /* HAVE_RECURSIVE_MUTEX */
 | 
						||
 | 
						||
  // No recursive mutex, so simulate one.
 | 
						||
  pthread_mutex_init (&mu->mutex, NULL);
 | 
						||
  pthread_mutex_init (&mu->mutex2, NULL);
 | 
						||
  pthread_cond_init (&mu->cond, 0);
 | 
						||
  mu->count = 0;
 | 
						||
 | 
						||
#endif /* HAVE_RECURSIVE_MUTEX */
 | 
						||
}
 | 
						||
 | 
						||
#endif /* not RECURSIVE_MUTEX_IS_DEFAULT */
 | 
						||
 | 
						||
#if ! defined (LINUX_THREADS) && ! defined (HAVE_RECURSIVE_MUTEX)
 | 
						||
 | 
						||
void
 | 
						||
_Jv_MutexDestroy (_Jv_Mutex_t *mu)
 | 
						||
{
 | 
						||
  pthread_mutex_destroy (&mu->mutex);
 | 
						||
  pthread_mutex_destroy (&mu->mutex2);
 | 
						||
  pthread_cond_destroy (&mu->cond);
 | 
						||
}
 | 
						||
 | 
						||
int
 | 
						||
_Jv_MutexLock (_Jv_Mutex_t *mu)
 | 
						||
{
 | 
						||
  if (pthread_mutex_lock (&mu->mutex))
 | 
						||
    return -1;
 | 
						||
  while (1)
 | 
						||
    {
 | 
						||
      if (mu->count == 0)
 | 
						||
	{
 | 
						||
	  // Grab the lock.
 | 
						||
	  mu->thread = pthread_self ();
 | 
						||
	  mu->count = 1;
 | 
						||
	  pthread_mutex_lock (&mu->mutex2);
 | 
						||
	  break;
 | 
						||
	}
 | 
						||
      else if (pthread_self () == mu->thread)
 | 
						||
	{
 | 
						||
	  // Already have the lock.
 | 
						||
	  mu->count += 1;
 | 
						||
	  break;
 | 
						||
	}
 | 
						||
      else
 | 
						||
	{
 | 
						||
	  // Try to acquire the lock.
 | 
						||
	  pthread_cond_wait (&mu->cond, &mu->mutex);
 | 
						||
	}
 | 
						||
    }
 | 
						||
  pthread_mutex_unlock (&mu->mutex);
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
int
 | 
						||
_Jv_MutexUnlock (_Jv_Mutex_t *mu)
 | 
						||
{
 | 
						||
  if (pthread_mutex_lock (&mu->mutex))
 | 
						||
    return -1;
 | 
						||
  int r = 0;
 | 
						||
  if (mu->count == 0 || pthread_self () != mu->thread)
 | 
						||
    r = -1;
 | 
						||
  else
 | 
						||
    {
 | 
						||
      mu->count -= 1;
 | 
						||
      if (! mu->count)
 | 
						||
	{
 | 
						||
	  pthread_mutex_unlock (&mu->mutex2);
 | 
						||
	  pthread_cond_signal (&mu->cond);
 | 
						||
	}
 | 
						||
    }
 | 
						||
  pthread_mutex_unlock (&mu->mutex);
 | 
						||
  return r;
 | 
						||
}
 | 
						||
 | 
						||
#endif /* not LINUX_THREADS and not HAVE_RECURSIVE_MUTEX */
 | 
						||
 | 
						||
static void
 | 
						||
handle_intr (int)
 | 
						||
{
 | 
						||
  // Do nothing.
 | 
						||
}
 | 
						||
 | 
						||
void
 | 
						||
_Jv_InitThreads (void)
 | 
						||
{
 | 
						||
  pthread_key_create (&_Jv_ThreadKey, NULL);
 | 
						||
  pthread_mutex_init (&daemon_mutex, NULL);
 | 
						||
  pthread_cond_init (&daemon_cond, 0);
 | 
						||
  non_daemon_count = 0;
 | 
						||
 | 
						||
  // Arrange for the interrupt signal to interrupt system calls.
 | 
						||
  struct sigaction act;
 | 
						||
  act.sa_handler = handle_intr;
 | 
						||
  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.
 | 
						||
 | 
						||
  *data = info;
 | 
						||
}
 | 
						||
 | 
						||
void
 | 
						||
_Jv_ThreadSetPriority (_Jv_Thread_t *data, jint prio)
 | 
						||
{
 | 
						||
  if (data->flags & FLAG_START)
 | 
						||
    {
 | 
						||
      struct sched_param param;
 | 
						||
 | 
						||
      param.sched_priority = prio;
 | 
						||
      pthread_setschedparam (data->thread, SCHED_RR, ¶m);
 | 
						||
    }
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
// 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.
 | 
						||
static void *
 | 
						||
really_start (void *x)
 | 
						||
{
 | 
						||
  struct starter *info = (struct starter *) x;
 | 
						||
 | 
						||
  pthread_cleanup_push (throw_cleanup, info->data);
 | 
						||
  pthread_setspecific (_Jv_ThreadKey, info->object);
 | 
						||
  info->method (info->object);
 | 
						||
  pthread_cleanup_pop (0);
 | 
						||
 | 
						||
  if (! (info->data->flags & FLAG_DAEMON))
 | 
						||
    {
 | 
						||
      pthread_mutex_lock (&daemon_mutex);
 | 
						||
      --non_daemon_count;
 | 
						||
      if (! non_daemon_count)
 | 
						||
	pthread_cond_signal (&daemon_cond);
 | 
						||
      pthread_mutex_unlock (&daemon_mutex);
 | 
						||
    }
 | 
						||
 | 
						||
  return NULL;
 | 
						||
}
 | 
						||
 | 
						||
void
 | 
						||
_Jv_ThreadStart (java::lang::Thread *thread, _Jv_Thread_t *data,
 | 
						||
		 _Jv_ThreadStartFunc *meth)
 | 
						||
{
 | 
						||
  struct sched_param param;
 | 
						||
  pthread_attr_t attr;
 | 
						||
  struct starter *info;
 | 
						||
 | 
						||
  if (data->flags & FLAG_START)
 | 
						||
    return;
 | 
						||
  data->flags |= FLAG_START;
 | 
						||
 | 
						||
  param.sched_priority = thread->getPriority();
 | 
						||
 | 
						||
  pthread_attr_init (&attr);
 | 
						||
  pthread_attr_setschedparam (&attr, ¶m);
 | 
						||
 | 
						||
  // FIXME: handle marking the info object for GC.
 | 
						||
  info = (struct starter *) _Jv_AllocBytes (sizeof (struct starter));
 | 
						||
  info->method = meth;
 | 
						||
  info->object = thread;
 | 
						||
  info->data = data;
 | 
						||
 | 
						||
  if (! thread->isDaemon())
 | 
						||
    {
 | 
						||
      pthread_mutex_lock (&daemon_mutex);
 | 
						||
      ++non_daemon_count;
 | 
						||
      pthread_mutex_unlock (&daemon_mutex);
 | 
						||
    }
 | 
						||
  else
 | 
						||
    data->flags |= FLAG_DAEMON;
 | 
						||
  pthread_create (&data->thread, &attr, really_start, (void *) info);
 | 
						||
 | 
						||
  pthread_attr_destroy (&attr);
 | 
						||
}
 | 
						||
 | 
						||
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);
 | 
						||
  pthread_mutex_unlock (&daemon_mutex);
 | 
						||
}
 | 
						||
 | 
						||
void
 | 
						||
_Jv_ThreadInterrupt (_Jv_Thread_t *data)
 | 
						||
{
 | 
						||
  pthread_kill (data->thread, INTR);
 | 
						||
}
 |