/* sthreads_scout.c -- function definitions for Scout thread support */

/*
 * defines
 *    mythread
 *    sthread_init
 *    sthread_launch
 *    sthread_got_exception
 *    sthread_create
 *    sthread_stop
 *    sthread_yield
 *    sthread_sleep
 *    sthread_suspend
 *    sthread_resume
 *    sthread_setpriority
 *    sthread_current
 *    sthread_exit
 *    sthread_dontkill_start
 *    sthread_dontkill_end
 *    sthread_mutex_init
 *    sthread_mutex_lock
 *    sthread_mutex_unlock
 *    sthread_mutex_destroy
 *    sthread_cond_init
 *    sthread_cond_wait
 *    sthread_cond_signal
 *    sthread_cond_broadcast
 *    sthread_cond_destroy
 * 
 * TO DO:
 *   - There is some code in here that is not dependent
 *     on the underlying threads package - it should probably
 *     be broken out
 *
 * NOTE: We use the (otherwise unused) threadQ field of
 *       a java.lang.Thread to point to a thread's TCB
 */

#include <stdio.h>
#include <stddef.h>
#include <assert.h>
#include <stdlib.h>
#include <sys/time.h>
#ifdef SCOUT
#include <sys/errno.h>
#include <scout/path.h>
#include <scout/fsfattr.h>
#else /* SCOUT */
#include <errno.h>
#endif /* SCOUT */
#include <limits.h>

#include "toba.h"
#include "sthreads.h"
#include "runtime.h"

#include "java_lang_Object.h"
#include "java_lang_Thread.h"
#include "java_lang_ThreadGroup.h"
#include "java_lang_IllegalThreadStateException.h"

#ifdef SCOUT
#include <scout/event.h>
#include <scout/time.h>
#include <scout/thread.h>
#include <scout/thread_prio.h>
#include <scout/semaphore.h>
#else /* SCOUT */
#include "scout_thread.h"
#include "scout_semaphore.h"
#include "scout_thread_toba.h"
#endif /* SCOUT */

/* counter of non-daemon threads and semaphore for signalling last thread */
static struct Semaphore last_thread;

static int thread_counter;

static int sthreads_initialized = 0;

/* single threaded flag and the single threads tcb */
int singlethreaded = 1;
static tcb *onlythread;

void GC_invoke_finalizers();

/* Preemption implementataion in scout threads -
 *
 * Scout threads are inherently non-preemptive. However, the Java thread
 * model requires preemption. How do we deal with this situation? Since
 * we control the code that is generated, we have the translator and 
 * JIT-generated code potentially yield on every back edge in the control
 * flow graph and on every function call. Potentially, because function
 * call overhead at every back-edge would be slow. 
 *
 * Instead of doing this function call at every back edge, we define global 
 * variables that define a time-slice for the currently running thread. These 
 * variables, timeSliceEnd (which we define) and timeNow (which is defined by 
 * scout threads and incremented in the clock interrupt) are compared. 
 * When reset, Our timeslice runs from [timeNow, timeSliceEnd).I f our
 * timeslice has expired, we make a function call for a full 
 * preemption/suspension/kill check. This meets the requirements of the 
 * Java language specification that if a thread is suspended or killed
 * by another thread, the killed/suspended thread can only run for a
 * bounded amount of time before it is suspended or killed.
 */

/* Time slice length in milliseconds */
#define TIME_SLICE_MILLIS 50

/* Timeslice length in ticks */
static unsigned int timeSliceLen;
/* Variable used to do yield checking. */
unsigned long timeSliceEnd;

static void 
sthread_timeslice_init()
{
    char *tslen;
    int tsticks, tsmsec;

#ifndef SCOUT
    if ((tslen = getenv("TOBA_TIMESLICE_MILLIS")) != 0)
	tsmsec = atoi(tslen);
    else
#endif /* SCOUT */
	tsmsec = TIME_SLICE_MILLIS;

    tsticks = (tsmsec * TIME_HZ / 1000);
    timeSliceLen = (tsticks > 0) ? tsticks : 1;
}

static void
sthread_new_timeslice()
{
#ifdef SCOUT
	if (singlethreaded)
	    timeSliceEnd = ULONG_MAX;
        else
#endif
	    timeSliceEnd = timeNow + timeSliceLen;
}

