/*
   clock.c

   simple code to provide a simulated clock interrupt, and routines
   for manipulating them in Windows NT on x86.

   You should not need to modify this file.
*/

#define MINITHREAD_VERSION 2

#include <stdio.h>
#include <assert.h>

#include "clock.h"
#include "minithread.h"
#include "minithread_md.h"
#include "minithread_public.h"

/* get the correct version of windows nt recognised */
#define _WIN32_WINNT 0x0400
#include <windows.h>


typedef struct signal_queue_t signal_queue_t;
struct signal_queue_t {
  HANDLE threadid;
  CONTEXT *context;
  signal_queue_t *next;
};

static signal_queue_t *signalq = NULL;
static HANDLE signalq_mutex;

/* categories of interrupt types */
typedef enum { TIMER_INTERRUPT, NETWORK_INTERRUPT } interrupt_type_t;

/* Are interrupts enabled? The interrupt_handler function be called
   iff this is true. */

interrupt_level_t interrupt_level;

/* global handle to the user's interrupt service routine */
static ClockHandler interrupt_handler;

static HANDLE clock_thread;  /* NT thread to check for timer events */
static HANDLE system_thread; /* NT thread for running the minithreads */
static HANDLE return_thread; /* NT thread for restarting system thread */


interrupt_level_t set_interrupt_level(interrupt_level_t newlevel) {
  return swap(&interrupt_level, newlevel);
}

/* run the user's supplied interrupt handler and clean up the state after it:
   we wake up the return thread and then loop indefinitely; it overwrites our
   execution context to set us back to where we were before the interrupt
*/
void __fastcall run_user_handler(CONTEXT* context, interrupt_type_t type) {
  signal_queue_t* sq;

  if (DEBUG)
    printf("Running user interrupt handler ...\n");
  
  /* now, call the user clock interrupt handler */
  interrupt_handler();

  if (DEBUG)
    printf("Completed user interrupt handler.\n");
  
  sq = (signal_queue_t *) malloc(sizeof(signal_queue_t));
  sq->context = context;

  sq->threadid = system_thread;
  
  assert(WaitForSingleObject(signalq_mutex, INFINITE) == WAIT_OBJECT_0);
  sq->next = signalq;
  signalq = sq;
  ReleaseMutex(signalq_mutex);
  
  /* this is correct but not elegant, we need an atomic way to wake up the
     assist thread and go to sleep ourselves. One could use SignalAndWait
     here, but it does not work on CE */
  ResumeThread(return_thread);

  for(;;)
    ;

  /* NOT REACHED */
  abort();
}

/* this thread assists with the returns from simulated interrupts

   ideally, the interrupthandler would just assume the state right before the
   interrupt, but this is not possible given the x86 and nt, so the interrupt
   handling thread has to suspend itself, and let some other thread manipulate
   its state while it is suspended. 
*/
int WINAPI interrupt_return_assist(void *ptr) {
  signal_queue_t *sq;
  int scount, rcount;
  
  for(;;) {
    assert(WaitForSingleObject(signalq_mutex, INFINITE) == WAIT_OBJECT_0);
    sq = signalq;
    signalq = signalq->next;
    ReleaseMutex(signalq_mutex);
    
    scount = SuspendThread(sq->threadid);
    assert(SetThreadContext(sq->threadid, sq->context) != 0);

    rcount = ResumeThread(sq->threadid);
    assert(rcount == scount + 1);

    free(sq);
    SuspendThread(GetCurrentThread());
  }

  /* never reached */
  return 0;
}

