/*
 * $RCSfile: event.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Log: event.c,v $
 * Revision 1.3  1997/01/28 22:47:04  rrp
 * pulled over from OSF/1 to support scout threads
 *
 * Revision 1.1  1996/01/30  22:41:23  slm
 * Initial revision
 *
 * Revision 1.34.1.2  1994/12/02  23:23:56  hkaram
 * Changed to new mapResolve interface
 *
 * Revision 1.34.1.1  1994/10/27  22:27:20  hkaram
 * New branch
 *
 * Revision 1.34  1994/02/05  00:06:47  menze
 *   [ 1994/01/28          menze ]
 *   assert.h renamed to xk_assert.h
 */

#include "xk_debug.h"
#include "event.h"
#include "event_i.h"
#include "xk_assert.h"
#include "x_libc.h"
#include "machine.h"
#include <sys/time.h>

#define THIS_IS_THE_HEADER ((EvFunc)-42)


/* BIG_N should be a function of the estimated number of scheduled events,
 * such that queues don't get too long, but with as little overhead as
 * possible. 128 is probably good for up to some 500-1000 events. [mats]
 */

#define BIG_N 128 

#ifdef __STDC__

#ifdef ALPHA
#include <search.h>
#elif defined X86
#include <stdlib.h>
#endif

extern void     enable_alarm (int);
extern bool	CreateProcess(void *, int, int , ...);

#endif /* __STDC__ */
void evMarkBlocked( Semaphore * );
void evMarkRunning(void);


static struct Event 	evHead[BIG_N];
static int 		tickmod;


#ifdef XK_THREAD_TRACE

Map		localEventMap;
static Map	threadEventMap;

#endif

#if 1
#ifdef XK_THREAD_TRACE
#undef XK_THREAD_TRACE
#endif
#endif

void
evInit(void)
{
    int i;

#ifdef XK_THREAD_TRACE
    localEventMap = mapCreate(BIG_N, sizeof(Event));
    threadEventMap = mapCreate(BIG_N, sizeof(Thread));
#endif
    for(i=0; i<BIG_N; i++)
      {
	  evHead[i].next = &evHead[i];
	  evHead[i].prev = &evHead[i];
	  evHead[i].func = THIS_IS_THE_HEADER;
      }
    tickmod = 0;
}


static void
e_insque(int q, Event e)
{
  Event a;

  for (a = evHead[q].next; a != &evHead[q]; a = a->next) {
    if (a->deltat < e->deltat) {
      /* E goes after a */
      e->deltat -= a->deltat;
      continue;
    } else {
      /* E goes just before a */
      a->deltat -= e->deltat;
      insque((struct qelem *)e, (struct qelem *)a->prev);
      return;
    }
  }
  /* E goes at the end */
  insque((struct qelem *)e, (struct qelem *)evHead[q].prev);
}


static void
e_remque(Event e)
{
  if (e->next->func != THIS_IS_THE_HEADER) {
    e->next->deltat += e->deltat;
  }
  remque((struct qelem *)e);
}


/* 
 * It is safe to call unbindEvent multiple times with the same event.
 * Only the first call will have any effect.
 */
static inline void
unbindEvent( Event e )
{
#ifdef XK_THREAD_TRACE
    xAssert(e);
    if ( e->bind ) {
	mapRemoveBinding(threadEventMap, e->bind);
	e->bind = 0;
    }
#endif
}


static inline void
freeEvent( Event e )
{
#ifdef XK_THREAD_TRACE
    XkReturn	xkr;
#endif

    xAssert(e);
#ifdef XK_THREAD_TRACE
    unbindEvent(e);
    xkr = mapRemoveKey(localEventMap, &e);
    xAssert( xkr == XK_SUCCESS );
#endif
    xFree((char *)e);
}


void
evMarkBlocked( Semaphore *s )
{
#ifdef XK_THREAD_TRACE
    Event	evSelf;

    if (mapResolve(threadEventMap, &scout_thread_self, (void **)&evSelf) ==
	XK_SUCCESS) {
	evSelf->state = E_BLOCKED;
	xGetTime(&evSelf->stopTime);
    }
    else
	xTrace0(event, TR_ERRORS, "realP -- Couldn't find event structure");
#endif
}


void
evMarkRunning(void)
{
#ifdef XK_THREAD_TRACE
    Event	evSelf;

    if (mapResolve(threadEventMap, &scout_thread_self, (void **)&evSelf) ==
	XK_SUCCESS)
	evSelf->state = E_RUNNING;
    else
	xTrace0(event, TR_ERRORS, "realP -- Couldn't find event structure");
#endif
}


static void
stub( void *arg )
{
    Event	e = arg;

    if ( e->flags & E_CANCELLED_F ) {
	xTrace1(event, TR_MORE_EVENTS,
		"event stub not starting cancelled event %x", e);
	freeEvent(e);
	return;
    }
#ifdef XK_THREAD_TRACE
    {
	e->bind = mapBind(threadEventMap, scout_thread_self, e);
	xAssert( e->bind != ERR_BIND );
	xGetTime(&e->startTime);
    }
#endif
    e->state = E_RUNNING;
    e->func(e, e->arg);
    e->state = E_FINISHED;
    if ( e->flags & E_DETACHED_F ) {
	freeEvent(e);
    } else {
	unbindEvent(e);
#ifdef XK_THREAD_TRACE
	xGetTime(&e->stopTime);
#endif
    }
}
      

