/*
 * Copyright 2010  OpenSourceCodeStewardshipFoundation
 *
 * Licensed under BSD
 */

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

#include "VMS.h"
#include "Queue_impl/BlockingQueue.h"


/*Setup has two phases:
 * 1) Semantic layer first calls init_VMS, which creates masterEnv, and puts
 *    the master work-unit into the work-queue
 * 2) Semantic layer then does its own init, which creates the initial
 *    work-units inside the semantic layer, ready to schedule them when
 *    asked by the first run of the masterLoop.
 *
 *This part is bit weird because VMS really wants to be "always there", and
 * have applications attach and detach..  for now, this VMS is part of
 * the app, so the VMS system starts up as part of running the app.
 *
 *The semantic layer is fully isolated from the VMS internasl by
 * making the semantic layer setup into a state that it's ready with its
 * initial work-units, ready to schedule them to slaves when the masterLoop
 * asks.  Without this pattern, the semantic layer's setup would
 * have to modify slaves directly to assign the initial work-units, and put
 * them into the workQ itself, breaking the isolation completely.
 *
 * 
 *The semantic layer creates the initial work-unit(s), and adds its
 * own environment data to masterEnv, and fills in the pointers to
 * the requestHandler and slaveScheduler plug-in functions
 *
 *This allocates VMS data structures, populates the master VMSProc,
 * and master environment, and returns the master environment to the semantic
 * layer.
 */
   //Global vars are all inside VMS.h
MasterEnv *
init_VMS(  )
 {
      //Make the central work-queue
   workQ = makeQ();

   masterEnv = malloc( sizeof(MasterEnv) );

   create_master( masterEnv );

   create_slaves( masterEnv );

      //When coreLoops start up, the first thing 
   writeQ( masterEnv->masterWorkUnit, workQ );
 }



/*Fill up the virtual master data structure, which is already alloc'd in the
 * masterEnv.
 *The virtual Master is the same structure as a virtual slave, but it
 * isn't in the array of virtual slaves.
 * The reason it's the same structure is so that the coreLoop doesn't
 *  have to differentiate -- all work units are assigned to a VMSProcr, and
 *  the core loop treats them all the same way, whether it's the virtual
 *  master continuation or a slave's work-unit.
 *Note: masterLoop is jumped into an back out of, so have to be careful with
 * register usage and saving all persistent-across-calls state to masterEnv
 */
void
create_master( MasterEnv *masterEnv )
 { VMSProcr virtMaster;

   virtMaster = &(masterEnv->virtMaster);
   virtMaster->workUnitToDo                  = malloc( sizeof( WorkUnit ) );
   virtMaster->workUnitToDo->workData        = masterEnv;
      //TODO: figure out call structure: what GCC will do with regs
      // will jump to the masterLoop from the coreLoop -- what regs need
      // saving, from before jump to after -- and what reg to put masterEnv
      // pointer in when jump to masterLoop
   virtMaster->workUnitToDo->addrToJumpTo    = &masterLoop;
   virtMaster->workUnitToDo->slaveAssignedTo = virtMaster;
 }

void
create_slaves( MasterEnv *masterEnv )
 { VMSProcr *virtSlaves;
   int i;

   virtSlaves = masterEnv->virtSlaves;  //TODO: make sure this is right
   for( i = 0; i < NUM_SLAVES; i++ )
    {
         //Set state to mean "everything done, schedule work to slave"
      virtSlaves[i].workIsDone        = FALSE;
      virtSlaves[i].needsWorkAssigned = TRUE;
    }
 }

/*Semantic layer calls this when it want the system to start running..
 *
 *This creates the core loops, pins them to physical cores, gives them the
 * pointer to the workQ, and starts them running.
 */
 void
VMS__start()
 { int retCode, coreIdx;

//TODO: still just skeleton code -- figure out right way to do this

      //Create the PThread loops that take from work-queue, and start them
   for( coreIdx=0; coreIdx < NUM_WORKERS; coreIdx++ )
    {
      thdParams[coreIdx]        = (ThdParams *)malloc( sizeof(ThdParams) );
      thdParams[coreIdx]->workQ = workQ;
      thdParams[coreIdx]->id    = coreIdx;

         //Now make and start thd..  the coreLoopThds entry
         // has all the info needed to later stop the thread.
      retCode =
       pthread_create( &(coreLoopThds[coreIdx]), thdAttrs, &coreLoop,
                       (void *)(thdParams[coreIdx]) );
      if( retCode != 0 )
       { //error
         printf("ERROR creating coreLoop %d, code: %d\n", coreIdx, retCode);
         exit(-1);
       }

      pinThdToCore( );  //figure out how to specify this..

      startThd(); //look up PThread call to start the thread running, if it's
                  // not automatic
    }
 }

 /*there is a label inside this function -- save the addr of this label in
 * the callingPr struc, as the pick-up point from which to start the next
 * work-unit for that procr.  If turns out have to save registers, then
 * save them in the procr struc too.  Then do assembly jump to the CoreLoop's
 * "done with work-unit" label.  The procr struc is in the request in the
 * slave that animated the just-ended work-unit, so all the state is saved
 * there, and will get passed along, inside the request handler, to the
 * next work-unit for that procr.
 */
VMS__save_ret_and_jump_to_CoreLoop( callingPr )
 {
   //TODO: figure out how to save the addr of a label into a mem loc
   //NOTE: because resume pt is inside the VMS fn, it's always the same, no
   // matter what the semantic layer is, no matter what semantic libr called.
   callingPr->resumePt = &resumeNextWorkUnitPt;
   save_processor_state_in( callingPr ); //save x86 regs, if GCC needs it to
   coreLoopRetPt = callingPr->coreLoopRetPt;
   //TODO: figure out how to do jump correctly -- target addr is constant
   asm( jmp coreLoopRetPt );

resumeNextWorkUnitPt:
   return;
 }


/*The semantic virt procr is available in the request sent from the slave
 * 
 * The request handler has to add the work-unit created to the semantic
 * virtual processor the work-unit is a section of its time-line -- does this when create the
 * work-unit -- means the procr data struc is available in the request sent
 * from the slave, from which the new work-unit is generated..
 */
VMS__add_request_to_slave( SlaveReqst req, VMSProcr callingPr )
 { VMSProcr slave;
   slave = callingPr->workUnit->currSlave
 req->nextRequest =  callingPr->workUnit->currSlave->requests = req;
 }



