// natThread.cc - Native part of Thread class.

/* 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.  */

#include <config.h>

#include <stdlib.h>

#include <cni.h>
#include <jvm.h>
#include <java/lang/Thread.h>
#include <java/lang/ThreadGroup.h>
#include <java/lang/IllegalArgumentException.h>
#include <java/lang/IllegalThreadStateException.h>
#include <java/lang/InterruptedException.h>
#include <java/lang/NullPointerException.h>



// This structure is used to represent all the data the native side
// needs.  An object of this type is assigned to the `data' member of
// the Thread class.
struct natThread
{
  // These are used to interrupt sleep and join calls.  We can share a
  // condition variable here since this thread can either be sleeping
  // or waiting for a thread exit, but not both.
  _Jv_Mutex_t interrupt_mutex;
  _Jv_ConditionVariable_t interrupt_cond;

  // This is private data for the thread system layer.
  _Jv_Thread_t *thread;

  // All threads waiting to join this thread are linked together and
  // waiting on their respective `interrupt' condition variables.
  // When this thread exits, it notifies each such thread by
  // signalling the condition.  In this case the `interrupt_flag' is
  // not set; this is how the waiting thread knows whether the join
  // has failed or whether it should throw an exception.
  struct natThread *joiner;

  // Chain for waiters.
  struct natThread *next;
};

// We use this for its side effects: it lets us lock a mutex directly
// and not lose if an exception is thrown.
class locker
{
private:
  _Jv_Mutex_t *mutex;

public:
  locker (_Jv_Mutex_t *m)
    : mutex (m)
  {
    _Jv_MutexLock (mutex);
  }

  ~locker ()
  {
    _Jv_MutexUnlock (mutex);
  }
};

// This is called from the constructor to initialize the native side
// of the Thread.
void
java::lang::Thread::initialize_native (void)
{
  // FIXME: this must interact with the GC in some logical way.  At
  // the very least we must register a finalizer to clean up.  This
  // isn't easy to do.  If the Thread object resurrects itself in its
  // own finalizer then we will need to reinitialize this structure at
  // any "interesting" point.
  natThread *nt = (natThread *) _Jv_AllocBytes (sizeof (natThread));
  data = (jobject) nt;
  _Jv_MutexInit (&nt->interrupt_mutex);
  _Jv_CondInit (&nt->interrupt_cond);
  _Jv_ThreadInitData (&nt->thread, this);
  nt->joiner = 0;
  nt->next = 0;
}

jint
java::lang::Thread::countStackFrames (void)
{
  // NOTE: This is deprecated in JDK 1.2.
  JvFail ("java::lang::Thread::countStackFrames unimplemented");
  return 0;
}

java::lang::Thread *
java::lang::Thread::currentThread (void)
{
  return _Jv_ThreadCurrent ();
}

// FIXME: this is apparently the only way a thread can be removed from
// a ThreadGroup.  That seems wrong.
void
java::lang::Thread::destroy (void)
{
  // NOTE: This is marked as unimplemented in the JDK 1.2
  // documentation.
  JvFail ("java::lang::Thread::destroy unimplemented");
}

void
java::lang::Thread::dumpStack (void)
{
  // We don't implement this because it is very hard.  Once we have a
  // VM, this could potentially ask the VM to do the dump in cases
  // where it makes sense.
  JvFail ("java::lang::Thread::dumpStack unimplemented");
}

void
java::lang::Thread::interrupt (void)
{
  interrupt_flag = true;

  // Wake up this thread, whether it is sleeping or waiting for
  // another thread to exit.
  natThread *nt = (natThread *) data;
  _Jv_MutexLock (&nt->interrupt_mutex);
  _Jv_CondNotify (&nt->interrupt_cond, &nt->interrupt_mutex);
  _Jv_MutexUnlock (&nt->interrupt_mutex);

  _Jv_ThreadInterrupt (nt->thread);
}