Event
evSchedule( EvFunc func, void *arg, unsigned tme)  /* time in usec */
{
  Event e;
  u_int delt;

  enable_alarm (0);
  xTrace3(event, TR_GROSS_EVENTS, "evSchedule: f = %x, arg = %x, tme = %d",
	  func, arg, tme);
  e = (Event)xMalloc(sizeof(struct Event));
  e->func = func;
  e->arg = arg;

#ifdef XK_THREAD_TRACE
  {
      XTime	offset = { tme / 1E6, tme % (unsigned)1E6 };
      Binding	b;
      
      b = mapBind(localEventMap, &e, e);
      xAssert( b != ERR_BIND );
      e->bind = 0;   /* filled in in 'stub' once a native thread is assigned */
      xGetTime(&e->startTime);
      xAddTime(&e->startTime, e->startTime, offset);
  }
#endif
  
  /*
   * EVENT_INTERVAL, time between scans of the event list, is also in
   * usec.
   */
  delt = (tme + EVENT_INTERVAL / 2) / EVENT_INTERVAL;
  if (delt == 0 && tme != 0) {
      delt = 1;
  }
  xTrace1(event, TR_EVENTS, "event requires %d clock ticks", delt);
  e->deltat = delt / BIG_N + 1;
  e->flags = 0;
  if (delt == 0) {
    e->state = E_SCHEDULED;
    /* createprocess(stub, e); */
    CreateProcess(stub, STD_PRIO, 1, e);
  } else {
    xTrace1(event, TR_FUNCTIONAL_TRACE, "Placing event in queue %d", (tickmod+delt)%BIG_N);
    e_insque((tickmod + delt) % BIG_N, e);
    e->state = E_PENDING;
  }
  xTrace1(event, TR_MAJOR_EVENTS, "evSchedule returns event %x", e);
  enable_alarm (1);
  return e;
}


void
evDetach( Event e )
{
    enable_alarm (0);
    xTrace1(event, TR_GROSS_EVENTS, "evDetach: event = %x", e);
    if ( e->state == E_FINISHED ) {
	freeEvent(e);
    } else {
	e->flags |= E_DETACHED_F;
    }
    enable_alarm (1);
}


/* 
 * See description in event.h
 */
EvCancelReturn
evCancel(Event e)
{
  int ans;

  enable_alarm (0);
  xTrace1(event, TR_GROSS_EVENTS, "evCancel: event = %x", e);
  switch ( e->state ) {

    case E_SCHEDULED:
      e->flags |= ( E_DETACHED_F | E_CANCELLED_F );
      /* 
       * The stub routine will catch this event before it gets a chance to
       * run
       */
      ans = EVENT_CANCELLED;
      break;

    case E_BLOCKED:
    case E_RUNNING:
      e->flags |= ( E_DETACHED_F | E_CANCELLED_F );
      ans = EVENT_RUNNING;
      break;

    case E_FINISHED:
      ans = EVENT_FINISHED;
      freeEvent(e);
      break;

    case E_PENDING:
      e_remque(e);
      ans = EVENT_CANCELLED;
      freeEvent(e);
      break;
  }

  enable_alarm (1);
  return ans;
}
  

int
evIsCancelled( e )
    Event	e;
{
    xAssert(e);
    return e->flags & E_CANCELLED_F;
}

extern void set_clock (void);

void
clock_ih()
{
    Event e;
    Event head;
    
    /* Need to protect any routines which manipulate the event queue, and
       the thread routines. */

    xTrace0(event, TR_GROSS_EVENTS, "event clock interrupt");
    head = &evHead[tickmod];
    e = head->next;

    if (e != head) {
	e->deltat--;
	xTrace2(event, TR_EVENTS, "lead event in queue %d has %d interrupts to go",
		tickmod, e->deltat);
	xIfTrace(event, TR_FUNCTIONAL_TRACE) {
	    int i;
	    for (i=0; e != head; e = e->next) {
		i++;
	    }
	    e = head->next;
	    xTrace2(event, TR_FUNCTIONAL_TRACE, "%d events in queue %d", i, tickmod);
	}
	for (; e != head && e->deltat == 0; e = e->next) {
	    /*
	     * Fire this event and remove it from the queue. No adjusting
	     * of the following deltat is required.
	     */
	    xTrace1(event, TR_GROSS_EVENTS, "Event %x expired", e->func);
            remque((struct qelem *)e);
	    e->state = E_SCHEDULED;
#ifdef XK_THREAD_TRACE
	    xGetTime(&e->startTime);
#endif
	    /* createprocess(stub, e); */
	    CreateProcess(stub, STD_PRIO, 1, e);
	}
    } else {
	xTrace1(event, TR_FUNCTIONAL_TRACE, "0 events in queue %d", tickmod);
    }
    tickmod = (tickmod + 1 ) % BIG_N;
    xTrace0(event, TR_GROSS_EVENTS, "end event clock interrupt");
}


void
evCheckStack(name)
    char	*name;
{
}
