/*
 * process.c
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1996,1993,1991,1990  Arizona Board of Regents
 *
 * $Revision: 1.5 $
 * $Date: 1996/06/19 16:23:29 $
 */

#include "platform.h"
#include "event.h"
#include "event_i.h"
#include "xk_debug.h"
#include <stdarg.h>

int SignalsPossible = 0;

int traceprocesscreation, traceprocessswitch;

#ifdef __STDC__

static void	wake_sem( Event, VOID * );

#else

static void	wake_sem();

#endif

#define MAXARGS 6

typedef void (*CreateProcFunc) ( long, ... );

extern int 	CurHost;
extern void	xsimWaitUntilHostNotBusy();
extern int	xsimHostNotBusy();

#ifdef XMEMTRACK

static int	ProcTrackId=0;

extern char     *xMallocTrack(unsigned, int);
extern char     *xMallocZeroTrack(unsigned, int);
extern void     xFreeTrack(char *, unsigned, int);
extern int      TrackGetId(char *);

#define xMalloc(s)      xMallocTrack(s, ProcTrackId)
#define xMallocZero(s)  xMallocZeroTrack(s, ProcTrackId)

#endif

#ifdef MASTER_LOCK
extern Semaphore master_lock;
#endif

#define ARGSLISTSIZE 20

struct arglist {
  long arg[MAXARGS+2];
  struct argslist *next;
} *argslist = NULL;

static struct argslist *
getMoreArgs()
{
  struct arglist *list=NULL, *elem;
  int i;

  for (i=0; i < ARGSLISTSIZE; i++) {
    elem = (struct arglist *)xMalloc(sizeof(struct arglist));
    elem->next = list;
    list = elem;
  }
  return list;
}

static void enter_master_sledgehammer_monitor(struct arglist *);


/*
 *  xkernel_activate_thread
 *
 *     startup a thread, given the arguments
 *
 */
static void xkernel_activate_thread(struct arglist *args, int priority)
{
  Scout_Thread *child;
  Scout_Thread_Options options;

  xTrace0(processcreation, TR_FULL_TRACE, "xkernel_activate_thread enter");

  if (priority < THREAD_MAXPRIO) {
    priority=THREAD_MAXPRIO;
    xTrace0(processcreation, TR_SOFT_ERRORS, "requested priority was too high");
  }
  else if (priority > THREAD_MINPRIO) {
    priority=THREAD_MINPRIO;
    xTrace0(processcreation, TR_SOFT_ERRORS, "requested priority was too low");
  }

  options.name = "normal";
  options.arg = (void *)args;
  options.stack_size = DEFAULT_STACK_SIZE;
  options.priority = priority;

  /* start the thread suspended so we can set its priority */
  child = scout_thread_create((Scout_Thread_Func)enter_master_sledgehammer_monitor, &options);
  if (child) {
    xTrace1(processcreation, TR_MAJOR_EVENTS, "Created thread_id: %x", child);
    xTrace1(processswitch, TR_GROSS_EVENTS, "Created thread_id: %x", child);

    scout_thread_wakeup(child);
  }
  else {
    xTrace0(processswitch, TR_ERRORS, "xkernel_activate_thread: thr_create failed");
  }
}

/*
 *
 * CreateProcess(function_ptr, priority, argcount, arg1, arg2, ...)
 *
 */

bool
CreateProcess(void *r, int pri, int nargs, ... )
{
  va_list ap;
  int i;
  struct arglist *argelem;

  xTrace0(processcreation, TR_EVENTS, "Enter CreateProcess");

  va_start(ap, nargs);

  if (!argslist) {
    argslist = getMoreArgs();
  }
  argelem = argslist;
  argslist = argslist->next;

  argelem->arg[0] = (long)r;
  argelem->arg[1] = nargs;
  if (nargs > MAXARGS)
    Kabort("CreateProcess: cannot handle more than 6 args.");

  for (i=0; i<nargs; i++)
    argelem->arg[i+2] = va_arg(ap, long);

  xTrace2(processcreation, TR_MAJOR_EVENTS, "Solaris CreateProcess: func_addr %x nargs %d",
         r, nargs);

  xkernel_activate_thread(argelem, pri);
  return(TRUE);
}

/*
 *  CreateKernelProcess
 *
 *
 */
void
CreateKernelProcess(CreateProcFunc r, int prio, long arg1, long arg2,long arg3)
{
    CreateProcess((void *)r, prio, 3, arg1, arg2, arg3);
}


Yield()
{
  int	  host=CurHost;

  xk_master_unlock();
  scout_thread_yield();
  xk_master_lock();
  xsimWaitUntilHostNotBusy(host);
  CurHost = host;
}

/*
 *
 * enter_master_sledgehammer_monitor
 *
 * Concurrency control is enforced in the xkernel by means of a monitor,
 *  in which all execution takes place. Here it is!
 *
 * A new thread begins here.
 * Inside the kernel:
 *      Dequeue a newThreadInfo block and do what it says
 * Outside the kernel:
 *      We are passed the argBlock.
 *
 * All the process block stuff looks totally defunct
 *
 */