void
java::lang::Thread::join (jlong millis, jint nanos)
{
  // FIXME: what if we are trying to join ourselves with no timeout?

  if (millis < 0 || nanos < 0 || nanos > 999999)
    _Jv_Throw (new IllegalArgumentException);

  Thread *current = currentThread ();
  if (current->isInterrupted ())
    _Jv_Throw (new InterruptedException);

  // Update the list of all threads waiting for this thread to exit.
  // We grab a mutex when doing this in order to ensure that the
  // required state changes are atomic.
  _Jv_MonitorEnter (this);
  if (! isAlive ())
    {
      _Jv_MonitorExit (this);
      return;
    }

  // Here `CURR_NT' is the native structure for the currently
  // executing thread, while `NT' is the native structure for the
  // thread we are trying to join.
  natThread *curr_nt = (natThread *) current->data;
  natThread *nt = (natThread *) data;

  JvAssert (curr_nt->next == NULL);
  // Put thread CURR_NT onto NT's list.  When NT exits, it will
  // traverse its list and notify all joiners.
  curr_nt->next = nt->joiner;
  nt->joiner = curr_nt;
  _Jv_MonitorExit (this);


  // Now wait for: (1) an interrupt, (2) the thread to exit, or (3)
  // the timeout to occur.  Use a `locker' object because _Jv_CondWait
  // can throw an exception.
  {
    locker l (&curr_nt->interrupt_mutex);
    _Jv_CondWait (&curr_nt->interrupt_cond,
		  &curr_nt->interrupt_mutex,
		  millis, nanos);
  }

  // Now the join has completed, one way or another.  Update the
  // joiners list to account for this.
  _Jv_MonitorEnter (this);
  JvAssert (nt->joiner != NULL);
  natThread *prev = 0;
  natThread *t;
  for (t = nt->joiner; t != NULL; t = t->next)
    {
      if (t == curr_nt)
	{
	  if (prev)
	    prev->next = t->next;
	  else
	    nt->joiner = t->next;
	  t->next = 0;
	  break;
	}
    }
  JvAssert (t != NULL);
  _Jv_MonitorExit (this);

  if (current->isInterrupted ())
    _Jv_Throw (new InterruptedException);
}

void
java::lang::Thread::resume (void)
{
  checkAccess ();
  JvFail ("java::lang::Thread::resume unimplemented");
}

void
java::lang::Thread::setPriority (jint newPriority)
{
  checkAccess ();
  if (newPriority < MIN_PRIORITY || newPriority > MAX_PRIORITY)
    _Jv_Throw (new IllegalArgumentException);

  jint gmax = group->getMaxPriority();
  if (newPriority > gmax)
    newPriority = gmax;

  priority = newPriority;
  natThread *nt = (natThread *) data;
  _Jv_ThreadSetPriority (nt->thread, priority);
}

void
java::lang::Thread::sleep (jlong millis, jint nanos)
{
  if (millis < 0 || nanos < 0 || nanos > 999999)
    _Jv_Throw (new IllegalArgumentException);

  if (millis == 0 && nanos == 0)
    ++nanos;

  Thread *current = currentThread ();
  if (current->isInterrupted ())
    _Jv_Throw (new InterruptedException);

  // We use a condition variable to implement sleeping so that an
  // interrupt can wake us up.
  natThread *nt = (natThread *) current->data;
  {
    // Use a locker because _Jv_CondWait can throw an exception.
    locker l (&nt->interrupt_mutex);
    _Jv_CondWait (&nt->interrupt_cond, &nt->interrupt_mutex,
		  millis, nanos);
  }

  if (current->isInterrupted ())
    _Jv_Throw (new InterruptedException);
}

void
java::lang::Thread::finish_ (void)
{
  // Notify all threads waiting to join this thread.
  _Jv_MonitorEnter (this);
  alive_flag = false;

  // Note that we don't bother cleaning up the joiner list here.  That
  // is taken care of when each thread wakes up again.
  natThread *nt = (natThread *) data;
  for (natThread *t = nt->joiner; t != NULL; t = t->next)
    {
      _Jv_MutexLock (&t->interrupt_mutex);
      _Jv_CondNotify (&t->interrupt_cond, &t->interrupt_mutex);
      _Jv_MutexUnlock (&t->interrupt_mutex);
    }

  group->remove (this);

  _Jv_MonitorExit (this);
}

void
java::lang::Thread::run__ (jobject obj)
{
  java::lang::Thread *thread = (java::lang::Thread *) obj;
  thread->run_ ();
}

void
java::lang::Thread::start (void)
{
  JvSynchronize sync (this);

  if (alive_flag)
    _Jv_Throw (new IllegalThreadStateException);

  alive_flag = true;
  natThread *nt = (natThread *) data;
  _Jv_ThreadStart (this, nt->thread, (_Jv_ThreadStartFunc *) &run__);
}

void
java::lang::Thread::stop (java::lang::Throwable *e)
{
  JvSynchronize sync (this);
  checkAccess ();
  if (! e)
    _Jv_Throw (new NullPointerException);
  natThread *nt = (natThread *) data;
  _Jv_ThreadCancel (nt->thread, e);
}

void
java::lang::Thread::suspend (void)
{
  checkAccess ();
  JvFail ("java::lang::Thread::suspend unimplemented");
}

void
java::lang::Thread::yield (void)
{
  _Jv_ThreadYield ();
}