/* Check for and handle asynchronous actions */
static void
sthread_check_async()
{
    tcb *thr = sthread_current();

    if (thr == 0)
	return;

    if (thr->defer == 0) {
	if ((thr->do_async_exception) &&
	    (thr->got_async_exception == 0)) {
	    thr->got_async_exception = 1;
	    thr->do_async_exception = 0;
	    athrow(thr->mythread.exception);
	    /* NOT REACHED */
	} else if (thr->do_suspend) {
	    semaphoreSignal(&thr->synch);
	    thr->state = TS_SUSPENDED;
	    semaphoreWait(&thr->suspend);
	    thr->state = TS_RUNNING;
	    /* done_wait intentionally not called! */
        }
    }
}

/* We just left a wait on a semaphore (which may have included a
 * yield). Check if any async events happened while I was there. */
static void
sthread_done_wait()
{
    sthread_check_async();
    /* Do not reset the time slice: we may not have yielded, if we didn't
     * have to wait to acquire the semaphore.  Reset the time slice
     * at the call points to this function if we know we're OK. */
}

void
sthread_got_exception(void)
{
}

/*
 * Initialization of thread system
 */
void
sthread_init(void)
{
    tcb *newtcb;
    struct in_java_lang_Thread *obj;
    struct in_java_lang_ThreadGroup *sysgroup;

    /* Start up singlethreaded */
    singlethreaded = 1;

    /* create a java.lang.Thread object and a thread group
     * for this thread */
    newtcb = (tcb *)allocate(sizeof(*newtcb));

    /* associate the TCB with this thread */
    tSelf->info = (void *) newtcb;
    newtcb->thread = tSelf;
    semaphoreInit(&newtcb->synch, 0);
    semaphoreInit(&newtcb->suspend, 0);
    onlythread = newtcb;

    /* defer asynchronous actions until we're ready for them */
    newtcb->defer = 1;
    newtcb->do_suspend = newtcb->do_async_exception = 0;
    newtcb->state = TS_RUNNING;

    /* new ThreadGroup() */
    sysgroup = new(&cl_java_lang_ThreadGroup.C);
    init__UzqJE(sysgroup);

    /* new Thread(group, runnable, string) */
    obj = new(&cl_java_lang_Thread.C);
    newtcb->obj = obj;
    init_TRS_jtRvs(obj, sysgroup, obj, javastring("main"));

    /* XXX main should have its own thread group */

    /* set up non-daemon thread counter and locks */
    thread_counter = 0;

    semaphoreInit(&last_thread, 0);

    /* the thread is still not killable yet - 
     * the lock is held until default exception
     * handling is set up in start_thread() */

#ifndef SCOUT
    /* Only need to initialize user-level timers on Linux */
    threadTimerInit();
#endif /* SCOUT */
    /* compute and initialize the size of a timeslice */
    sthread_timeslice_init();

    sthreads_initialized = 1;
}

/* Note that this routine runs prior to calling sthread_init */
void
sthread_launch(void (*init)(AnyType arg), void *p)
{
    AnyType arg;
    arg.p = p;
#ifdef SCOUT
    /* In scout, just call the start function.  It won't return, since the
     * thread should call sthread_exit and terminate in runtime.c/real_start
     * (which is what init should be a pointer to). */
   init (arg);
#else /* SCOUT */
    /* If we're running outside a real scout kernel, we'll take responsibility
     * for initializing the thread system.  If we're in scout, doing this
     * wreaks great havoc. */
    threadInit (init, arg);
#endif /* SCOUT */
    /*NOTREACHED*/
}

static void
add_nondaemon(void)
{
    /* increment count of non-daemon threads */
    thread_counter ++;
}

static void
rem_nondaemon(void)
{
    int done;

    thread_counter --;
    done = (thread_counter == 0);
    if(done) {
        semaphoreSignal(&last_thread); 
    }
}

