/*
 * Copyright 2010  OpenSourceCodeStewardshipFoundation
 *
 * Licensed under BSD
 */



#include <stdio.h>
#include <malloc.h>

#include "VMS.h"



/*This code is animated by the virtual Master processor.
 *Note, it is animated on a different level in virtual processor hierarchy
 * than the CoreLoop -- this is the code pointed to in a work-unit that the
 * coreLoop jumps to
 *
 *Polls each virtual slave exactly once, hands any requests made by the slave
 * to the "request handler" plug-in function
 *
 *Any slaves that have no work-unit assigned are given to the "schedule"
 * plug-in function, which tries to assign a work-unit to it.
 *
 *When all slaves that need work-units have been given to the schedule plug-in,
 * half of the ones that were successfully scheduled are put into the work
 * queue, then a continuation of this function is put in, then the rest of the
 * slaves that were successfully scheduled.
 *
 *The first thing this function does is busy-wait until the previous work-unit
 * running this function is done.  This ensures it doesn't overlap with
 * tail-end of previous -- IE, continuation may sneak through queue before
 * previous done putting second half of scheduled slaves in.  This is the only
 * race condition.
 *
 */

void masterLoop( void *data )
 { bool8 success;
   int slaveIdx, numScheduled, numInFirstHalf, schedSlaveIdx;
   VMSProcr        currSlave, *virtSlaves;
   MasterEnv      *masterEnv;
   SlaveScheduler  slaveScheduler;
   RequestHandler  requestHandler;


   masterEnv = (MasterEnv *)data;

   requestHandler   = masterEnv->requestHandler;
   slaveScheduler   = masterEnv->slaveScheduler;
   virtSlaves       = masterEnv->virtSlaves;
   
      //if another continuation of Master still running, busy-wait
   while( masterEnv->stillRunning ) /*busy wait*/ ;

      //this is the only master running now, set flag again
   masterEnv->stillRunning = 1;

      //prepare for scheduling
   masterEnv->numScheduled = 0;

      //Poll each slave structure's Done flag
   for( slaveIdx = 0; slaveIdx < NUM_SLAVES; slaveIdx++)
    {
      currSlave = virtSlaves[ slaveIdx ];

      if( currSlave->workIsDone )
       {
         currSlave->workIsDone        = FALSE;
         currSlave->needsWorkAssigned = TRUE;

            //process requests from slave to master
         (*requestHandler)( currSlave );
       }
      if( currSlave->needsWorkAssigned )
       {    //give slave a new work-unit
         success =
         (*slaveScheduler)( currSlave, masterEnv );
         
         if( success )
          { addToVect( currSlave, &(masterEnv->scheduledSlaves),
                                  &(masterEnv->numScheduled) );
            currSlave->needsWorkAssigned = FALSE;
          }
       }
    }

      //put half scheduled slaves in, then continuation, then other half
   VMSProcr **scheduledSlaves;
   numInFirstHalf = masterEnv->numScheduled / 2;
   scheduledSlaves = masterEnv->scheduledSlaves;
   for( schedSlaveIdx = 0; schedSlaveIdx < numInFirstHalf; schedSlaveIdx++)
    {
      writeQ( scheduledSlaves[ schedSlaveIdx ], workQ );
    }

      //enqueue continuation of this loop
      // note that After this enqueue, continuation might sneak through
   writeQ( masterEnv->masterWorkUnit, workQ );
   for( schedSlaveIdx = numInFirstHalf; 
        schedSlaveIdx < numScheduled;
        schedSlaveIdx++)
    {
      writeQ( scheduledSlaves[ schedSlaveIdx ]->workUnitToDo, workQ );
    }

      //all done, so okay for continuation to proceed
   masterEnv->stillRunning = 0;
 }


