/*
 * thread.c
 * Thread support.
 *
 * Copyright (c) 1996 T. J. Wilkinson & Associates, London, UK.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * Written by Tim Wilkinson <tim@tjwassoc.demon.co.uk>, 1996.
 */

#define	DBG(s)
#define	SDBG(s)

#include "config.h"
#include "config-std.h"
#include "config-mem.h"
#include "config-io.h"
#include "config-signal.h"
#include "jtypes.h"
#include "access.h"
#include "object.h"
#include "constants.h"
#include "classMethod.h"
#include "baseClasses.h"
#include "lookup.h"
#include "thread.h"
#include "locks.h"
#include "exception.h"
#include "support.h"
#include "external.h"
#include "errors.h"
#include "lerrno.h"
#include "gc.h"
#include "md.h"

Hjava_lang_Class* ThreadClass;
Hjava_lang_Class* ThreadGroupClass;
Hjava_lang_Thread* currentThread;
Hjava_lang_Thread* garbageman;
Hjava_lang_Thread* finalman;
Hjava_lang_ThreadGroup* standardGroup;
int threadStackSize;

static void firstStartThread(void);
static void createInitialThread(char*);
static Hjava_lang_Thread* createDaemon(void*, char*);

static void walkThread(void*, uint32);
static void finalizeThread(void*);
static gcFuncs gcThread = { walkThread, finalizeThread };

/****/
static int numberOfThreads;
static int talive;
static int tdaemon;
static void alarmException(int);
static void allocThreadCtx(Hjava_lang_Thread*, int);
/***/

/*
 * Initialise threads.
 */
void
initThreads(void)
{
	/* Set default thread stack size if not set */
	if (threadStackSize == 0) {
		threadStackSize = THREADSTACKSIZE;
	}

	/* Get a handle on the thread and thread group classes */
	ThreadClass = lookupClass(THREADCLASS);
	assert(ThreadClass != 0);
	ThreadGroupClass = lookupClass(THREADGROUPCLASS);
	assert(ThreadGroupClass != 0);

	/* Create base group */
	standardGroup = (Hjava_lang_ThreadGroup*)newObject(ThreadGroupClass);
	assert(standardGroup != 0);
	unhand(standardGroup)->parent = 0;
	unhand(standardGroup)->name = makeJavaString("main", 4);
	unhand(standardGroup)->maxPriority = java_lang_Thread_MAX_PRIORITY;
	unhand(standardGroup)->destroyed = 0;
	unhand(standardGroup)->daemon = 0;
	unhand(standardGroup)->vmAllowSuspension = 0;
	unhand(standardGroup)->nthreads = 0;
	unhand(standardGroup)->threads = 0;
	unhand(standardGroup)->ngroups = 0;
	unhand(standardGroup)->groups = 0;

	/* Allocate a thread to be the main thread */
	createInitialThread("main");

#if defined(GC_ENABLE)
	/* Start the GC daemons we need */
	finalman = createDaemon(&finaliserMan, "finaliser");
	garbageman = createDaemon(&gcMan, "gc");
	gc_mode = GC_ENABLED;
#endif

#if !defined(USE_NATIVE_THREADS)
	/* Plug in the alarm handler */
#if defined(SIGALRM)
	catchSignal(SIGALRM, alarmException);
#else
#error "No alarm signal support"
#endif
#endif
}

/*
 * Start a new thread running.
 */
void
startThread(Hjava_lang_Thread* tid)
{
	THREAD_CREATE(tid, &firstStartThread);
}

/*
 * Stop a thread from running and terminate it.
 */
void
stopThread(Hjava_lang_Thread* tid)
{
	if (currentThread == tid) {
		SignalError(0, "java.lang.ThreadDeath", "");
	}
	else {
		THREAD_STOP(tid);
	}
}

/*
 * Create the initial thread with a given name.
 *  We should only ever call this once.
 */
static
void
createInitialThread(char* nm)
{
	/* Allocate a thread to be the main thread */
	currentThread = (Hjava_lang_Thread*)newObject(ThreadClass);
	assert(currentThread != 0);

	unhand(currentThread)->name = (HArrayOfChar*)makeJavaCharArray(nm, strlen(nm));
	unhand(currentThread)->priority = java_lang_Thread_NORM_PRIORITY;
	unhand(currentThread)->threadQ = 0;
	unhand(currentThread)->single_step = 0;
	unhand(currentThread)->daemon = 0;
	unhand(currentThread)->stillborn = 0;
	unhand(currentThread)->target = 0;
	unhand(currentThread)->initial_stack_memory = threadStackSize;
	unhand(currentThread)->group = standardGroup;

	THREAD_CREATEFIRST(currentThread);

	/* Attach thread to threadGroup */
	do_execute_java_method(0, (Hjava_lang_Object*)unhand(currentThread)->group, "add", "(Ljava/lang/Thread;)V", 0, 0, currentThread);
}