static void 
thread_startup(AnyType obj)
{
    struct in_java_lang_Thread *o = (Object)obj.p;
    tcb *curthread;
    int monitor_held;

    /* let parent continue - we're done our critical stuff */
    curthread = (tcb *)o->threadQ;
    /* NB: Originally disabled on scout */
    semaphoreSignal(&curthread->synch);

    /* set our priority and associate our TCB with the thread */
    /* TODO thr_setprio(curthread->thread, o->priority); */

    curthread->thread = tSelf;
    tSelf->info = (void *) curthread;
    sthread_new_timeslice();

    /* Actually start the thread's function */
    start_thread(o->class->M.run__QJ0S5.f, o);

    /* break association of object and tcb - mark thread as not being active */
    o->threadQ = 0;

    /* send notification up to anyone doing a join on us */
    /* TODO NB: Originally disabled on scout--why? */
    sthread_dontkill_start(&curthread->mythread);
    monitorenter(o, &curthread->mythread, 1, &monitor_held);
    monitornotifyall(o);
    monitorexit(o, &curthread->mythread, 0, &monitor_held);
    sthread_dontkill_end(&curthread->mythread);

    /* last non-daemon thread signals the main thread to exit */
    if(o->daemon == JAVA_FALSE)
        rem_nondaemon();
}

/*
 * create a new thread associated with this java.lang.Thread object
 * and start it running in its start method
 */
void
sthread_create(struct in_java_lang_Thread *o)
{
    tcb *newtcb;
    struct ThreadOptions opt;
    Thread t;

    /* dont start a new thread if there's already a running thread */
    if(o->threadQ) {
        throwMesg(&cl_java_lang_IllegalThreadStateException.C,
                  "thread already running");
    }

    /* we've just gone multithreaded boys */
    if (singlethreaded) {
        singlethreaded = 0;
        fixup_monitors();
	sthread_new_timeslice();
    }
    
    newtcb = (tcb *)allocate(sizeof(*newtcb));
    newtcb->obj = o;
    semaphoreInit(&newtcb->synch, 0); 
    semaphoreInit(&newtcb->suspend, 0); 
    newtcb->state = TS_RUNNING;

    /* defer asynchronous actions until we're ready for them */
    newtcb->defer = 1;
    o->threadQ = newtcb;    /* threadQ points to TCB */

    if(o->daemon == JAVA_FALSE)
        add_nondaemon();

    /* Wipe opt, lest threadCreate assume it has a pointer to the
     * scheduler object. */
    memset (&opt, 0, sizeof (opt));
#ifdef SCOUT
    opt.name = "Joust sthread";
#else /* SCOUT */
    opt.name = "noname";
#endif /* SCOUT */
    opt.arg.p = o;
    opt.priority.i = THREAD_PRIO_STD;	/* will change eventually */
#ifdef SCOUT
    opt.scheduler = &threadFixedPrioScheduler;
    opt.stacksize = STHREAD_STACK_SIZE;
#endif /* SCOUT */

    if ((t = threadCreate (thread_startup, &opt)) == 0) {
        /* thread creation failed */
        if(o->daemon == JAVA_FALSE)
            rem_nondaemon();
	printf ("Thread creation failed!!\n");
    }

    threadWakeup (t);

    /* wait here until child does critical stuff */
    /* NB: Originally disabled on scout */
    semaphoreWait(&newtcb->synch);
    sthread_done_wait();
    sthread_new_timeslice();
}

/*
 * Retrieve per-thread state
 */
tcb *
sthread_current()
{
    tcb *ret;

    if (singlethreaded) {
        return onlythread;
    } else {
        ret = (tcb *) tSelf->info;
        /* should only be null during thread initialization. No check, 
	 * though, because other non-toba threads may want to use
	 * the collector. Because of that, non-sthread-threads could
	 * enter here through the finalizer code. The finalizer code 
	 * explicitly deals with that eventuality */
        return ret;
    }
}

struct mythread *
mythread()
{
    return &sthread_current()->mythread;
}

void
sthread_preemption_point()
{
    GC_invoke_finalizers();
    threadYield();
    sthread_check_async();
    sthread_new_timeslice();
}

void
sthread_yield()
{
    /* A yield is just a full preemption in here */
    sthread_preemption_point();
}

extern long threadTimerTicks;

#ifdef SCOUT
static void
sthread_event_handler(Event e, AnyType a)
{
    Semaphore sem = (Semaphore) a.p;

    semaphoreSignal(sem);
}
#endif /* SCOUT */

