/*
 * process.c
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Revision: 1.3 $
 * $Date: 1997/01/28 22:53:33 $
 */

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

int SignalsPossible = 0;

#ifdef __STDC__

static void	wake_sem( Event, VOID * );

#else

static void	wake_sem();

#endif


#define MAXARGS 6

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

extern int 	CurHost;

#ifdef MASTER_LOCK
extern Semaphore master_lock;
#endif

#define ARGSLISTSIZE 20

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

static struct arglist *
getMoreArgs(void)
{
  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 *);
bool CreateProcess(void *r, int pri, int nargs, ... );
void CreateKernelProcess(CreateProcFunc r, int prio, long arg1, long arg2,
                         long arg3);
void Delay(int n);


/*
 *  xkernel_activate_thread
 *
 *     startup a thread, given the arguments
 *
 */
static void xkernel_activate_thread(struct arglist *args, int priority)
{
  Thread child;
  struct ThreadOptions 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 = *threadDefaultOptions;
  options.name = "normal";
  options.arg = (void *)args;
  options.priority = priority;

  /* start the thread suspended so we can set its priority */
  child = threadCreate((ThreadFunc)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);

    threadWakeup(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, "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);
}


void
Yield()
{
  xk_master_unlock();
  xTrace0 (processcreation, TR_MAJOR_EVENTS, "Yield\n");
  threadYield();
  xk_master_lock();
}

/*
 *
 * 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;
  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];

  xTrace1(processcreation,TR_EVENTS,"enter_master_monitor: starting thread %x",
	  tSelf);

  xTrace1 (processcreation, TR_MAJOR_EVENTS, "enter_master_monitor: numargs: %d\n", nargs);
  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",
          tSelf);

}


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


void
Delay(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;
  queueInit(&s->waitingQ);
}

void
semWait(Semaphore *s)
{
  xTrace0 (processswitch, TR_DETAILED, "semWait\n");
  if (--(s)->count < 0)
    realP(s);
}

void
semSignal(Semaphore *s)
{
  xTrace0 (processswitch, TR_DETAILED, "semSignal\n");
  if (++(s)->count <= 0)
    realV(s);
}


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

void
mutexLock(Semaphore *s)
{
  if (--(s)->count < 0) {
    queueAppend(&s->waitingQ, tSelf);
    threadSuspend();
  }
}

void
mutexUnlock(Semaphore *s)
{
  Thread t;
  
  if (s->count++ < 0) {
    t = queueRemove(&s->waitingQ);
    threadWakeup(t);
  }
}

void
realP(s)
register Semaphore *s;
{
  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();
    queueAppend(&s->waitingQ, tSelf);
    threadSuspend();
    xk_master_lock();

#ifdef XK_THREAD_TRACE
    evMarkRunning();
#endif XK_THREAD_TRACE
  }
}

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

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

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

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