#include <config.h>
#include <stdlib.h>
#include <gcj/cni.h>
#include <jvm.h>
#include <java-threads.h>
#include <gnu/gcj/RawDataManaged.h>
#include <java/lang/Thread.h>
#include <java/lang/ThreadGroup.h>
#include <java/lang/IllegalArgumentException.h>
#include <java/lang/UnsupportedOperationException.h>
#include <java/lang/IllegalThreadStateException.h>
#include <java/lang/InterruptedException.h>
#include <java/lang/NullPointerException.h>
#include <jni.h>
#ifdef ENABLE_JVMPI
#include <jvmpi.h>
#endif
struct natThread
{
_Jv_Mutex_t join_mutex;
_Jv_ConditionVariable_t join_cond;
_Jv_Thread_t *thread;
JNIEnv *jni_env;
};
static void finalize_native (jobject ptr);
void
java::lang::Thread::initialize_native (void)
{
natThread *nt = (natThread *) _Jv_AllocBytes (sizeof (natThread));
data = (gnu::gcj::RawDataManaged *) nt;
_Jv_RegisterFinalizer (data, finalize_native);
_Jv_MutexInit (&nt->join_mutex);
_Jv_CondInit (&nt->join_cond);
nt->thread = _Jv_ThreadInitData (this);
nt->jni_env = NULL;
}
static void
finalize_native (jobject ptr)
{
natThread *nt = (natThread *) ptr;
_Jv_ThreadDestroyData (nt->thread);
#ifdef _Jv_HaveCondDestroy
_Jv_CondDestroy (&nt->join_cond);
#endif
#ifdef _Jv_HaveMutexDestroy
_Jv_MutexDestroy (&nt->join_mutex);
#endif
_Jv_FreeJNIEnv(nt->jni_env);
}
jint
java::lang::Thread::countStackFrames (void)
{
throw new UnsupportedOperationException
(JvNewStringLatin1 ("Thread.countStackFrames unimplemented"));
return 0;
}
java::lang::Thread *
java::lang::Thread::currentThread (void)
{
return _Jv_ThreadCurrent ();
}
jboolean
java::lang::Thread::holdsLock (jobject obj)
{
if (!obj)
throw new NullPointerException;
return !_Jv_ObjectCheckMonitor (obj);
}
void
java::lang::Thread::interrupt (void)
{
checkAccess ();
natThread *nt = (natThread *) data;
_Jv_ThreadInterrupt (nt->thread);
}
void
java::lang::Thread::join (jlong millis, jint nanos)
{
if (millis < 0 || nanos < 0 || nanos > 999999)
throw new IllegalArgumentException;
Thread *current = currentThread ();
natThread *nt = (natThread *) data;
_Jv_MutexLock (&nt->join_mutex);
if (! isAlive ())
{
_Jv_MutexUnlock (&nt->join_mutex);
return;
}
_Jv_CondWait (&nt->join_cond, &nt->join_mutex, millis, nanos);
_Jv_MutexUnlock (&nt->join_mutex);
if (current->isInterrupted (true))
throw new InterruptedException;
}
void
java::lang::Thread::resume (void)
{
checkAccess ();
throw new UnsupportedOperationException
(JvNewStringLatin1 ("Thread.resume unimplemented"));
}
void
java::lang::Thread::setPriority (jint newPriority)
{
checkAccess ();
if (newPriority < MIN_PRIORITY || newPriority > MAX_PRIORITY)
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)
throw new IllegalArgumentException;
if (millis == 0 && nanos == 0)
++nanos;
Thread *current = currentThread ();
natThread *nt = (natThread *) current->data;
_Jv_MutexLock (&nt->join_mutex);
_Jv_CondWait (&nt->join_cond, &nt->join_mutex, millis, nanos);
_Jv_MutexUnlock (&nt->join_mutex);
if (current->isInterrupted (true))
throw new InterruptedException;
}
void
java::lang::Thread::finish_ ()
{
natThread *nt = (natThread *) data;
group->removeThread (this);
#ifdef ENABLE_JVMPI
if (_Jv_JVMPI_Notify_THREAD_END)
{
JVMPI_Event event;
event.event_type = JVMPI_EVENT_THREAD_END;
event.env_id = _Jv_GetCurrentJNIEnv ();
_Jv_DisableGC ();
(*_Jv_JVMPI_Notify_THREAD_END) (&event);
_Jv_EnableGC ();
}
#endif
group = NULL;
_Jv_MutexLock (&nt->join_mutex);
alive_flag = false;
_Jv_CondNotifyAll (&nt->join_cond, &nt->join_mutex);
_Jv_MutexUnlock (&nt->join_mutex);
}
static void
_Jv_NotifyThreadStart (java::lang::Thread* thread)
{
#ifdef ENABLE_JVMPI
if (_Jv_JVMPI_Notify_THREAD_START)
{
JVMPI_Event event;
jstring thread_name = thread->getName ();
jstring group_name = NULL, parent_name = NULL;
java::lang::ThreadGroup *group = thread->getThreadGroup ();
if (group)
{
group_name = group->getName ();
group = group->getParent ();
if (group)
parent_name = group->getName ();
}
int thread_len = thread_name ? JvGetStringUTFLength (thread_name) : 0;
int group_len = group_name ? JvGetStringUTFLength (group_name) : 0;
int parent_len = parent_name ? JvGetStringUTFLength (parent_name) : 0;
char thread_chars[thread_len + 1];
char group_chars[group_len + 1];
char parent_chars[parent_len + 1];
if (thread_name)
JvGetStringUTFRegion (thread_name, 0,
thread_name->length(), thread_chars);
if (group_name)
JvGetStringUTFRegion (group_name, 0,
group_name->length(), group_chars);
if (parent_name)
JvGetStringUTFRegion (parent_name, 0,
parent_name->length(), parent_chars);
thread_chars[thread_len] = '\0';
group_chars[group_len] = '\0';
parent_chars[parent_len] = '\0';
event.event_type = JVMPI_EVENT_THREAD_START;
event.env_id = NULL;
event.u.thread_start.thread_name = thread_chars;
event.u.thread_start.group_name = group_chars;
event.u.thread_start.parent_name = parent_chars;
event.u.thread_start.thread_id = (jobjectID) thread;
event.u.thread_start.thread_env_id = _Jv_GetCurrentJNIEnv ();
_Jv_DisableGC ();
(*_Jv_JVMPI_Notify_THREAD_START) (&event);
_Jv_EnableGC ();
}
#endif
}
void
_Jv_ThreadRun (java::lang::Thread* thread)
{
try
{
_Jv_NotifyThreadStart (thread);
thread->run ();
}
catch (java::lang::Throwable *t)
{
try
{
thread->group->uncaughtException (thread, t);
}
catch (java::lang::Throwable *f)
{
}
}
thread->finish_ ();
}
void
java::lang::Thread::start (void)
{
JvSynchronize sync (this);
if (!startable_flag)
throw new IllegalThreadStateException;
alive_flag = true;
startable_flag = false;
natThread *nt = (natThread *) data;
_Jv_ThreadStart (this, nt->thread, (_Jv_ThreadStartFunc *) &_Jv_ThreadRun);
}
void
java::lang::Thread::stop (java::lang::Throwable *)
{
checkAccess ();
throw new UnsupportedOperationException
(JvNewStringLatin1 ("Thread.stop unimplemented"));
}
void
java::lang::Thread::suspend (void)
{
checkAccess ();
throw new UnsupportedOperationException
(JvNewStringLatin1 ("Thread.suspend unimplemented"));
}
static int nextThreadNumber = 0;
jstring
java::lang::Thread::gen_name (void)
{
jint i;
jclass sync = &java::lang::Thread::class$;
{
JvSynchronize dummy(sync);
i = ++nextThreadNumber;
}
jchar buffer[7+11];
jchar *bufend = (jchar *) ((char *) buffer + sizeof(buffer));
i = _Jv_FormatInt (bufend, i);
jchar *ptr = bufend - i;
*--ptr = '-';
*--ptr = 'd';
*--ptr = 'a';
*--ptr = 'e';
*--ptr = 'r';
*--ptr = 'h';
*--ptr = 'T';
return JvNewString (ptr, bufend - ptr);
}
void
java::lang::Thread::yield (void)
{
_Jv_ThreadYield ();
}
JNIEnv *
_Jv_GetCurrentJNIEnv ()
{
java::lang::Thread *t = _Jv_ThreadCurrent ();
if (t == NULL)
return NULL;
return ((natThread *) t->data)->jni_env;
}
void
_Jv_SetCurrentJNIEnv (JNIEnv *env)
{
java::lang::Thread *t = _Jv_ThreadCurrent ();
JvAssert (t != NULL);
((natThread *) t->data)->jni_env = env;
}
jint
_Jv_AttachCurrentThread(java::lang::Thread* thread)
{
if (thread == NULL || thread->startable_flag == false)
return -1;
thread->startable_flag = false;
thread->alive_flag = true;
natThread *nt = (natThread *) thread->data;
_Jv_ThreadRegister (nt->thread);
return 0;
}
java::lang::Thread*
_Jv_AttachCurrentThread(jstring name, java::lang::ThreadGroup* group)
{
java::lang::Thread *thread = _Jv_ThreadCurrent ();
if (thread != NULL)
return thread;
if (name == NULL)
name = java::lang::Thread::gen_name ();
thread = new java::lang::Thread (NULL, group, NULL, name);
_Jv_AttachCurrentThread (thread);
_Jv_NotifyThreadStart (thread);
return thread;
}
java::lang::Thread*
_Jv_AttachCurrentThreadAsDaemon(jstring name, java::lang::ThreadGroup* group)
{
java::lang::Thread *thread = _Jv_ThreadCurrent ();
if (thread != NULL)
return thread;
if (name == NULL)
name = java::lang::Thread::gen_name ();
thread = new java::lang::Thread (NULL, group, NULL, name);
thread->setDaemon (true);
_Jv_AttachCurrentThread (thread);
_Jv_NotifyThreadStart (thread);
return thread;
}
jint
_Jv_DetachCurrentThread (void)
{
java::lang::Thread *t = _Jv_ThreadCurrent ();
if (t == NULL)
return -1;
_Jv_ThreadUnRegister ();
t->finish_ ();
return 0;
}