void
sthread_sleep(Long millis)
{
#ifdef SCOUT

    tcb *thr = sthread_current();

    eventDetach(eventSchedule(sthread_event_handler, 
                              ptrToAny((void *)&thr->suspend), millis * 1000));
    semaphoreWait(&thr->suspend);

#else /* SCOUT */

    extern void threadSleep(long millis);
    threadSleep(millis);

#endif /* SCOUT */

    sthread_done_wait();
    sthread_new_timeslice();
}

void
sthread_suspend(struct in_java_lang_Thread *o)
/*ARGSUSED*/
{
    tcb *thr = o->threadQ;

    thr->do_suspend = 1;
    semaphoreWait(&thr->synch); /* Wait until he suspends */
    sthread_done_wait();
    sthread_new_timeslice();
}

void
sthread_resume(struct in_java_lang_Thread *o)
/*ARGSUSED*/
{
    tcb *thr = o->threadQ;

    if (thr->do_suspend == 0)
	return;
    thr->do_suspend = 0;
    semaphoreSignalAll(&thr->suspend);
}

void
sthread_setpriority(struct in_java_lang_Thread *o, int priority)
/*ARGSUSED*/
{
}

void
sthread_stop(struct in_java_lang_Thread *o, Object e)
/*ARGSUSED*/
{
    tcb *thread = (tcb *)o->threadQ;

    if ( (thread == sthread_current()) && !thread->defer) {
	athrow(e);
	/* NOTREACHED */
    }

    if(thread && (thread->got_async_exception == 0)) {
        thread->mythread.exception = e;
	thread->do_async_exception = 1;
    }
}

/*
 * wait for all non-daemon threads to exit
 */
void
sthread_exit()
{
    /* Thread counter not incremented by initialization thread */
    while(thread_counter != 0) {
        semaphoreWait(&last_thread);
    }
}

/* start a sequence in which a thread shouldn't be killed */
void
sthread_dontkill_start(struct mythread *thr)
/*ARGSUSED*/
{
    if (thr == 0) return;
    ((tcb *)thr)->defer++;
}

/* end a sequence in which a thread shouldn't be killed */
void
sthread_dontkill_end(struct mythread *thr)
/*ARGSUSED*/
{
    if (thr == 0) return;
    ((tcb *)thr)->defer--;
}

int
sthread_dontkill_p(struct mythread *thr)
/*ARGSUSED*/
{
    return ((tcb *)thr)->defer;
}

void
sthread_mutex_init(struct sthread_mutex *m)
{
    semaphoreInit(&m->mutex, 1);
}

void
sthread_mutex_lock(struct sthread_mutex *m)
{
    semaphoreWait(&m->mutex);
    m->owner = tSelf;
    sthread_done_wait();
    /* Don't reset time slice */
}

void
sthread_mutex_unlock(struct sthread_mutex *m)
{
    if (tSelf == m->owner) {
	if (semaphoreCount(&m->mutex) <= 1) {
	    semaphoreSignal(&m->mutex);
	} else {
	    printf ("sthread_mutex_unlock(): mutex is not locked!\n");
	}
    } else
	printf ("sthread_mutex_unlock(): thread does not own lock!\n");
}

void
sthread_mutex_destroy(struct sthread_mutex *m)
/*ARGSUSED*/
{
}

void
sthread_cond_init(struct sthread_cond *c)
{
    semaphoreInit (&c->c, 0);
    c->nsignals = 0;
    c->nwaiting = 0;
}

void
sthread_cond_wait(struct sthread_cond *c, struct sthread_mutex *m, 
		  Long timeout)
{
    /* timeout not yet implemented */

    c->nwaiting++;
    while (c->nsignals == 0) {
	sthread_mutex_unlock(m);
	semaphoreWait(&c->c);
	sthread_mutex_lock(m);
    };
    c->nwaiting--;
    c->nsignals--;
    sthread_done_wait();
    /* Don't reset time slice */
}

void
sthread_cond_signal(struct sthread_cond *c) 
/*ARGSUSED*/
{
    if (c->nsignals < c->nwaiting) {
	c->nsignals++;
	semaphoreSignal(&c->c);
    }
}

void
sthread_cond_broadcast(struct sthread_cond *c)
/*ARGSUSED*/
{
    if (c->nsignals < c->nwaiting) {
	c->nsignals = c->nwaiting;
	semaphoreSignalAll(&c->c);
    }
}

void
sthread_cond_destroy(struct sthread_cond *c)
/*ARGSUSED*/
{
}