/*
 * Start a daemon thread.
 */
static
Hjava_lang_Thread*
createDaemon(void* func, char* nm)
{
	Hjava_lang_Thread* tid;

DBG(	printf("createDaemon %s\n", nm);				)

	/* Keep daemon threads as root objects */
	tid = (Hjava_lang_Thread*)newObject(ThreadClass);
	assert(tid != 0);

	unhand(tid)->name = (HArrayOfChar*)makeJavaCharArray(nm, strlen(nm));
	unhand(tid)->priority = java_lang_Thread_MAX_PRIORITY;
	unhand(tid)->threadQ = 0;
	unhand(tid)->single_step = 0;
	unhand(tid)->daemon = 1;
	unhand(tid)->stillborn = 0;
	unhand(tid)->target = 0;
	unhand(tid)->initial_stack_memory = threadStackSize;
	unhand(tid)->group = 0;

	THREAD_CREATE(tid, func);

	return (tid);
}

/*
 * All threads start here.
 */
static
void
firstStartThread(void)
{
	THREAD_INIT();

DBG(	printf("firstStartThread %x\n", CURRENTTHREAD());		)

	/* Find the run()V method and call it */
	do_execute_java_method(0, (Hjava_lang_Object*)CURRENTTHREAD(), "run", "()V", 0, 0);
	do_execute_java_method(0, (Hjava_lang_Object*)CURRENTTHREAD(), "exit", "()V", 0, 0);

	THREAD_EXIT();
}

/*
 * Walk a thread, context and stack.
 */
static
void
walkThread(void* base, uint32 size)
{
	Hjava_lang_Thread* tid;

	tid = (Hjava_lang_Thread*)base;

#ifdef notyet
        markObject (.. class ..);
#endif

	markObject(unhand(tid)->name);
	markObject(unhand(tid)->threadQ);
	markObject(unhand(tid)->target);
	markObject(unhand(tid)->group);

	/* Since this thread my be extended by another class, walk any
	 * remaining data.
	 */
	gcStats.markedmem -= (size - sizeof(Hjava_lang_Thread));
	walkConservative(tid+1, size - sizeof(Hjava_lang_Thread));

	if (unhand(tid)->PrivateInfo != 0) {
		ctx* ct;
		ct = TCTX(tid);
		/* Nothing in context worth looking at except the stack */
#if defined(STACK_GROWS_UP)
		walkConservative(ct->stackBase, ct->restorePoint - ct->stackBase);
#else
		walkConservative(ct->restorePoint, ct->stackEnd - ct->restorePoint);
#endif
	}
}

/*
 * Finalize a thread.
 *  This is chiefly to free the thread context.
 */
static
void
finalizeThread(void* mem)
{
	Hjava_lang_Thread* tid;
        Method* final;

	tid = (Hjava_lang_Thread*)mem;

	THREAD_FREE(tid);

	/* Call thread finalizer if it has one (can it have one?) */
	final = findMethod(OBJECT_CLASS(&tid->base), final_name, void_signature);
	if (final != NULL) {
		CALL_KAFFE_METHOD(final, &tid->base);
	}
}

void
walkLiveThreads(void)
{
	Hjava_lang_Thread* tid;

	for (tid = liveThreads; tid != NULL; tid = TCTX(tid)->nextlive) {
		walkMemory(tid);
	}
}

/*
 * Yield process to another thread of equal priority.
 */
void
yieldThread()
{
	THREAD_YIELD();
}

/*
 * Put a thread to sleep.
 */
void
sleepThread(jlong time)
{
	THREAD_SLEEP(time);
}

/*
 * Is this thread alive?
 */
bool
aliveThread(Hjava_lang_Thread* tid)
{
	bool status;

DBG(	printf("aliveThread: tid 0x%x\n", tid);				)

	THREAD_ALIVE(tid, status);

	return (status);
}

/*
 * How many stack frames have I invoked?
 */
jint
framesThread(Hjava_lang_Thread* tid)
{
	jint count;

	THREAD_FRAMES(tid, count);

	return (count);
}

/*
 * If we're not using native threads, include some extra thread support.
 */
#if !defined(USE_NATIVE_THREADS)
#include "thread-internal.c"
#endif