static void
enter_master_sledgehammer_monitor(struct arglist *argelem)
{
  CreateProcFunc user_fn;
  int nargs;
  int threadcount;
  int     host=CurHost;
  long *args;

  xTrace0(processcreation, TR_MAJOR_EVENTS, "enter_master_monitor: entering\n");
  xk_master_lock();

  xTrace0(processcreation, TR_MAJOR_EVENTS, "enter_master_monitor: locked\n");

  user_fn = (CreateProcFunc)argelem->arg[0];
  nargs = argelem->arg[1];
  args = &argelem->arg[2];

  CurHost = host;
  xTrace1(processcreation,TR_EVENTS,"enter_master_monitor: starting thread %x",
	  scout_thread_self());

  switch(nargs) {
  case 0: (user_fn)(0);
      break;
  case 1: (user_fn)(args[0]);
      break;
  case 2: (user_fn)(args[0], args[1]);
      break;
  case 3: (user_fn)(args[0], args[1], args[2]);
      break;
  case 4: (user_fn)(args[0], args[1], args[2], args[3]);
      break;
  case 5: (user_fn)(args[0], args[1], args[2], args[3], args[4]);
      break;
  case 6: (user_fn)(args[0],args[1],args[2],args[3],args[4],args[5]);
      break;
  }

  argelem->next = argslist;
  argslist = argelem;

  xk_master_unlock();
  xTrace1(processcreation, TR_MAJOR_EVENTS, "master_monitor: thread %x unlocks",
           scout_thread_self() );

}


static void
wake_sem( ev, arg )
    Event	ev;
    VOID 	*arg;
{
  semSignal((Semaphore *)arg);
}


void
Delay(n)
     int n;
{
  Semaphore s;

  semInit(&s, 0);
  evDetach( evSchedule(wake_sem, &s, n * 1000) );
  semWait(&s);
}


  
void
semInit(s, n)
register Semaphore *s;
unsigned n;
{
  s->count = n;
  scout_queue_init(&s->waitingQ);
}

void
semWait(Semaphore *s)
{
  if (--(s)->count < 0)
    realP(s);
}

void
semSignal(Semaphore *s)
{
  if (++(s)->count <= 0)
    realV(s);
}


void
mutexInit(Semaphore *s)
{
  s->count = 1;
  scout_queue_init(&s->waitingQ);
}

void
mutexLock(Semaphore *s)
{
  if (--(s)->count < 0) {
    scout_queue_append(&s->waitingQ, scout_thread_self());
    scout_thread_suspend();
  }
}

void
mutexUnlock(Semaphore *s)
{
  Scout_Thread *t;
  
  if (s->count++ < 0) {
    t = scout_queue_remove(&s->waitingQ);
    scout_thread_wakeup(t);
  }
}

realPforHost(s)
register Semaphore *s;
{
  int	   host=CurHost;

  xTrace2(processswitch, TR_MAJOR_EVENTS, "P on %#x", s, NULL);

#ifdef XK_THREAD_TRACE
  evMarkBlocked(s);
#endif XK_THREAD_TRACE

  s->count--;
  while (1) {
    xk_master_unlock();
    scout_queue_append(&s->waitingQ, scout_thread_self());
    scout_thread_suspend();
    xk_master_lock();
    if (xsimHostNotBusy(host))
      break;
  }

#ifdef XK_THREAD_TRACE
  evMarkRunning();
#endif XK_THREAD_TRACE

  CurHost = host;
}

realP(s)
register Semaphore *s;
{
  int	   host=CurHost;

  xTrace2(processswitch, TR_MAJOR_EVENTS, "P on %#x", s, NULL);
  
  if (s->count < 0) {

#ifdef XK_THREAD_TRACE
    evMarkBlocked(s);
#endif XK_THREAD_TRACE

    xk_master_unlock();
    scout_queue_append(&s->waitingQ, scout_thread_self());
    scout_thread_suspend();
    xk_master_lock();
    xsimWaitUntilHostNotBusy(host);

#ifdef XK_THREAD_TRACE
    evMarkRunning();
#endif XK_THREAD_TRACE

    CurHost = host;
  }
}

realV(s)
register Semaphore *s;
{
  Scout_Thread *t;
  xTrace2(processswitch, TR_MAJOR_EVENTS, "V on %#x by %s", s, NULL);

  if (s->count <= 0) {
    t = scout_queue_remove(&s->waitingQ);
    scout_thread_wakeup(t);
    xTrace2(processswitch, TR_GROSS_EVENTS, "Unblocking %x by V on %#x", t, s);
  }
}

VAll(s)
register Semaphore *s;
{
  Scout_Thread *t;
  xTrace2(processswitch, TR_MAJOR_EVENTS, "VAll on %#x by %s", s, NULL);

  while (s->count < 0) {
    t = scout_queue_remove(&s->waitingQ);
    scout_thread_wakeup(t);
    s->count ++;
  }
}

FreeSema(s)
register Semaphore *s;
{
  return 0;
}
