/*
 * polling process stuff that gets called from client run.
 *
 * written by Cyrus Patel <cyp@fb14.uni-mainz.de>
 *
*/

#include <string.h>   /* memset() */
#include "nwlemu.h"
#include "nwcus.h"    /* nwCliUtilSuppressorControl() */
#include "problem.h"
#include "cpucheck.h"

/* ===================================================================== */

struct callbacksched
{
  Problem *problem;
  int timeslice;
  int run;
  int cpu_i;
};

static struct
{
  int callbackschedinit;
  struct callbacksched cbschedtable[1];
} plrp;

/* ------------------------------------------------------------------- */

static void __OnIdleCruncher( void )
{
  static unsigned int index = 0;

  if (!GetNestedInterruptLevel() && !GetDiskIOsPending())
  {
    unsigned int i=index;
    while (!plrp.cbschedtable[i].problem)
    {
      ++i;
      if (i>=(sizeof(plrp.cbschedtable)/sizeof(plrp.cbschedtable[0])))
        i=0;
      if (i==index) //nothing to do
        return;
    }
    plrp.cbschedtable[i].problem->tslice = plrp.cbschedtable[i].timeslice;
    plrp.cbschedtable[i].run = 
      plrp.cbschedtable[i].problem->Run( /* plrp.cbschedtable[i].cpu_i */ );
    
    index = ++i;
    if (index>=(sizeof(plrp.cbschedtable)/sizeof(plrp.cbschedtable[0])))
      index = 0;
  }
  return;
}

/* ------------------------------------------------------------------- */

static int callbackschedinit = -1;

static struct callbacksched *CliAllocIdleCallback( Problem *problem, int timeslice, int cpu_i )
{
  static int callbackschedinproc = 1;
  struct callbacksched *cbs = (struct callbacksched *)0;
  unsigned long plrpResourceTag;

  if (!nwCliGetPollingAllowedFlag()) /* nwcmisc.cpp */
    return (struct callbacksched *)0;

  while ((++callbackschedinproc)>2)  //mutex or semaphore takes too long
  {                                  //should use a spinlock or atomic inc
    --callbackschedinproc;
    ThreadSwitchLowPriority(); 
  }

  if ((plrpResourceTag = nwCliGetPollingProcedureResourceTag()) != 0)
  {
    if (callbackschedinit < 0)
    {
      memset( (void *)&plrp, 0, sizeof(plrp));
      callbackschedinit = 0;
      if (AddPollingProcedureRTag( __OnIdleCruncher, plrpResourceTag) == 0)
        callbackschedinit = +1;
    }
    if (callbackschedinit > 0)
    {
      unsigned int i;
      for (i=0;i<(sizeof(plrp.cbschedtable)/sizeof(plrp.cbschedtable[0]));i++)
      {
        if (plrp.cbschedtable[i].problem == problem)
        {
          plrp.cbschedtable[i].timeslice = timeslice;
          plrp.cbschedtable[i].cpu_i = cpu_i;
          plrp.cbschedtable[i].run = RESULT_WORKING;
          cbs = &(plrp.cbschedtable[i]);
          break;
        }
      }
      if (!cbs)
      {
        for (i=0;i<(sizeof(plrp.cbschedtable)/sizeof(plrp.cbschedtable[0]));i++)
        {
          if (!plrp.cbschedtable[i].problem)
          {
            plrp.cbschedtable[i].problem = problem;
            plrp.cbschedtable[i].timeslice = timeslice;
            plrp.cbschedtable[i].cpu_i = cpu_i;
            plrp.cbschedtable[i].run = RESULT_WORKING;
            cbs = &(plrp.cbschedtable[i]);
            break;
          }
        }
      }
    }
  }
  
  callbackschedinproc--;
  return cbs;
}  

/* ------------------------------------------------------------------- */

static int CliFreeIdleCallback( Problem *problem, int timeslice, int cpu_i )
{
  problem = problem;
  timeslice = timeslice;
  cpu_i = cpu_i;
  return 0;
}    

/* ------------------------------------------------------------------- */

void nwCliUninitPollingProcedures(void) //this must be clib-call free
{
  if (callbackschedinit > 0)
  {
    unsigned int i;
    RemovePollingProcedure( __OnIdleCruncher );
    callbackschedinit = -1;
    for (i=0;i<(sizeof(plrp.cbschedtable)/sizeof(plrp.cbschedtable[0]));i++)
      plrp.cbschedtable[i].problem = (Problem *)0;
  }
  return;
}  

/* ------------------------------------------------------------------- */

void nwCliInitPollingProcedures(void)
{
  GetDiskIOsPending();          //all are self initializing
  GetNestedInterruptLevel(); 
  return;
}

/* ------------------------------------------------------------------- */

int nwCliRunProblemAsCallback( Problem *problem, int threadindex, int priority)
{
  static int hardslice, softslice, nice=-1;   //for speed
  static unsigned int freerun, ver;

  struct callbacksched *cbs;
  int run = RESULT_WORKING; 
  unsigned int perc;
  
  if (!problem)
    return -1;
  if (!problem->IsInitialized())
    return -1;
  perc = problem->CalcPermille();

  if (nice == -1)
  {
    if (priority > 2)
      priority = 2;
    nice = priority;
    freerun = 0;
    ver = GetFileServerMajorVersionNumber();
    if (GetNumberOfRegisteredProcessors()>1) 
      freerun = GetCurrentTicks()+(18*60); //run full speed for 1 min
    softslice = GetTimesliceBaseline();
    hardslice = softslice-((softslice/5)*(3-nice));
    softslice = ((softslice+54)/100) << (nice);
  }
  cbs = (struct callbacksched *)0;
  
  if ( !NWSMPIsAvailable() && freerun == 0 )
  {
    cbs = CliAllocIdleCallback(problem, softslice, threadindex);
    if (cbs)
    {
      while ((run=cbs->run) == RESULT_WORKING && !CheckExitRequestTriggerNoIO() 
            &&  problem->CalcPermille() == perc)
        delay(230);
      CliFreeIdleCallback( problem, softslice, threadindex );  
      nwCliUtilSuppressorControl(0); /* or at least try */
    }
  }
    
  if (!cbs)
  {
    problem->tslice = hardslice;
    while (run == RESULT_WORKING && !CheckExitRequestTriggerNoIO() && 
           problem->CalcPermille() == perc)
    {     
      run = problem->Run( /* threadindex */ ); 
      ThreadSwitchLowPriority(); //threadswitchlowpriority
    }
    if (freerun && GetCurrentTicks()>=freerun )
      freerun = 0;
  }
  return run;
}  

/* ===================================================================== */