/* this routine simulates a periodic clock tick.  every PERIOD
   microseconds it will be called. if interrupts are enabled, it will
   call your interrupt service routine.

   If interrupts are enabled, we stun the system thread to stop it executing;
   then we modify its execution context so that it will start executing by
   calling the user handler, and eventually return to whatever it was doing
   when the timer went off. Once these manipulations are complete, we
   "unstun" it so it can run again.
   Before doing the modifications, we check that the user thread is at a
   point where it can be safely preempted, otherwise we don't preempt.
*/
void clock_tick() {
  CONTEXT context;

  if (DEBUG)
    printf("Clock tick\n");

  /* need to protect this code: we only activate the interrupt if interrupts
     are actually enabled, but we also need to ensure they are not disabled
     after we test -- so we disable the system thread first. */
  SuspendThread(system_thread);
  if (interrupt_level == DISABLED) {
    ResumeThread(system_thread);
    return;
  }

  memset(&context, 0, sizeof(CONTEXT));
  context.ContextFlags = 
    CONTEXT_FULL | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS;
  assert(GetThreadContext(system_thread, &context) != 0);

  if ((context.Eip > (unsigned int) start) && 
      (context.Eip < (unsigned int) end)) {
    int stack;
    int* stack_ptr;

    /* check Eip == extended "instruction pointer" (program counter):
       we can only run if we are outside the OS-included libraries, our
       code is delimited by "start()" and "end()" in the makefile. */  
    if (DEBUG)
      printf("Interrupts are enabled, system thread pc = 0x%x\n", context.Eip);

    if (DEBUG)
      printf("Adjusting system thread context ...\n");

    stack = context.Esp; /* Esp == extended stack pointer */
    /* safe to do a printf because other thread is stunned in user code */

    if (DEBUG)
      printf("Suspended system thread, adjusting stack, sp = 0x%x\n", stack);

    stack -= (sizeof(CONTEXT) + 64); /* 64 is slop */
    stack_ptr = (int *) stack;
    memcpy(stack_ptr, &context, sizeof(CONTEXT));
    context.Esp = stack;
    context.Eip = (int) ((void *) run_user_handler);
    context.Edx = TIMER_INTERRUPT;
    context.Ecx = stack;
    
    SetThreadContext(system_thread, &context);
  }

  ResumeThread(system_thread);
}

/* procedure to poll the event queue for timer events, run by the clock
   thread; on a timer event, call "clock_tick()" to run the system thread's
   clock handler routine 
*/
int WINAPI clock_poll(void* arg) {
  LARGE_INTEGER i;
  HANDLE timer = CreateWaitableTimer(NULL, TRUE, "a timer");
  HANDLE thread = GetCurrentThread();

  assert(timer != NULL);

  for (;;) {
    i.QuadPart = -PERIOD*10;
    assert(SetWaitableTimer(timer, &i, 0, NULL, NULL, FALSE) != 0);
    if (WaitForSingleObject(timer, INFINITE) == WAIT_OBJECT_0)
      clock_tick();
  }

  /* never reached */
  return 0;
}

/* setup the interval timer and install user interrupt handler.  After this
   routine is called, and after you call set_interrupt_level(ENABLED), clock
   interrupts will begin to be sent.  They will call the handler
   function h specified by the caller.
*/
void minithread_clock_init(ClockHandler h)
{
  HANDLE process;
  int id;
  interrupt_handler = h;

  if (h == NULL) {
    printf("Must provide an interrupt handler, interrupts not started.\n");
    return;
  }

  printf("Starting clock interrupts ...\n");

  /* set up the signal queue and semaphores */
  signalq_mutex = CreateMutex(NULL, FALSE, "interrupt return mutex");
  interrupt_level = DISABLED;

  /* overcome an NT "feature" -- GetCurrentThread() returns a "pseudohandle" to
     the executing thread, not the real handle, so we need to use 
     DuplicateHandle() to get the real handle.
  */
  process = GetCurrentProcess();
  assert(DuplicateHandle(process, GetCurrentThread(), process, 
			 &system_thread, THREAD_ALL_ACCESS, FALSE, 0) == TRUE);

  /* create clock and return threads, but discard ids */
  clock_thread = CreateThread(NULL, 0, clock_poll, NULL, 0, &id); 
  assert(clock_thread != NULL);
  return_thread = CreateThread(NULL, 0, interrupt_return_assist, NULL, 
			       CREATE_SUSPENDED, &id);
  assert(return_thread != NULL);
}
