/*
 *  Copyright 2009 OpenSourceStewardshipFoundation.org
 *  Licensed under GNU General Public License version 2
 *
 * Author: seanhalle@yahoo.com
 * 
 */

#ifndef _PR_H
#define	_PR_H
#define _GNU_SOURCE

#include "DynArray/DynArray.h"
#include "Hash_impl/PrivateHash.h"
#include "Histogram/Histogram.h"
#include "Queue_impl/PrivateQueue.h"

#include "PR_primitive_data_types.h"
#include "Services_Offered_by_PR/Memory_Handling/vmalloc.h"

#include <pthread.h>
#include <sys/time.h>

//=================  Defines: included from separate files  =================
//
// Note: ALL defines are in other files, none are in here
//
#include "Defines/PR_defs.h"


//================================ Typedefs =================================
//
typedef unsigned long long    TSCount;

typedef struct _AnimSlot      AnimSlot;
typedef struct _PRReqst       PRReqst;
typedef struct _SlaveVP       SlaveVP;
typedef struct _MasterVP      MasterVP;
typedef struct _IntervalProbe IntervalProbe;
typedef struct _PRMetaTask    PRMetaTask;


typedef SlaveVP *(*SlaveAssigner)  ( void *, AnimSlot*); //semEnv, slot for HW info
typedef void     (*RequestHandler) ( SlaveVP *, void * ); //prWReqst, semEnv
typedef void     (*IndivReqHandler)( SlaveVP *, void * ); //prWReqst, semEnv
typedef void     (*TopLevelFnPtr)  ( void *, SlaveVP * ); //initData, animSlv
typedef void       TopLevelFn      ( void *, SlaveVP * ); //initData, animSlv
typedef void     (*ResumeSlvFnPtr) ( SlaveVP *, void * );
      //=========== MEASUREMENT STUFF ==========
        MEAS__Insert_Counter_Handler
      //========================================

//============================ HW Dependent Fns ================================

#include "HW_Dependent_Primitives/PR__HW_measurement.h"
#include "HW_Dependent_Primitives/PR__primitives.h"


//============= Request Related ===========
//

enum PRReqstType  //avoid starting enums at 0, for debug reasons
 {
   TaskCreate = 1,
   TaskEnd,
   SlvCreate,
   SlvDissipate,
   Language,
   Service,       //To invoke a PR provided equivalent of a language request (ex: probe)
   Hardware,
   IO,
   OSCall
 };

struct _PRReqst
 {
   enum PRReqstType   reqType;//used for special forms that have PR behavior
   void              *semReq;
   PRProcess         *processReqIsIn;
   int32              langMagicNumber;
   TopLevelFn         topLevelFn;
   void              *initData;
   int32             *ID;
   
      //The request handling structure is a bit messy..  for special forms, 
      // such as create and dissipate, the language inserts pointer to handler
      // fn directly into the request..  might change to this for all requests
   IndivReqHandler    handler; //pointer to handler fn for create, dissip, etc
   
   PRReqst *nextReqst;
 };
//PRReqst

enum PRServReqType   //These are equivalent to semantic requests, but for
 {                    // PR's services available directly to app, like OS
   make_probe = 1,    // and probe services -- like a PR-wide built-in lang
   throw_excp,
   openFile,
   otherIO
 };

typedef struct
 { enum PRServReqType   reqType;
   SlaveVP             *requestingSlv;
   char                *nameStr;  //for create probe
   char                *msgStr;   //for exception
   void                *exceptionData;
 }
PRServReq;


//====================  Core data structures  ===================

typedef struct
 {
   //for future expansion
 }
SlotPerfInfo;

struct _AnimSlot
 {
   int           workIsDone;
   int           needsWorkAssigned;
   SlaveVP      *slaveAssignedToSlot;
   
   int           slotIdx;  //needed by Holistic Model's data gathering
   int           coreSlotIsOn;
   SlotPerfInfo *perfInfo; //used by assigner to pick best slave for core
 };
//AnimSlot

enum VPtype 
 { SlotTaskSlv = 1,//Slave tied to an anim slot, only animates tasks
   FreeTaskSlv,   //When a suspended task ends, the slave becomes this
   GenericSlv,     //the VP is explicitly seen in the app code, or task suspends
   Master,
   Shutdown,
   Idle
 };
 
/*This structure embodies the state of a slaveVP.  It is reused for masterVP
 * and shutdownVPs.
 */
struct _SlaveVP
 {    //The offsets of these fields are hard-coded into assembly
   void       *stackPtr;         //save the core's stack ptr when suspend
   void       *framePtr;         //save core's frame ptr when suspend
   void       *resumeInstrPtr;   //save core's program-counter when suspend
   void       *coreCtlrFramePtr; //restore before jmp back to core controller
   void       *coreCtlrStackPtr; //restore before jmp back to core controller
   
      //============ below this, no fields are used in asm =============
   
   void       *startOfStack;  //used to free, and to point slave to Fn
   PRProcess  *processSlaveIsIn;
   PRMetaTask *metaTask;
   enum VPtype typeOfVP;      //Slave vs Master vs Shutdown..
   int         slaveID;       //each slave given a globally unique ID
   int         coreAnimatedBy; 
   int         numTimesAssignedToASlot;   //Each assign is for one work-unit, so is an ID
      //note, a scheduling decision is uniquely identified by the triple:
      // <slaveID, coreAnimatedBy, numTimesAssignedToASlot> -- used in record & replay
   
      //for comm -- between master and coreCtlr & btwn wrapper lib and plugin
   AnimSlot   *animSlotAssignedTo;
   PRReqst    *request;      //wrapper lib puts in requests, plugin takes out
   void       *dataRetFromReq;//Return vals from plugin to Wrapper Lib

      //For language specific data that needs to be in the slave
   void       *semanticData;  //Lang saves lang-specific things in slave here

      //Task related stuff
//   bool        needsTaskAssigned;
   
        //=========== MEASUREMENT STUFF ==========
         MEAS__Insert_Meas_Fields_into_Slave;
         float64     createPtInSecs;  //time VP created, in seconds
        //========================================
 };
//SlaveVP

 
/* The one and only global variable, holds many odds and ends
 */
typedef struct
 {    //The offsets of these fields are hard-coded into assembly
   void            *coreCtlrReturnPt;    //offset to this field used in asm
   int8             falseSharePad1[256 - sizeof(void*)];
   int32            masterLock;          //offset to this field used in asm
   int8             falseSharePad2[256 - sizeof(int32)];
      //============ below this, no fields are used in asm =============

      //Basic PR infrastructure
   SlaveVP        **masterVPs;
   AnimSlot      ***allAnimSlots;
 
   PRProcess      **processes;
   
//move to processEnv      //Slave creation -- global count of slaves existing, across langs and processes
   int32            numSlavesCreated;  //used to give unique ID to processor
   int32            numTasksCreated;   //to give unique ID to a task

      //Initialization related
   int32            setupComplete;      //use while starting up coreCtlr

      //Memory management related
   MallocArrays    *freeLists;
   int32            amtOfOutstandingMem;//total currently allocated

      //Random number seeds -- random nums used in various places  
   uint32_t seed1;
   uint32_t seed2;

   These_Prob_belong_in_PRPRocess;
//   SlaveVP         *slotTaskSlvs[NUM_CORES][NUM_ANIM_SLOTS];
//   int32            numLiveFreeTaskSlvs;
//   int32            numLiveThreadSlvs;
//   bool32          *coreIsDone;
//   int32            numCoresDone;
   
//   SlaveVP* idleSlv[NUM_CORES][NUM_ANIM_SLOTS];
//   int shutdownInitiated;
   
      //=========== MEASUREMENT STUFF =============
       IntervalProbe   **intervalProbes;
       PrivDynArrayInfo *dynIntervalProbesInfo;
       HashTable        *probeNameHashTbl;
       int32             masterCreateProbeID;
       float64           createPtInSecs; //real-clock time PR initialized
       Histogram       **measHists;
       PrivDynArrayInfo *measHistsInfo;
       MEAS__Insert_Susp_Meas_Fields_into_MasterEnv;
       MEAS__Insert_Master_Meas_Fields_into_MasterEnv;
       MEAS__Insert_Master_Lock_Meas_Fields_into_MasterEnv;
       MEAS__Insert_Malloc_Meas_Fields_into_MasterEnv;
       MEAS__Insert_Plugin_Meas_Fields_into_MasterEnv;
       MEAS__Insert_System_Meas_Fields_into_MasterEnv;
       MEAS__Insert_Counter_Meas_Fields_into_MasterEnv;
      //==========================================
 }
MasterEnv;

//=====================
typedef struct
 { int32     langMagicNumber; //indexes into hash array of semEnvs in PRProcess
   PRSemEnv *chainedSemEnv;   //chains to semEnvs with same hash
   void     *langSemEnv;
   
   SlaveAssigner   slaveAssigner;
   RequestHandler  requestHdlr;
   
   RequestHandler  createTaskHdlr;
   RequestHandler  endTaskHdlr;
   RequestHandler  createSlaveHdlr;
   RequestHandler  dissipateSlaveHdlr;
   RequestHandler  semDataCreator;
   RequestHandler  semDataInitializer;
  
   
      //Track slaves created, separately for each langlet? (in each process)
//   int32            numSlavesCreated;  //gives ordering to processor creation
//   int32            numSlavesAlive;    //used to detect fail-safe shutdown
   
      //when multi-lang, master polls sem env's to find one with work in it..
      // in single-lang case, flag ignored, master always asks lang for work
   int32   hasWork;
 }
PRSemEnv;

//The semantic env of every langlet must start with these two fields, so that
// PR can cast the void * to this struct, in order to access these two fields
typedef struct
 { int32     langMagicNumber;
   PRSemEnv *protoSemEnv;
 }
PRLangSemEnv;

//can cast any langlet's sem env to one of these, so PR can access values
typedef struct
 { int32     langMagicNumber;
   PRSemEnv *protoSemEnv;
 }
PRServSemEnv;

enum PRTaskType
 { GenericSlave = 1,
   SlotTask,
   FreeTask
 };

struct _PRMetaTask
 { 
   PRTaskType      taskType;
//   RequestHandler  reqHandler;      //Lang-specific hdlr for create, end, etc
   int32          *taskID;          //is standard PR ID
   SlaveVP        *slaveAssignedTo; //no valid until task animated
   TopLevelFn      topLevelFn;      //This is the Fn executes as the task
   void           *initData;        //The data taken by the function
   void           *langMetaTask;

   //NOTE: info needed for "wait" functionality is inside lang's metaTask
 };
//PRMetaTask

/*The language's meta task is cast to this struct, inside PR, then the 
 * back pointer to protoMetaTask is set.  Keeps existence of PRMetaTask hidden
 * from plugin -- so can change later.
 */
typedef struct
 { int32       langMagicNumber;
   PRMetaTask *protoMetaTask;
 }
PRLangMetaTask;
 
typedef struct
 {
   void (*freeFn)(void *);
 }
PRSemDataTemplate;
 
typedef struct
 {
   void (*recycler)(void *);
   void *langSemData;
 }
PRSemData;

typedef struct
 { PRSemDataTemplate **semDatas;
   PRSemDataTemplate **semDatasIter;
   int32               numSemDatas;
 }
PRSemDataHolder;
//=====================  Top Process level Data Strucs  ======================

/*This structure holds all the information PR needs to manage a program.  PR
 * stores information about what percent of CPU time the program is getting, 
 * 
 */
typedef struct
 { 
   PRSemEnv semEnvs[NUM_SEM_ENVS_IN_PROCESS];    //used as a hash table
   PRSemEnv semEnvList[NUM_SEM_ENVS_IN_PROCESS]; //lines up the semEnvs, so can iterate through
   int32    numSemEnvs;     //must be less than num sem envs.. used to iterate through
    
   int32           numLiveGenericSlvs;
   int32           numLiveFreeTaskSlvs;
   int32           numLiveTasks;
//   bool32          coreIsDone[NUM_CORES][CACHE_LINE_SZ]; //Fixes false sharing

   PrivQueueStruc *freeTaskSlvRecycleQ;
   SlaveVP         slotTaskSlvs[NUM_CORES][NUM_ANIM_SLOTS];
   void           *resultToReturn;
  
   SlaveVP        *seedSlv;   
   
   SlaveAssigner   overrideAssigner;
   
      //These are used to coord with OS thread waiting for process to end
   bool32          executionIsComplete;
   pthread_mutex_t doneLock;
   pthread_cond_t  doneCond;
 }
PRProcess;


//=========================  Extra Stuff Data Strucs  =======================
typedef struct
 {

 }
PRExcp; //exception

//=======================  OS Thread related  ===============================

void * coreController( void *paramsIn );  //standard PThreads fn prototype
void * coreCtlr_Seq( void *paramsIn );  //standard PThreads fn prototype
void animationMaster( void *initData, SlaveVP *masterVP );


typedef struct
 {
   void           *endThdPt;
   unsigned int    coreNum;
 }
ThdParams;

//=============================  Global Vars ================================

volatile MasterEnv      *_PRTopEnv __align_to_cacheline__;

   //these are global, but only used for startup and shutdown
pthread_t       coreCtlrThdHandles[ NUM_CORES ]; //pthread's virt-procr state
ThdParams      *coreCtlrThdParams [ NUM_CORES ];

pthread_mutex_t suspendLock;
pthread_cond_t  suspendCond;

//=========================  Function Prototypes  ===========================
/* MEANING OF   WL  PI  SS  int PROS
 * These indicate which places the function is safe to use.  They stand for:
 * 
 * WL   Wrapper Library -- wrapper lib code should only use these
 * PI   Plugin          -- plugin code should only use these
 * SS   Startup and Shutdown -- designates these relate to startup & shutdown
 * int  internal to PR -- should not be used in wrapper lib or plugin
 * PROS means "OS functions for applications to use"
 * 
 * PR_int__ functions touch internal PR data structs and are only safe
 *  to be used inside the master lock.  However, occasionally, they appear
 * in wrapper-lib or plugin code.  In those cases, very careful analysis
 * has been done to be sure no concurrency issues could arise.
 * 
 * PR_WL__ functions are all safe for use outside the master lock.
 * 
 * PROS are only safe for applications to use -- they're like a second
 * language mixed in -- but they can't be used inside plugin code, and
 * aren't meant for use in wrapper libraries, because they are themselves
 * wrapper-library calls!
 */
//========== Startup and shutdown ==========
void
PR__start();

SlaveVP* 
PR_SS__create_shutdown_slave();

void
PR_SS__shutdown();

void
PR_SS__cleanup_at_end_of_shutdown();

void
PR_SS__register_langlets_semEnv( PRSemEnv *semEnv, SlaveVP  *seedVP, int32 VSs_MAGIC_NUMBER );


//==============    ===============

inline SlaveVP *
PR_int__create_slaveVP( TopLevelFnPtr fnPtr, void *dataParam );
#define PR_PI__create_slaveVP PR_int__create_slaveVP
#define PR_WL__create_slaveVP PR_int__create_slaveVP

inline 
SlaveVP *
PR_int__create_slot_slave();

inline 
SlaveVP *
PR_int__create_slaveVP_helper( SlaveVP *newSlv,       TopLevelFnPtr  fnPtr,
                                void      *dataParam, void           *stackLocs );

inline
PRMetaTask *
PR_int__create_generic_slave_meta_task( void *initData );

inline
void
PR_int__reset_slaveVP_to_TopLvlFn( SlaveVP *slaveVP, TopLevelFnPtr fnPtr,
                              void    *dataParam);

inline
void
PR_int__point_slaveVP_to_OneParamFn( SlaveVP *slaveVP, void *fnPtr,
                              void    *param);

inline
void
PR_int__point_slaveVP_to_TwoParamFn( SlaveVP *slaveVP, void *fnPtr,
                              void    *param1, void *param2);

inline
void
PR_int__dissipate_slaveVP( SlaveVP *slaveToDissipate );
#define PR_PI__dissipate_slaveVP PR_int__dissipate_slaveVP
//WL: dissipate a SlaveVP by sending a request

inline
void
PR_int__dissipate_slaveVP_multilang( SlaveVP *slaveToDissipate );

inline
void
PR_int__throw_exception( char *msgStr, SlaveVP *reqstSlv, PRExcp *excpData );
#define PR_PI__throw_exception  PR_int__throw_exception
void
PR_WL__throw_exception( char *msgStr, SlaveVP *reqstSlv,  PRExcp *excpData );
#define PR_App__throw_exception PR_WL__throw_exception

inline
void *
PR_int__give_sem_env_for_slave( SlaveVP *slave, int32 magicNumber );
#define PR_PI__give_sem_env_for  PR_int__give_sem_env_for_slave
#define PR_SS__give_sem_env_for_slave  PR_int__give_sem_env_for_slave
//No WL version -- not safe!  if use env in WL, be sure data rd & wr is stable
inline
PRSemEnv *
PR_int__give_proto_sem_env_for_slave( SlaveVP *slave, int32 magicNumber );
#define PR_PI__give_proto_sem_env_for  PR_int__give_proto_sem_env_for_slave
#define PR_SS__give_proto_sem_env_for_slave  PR_int__give_proto_sem_env_for_slave
//No WL version -- not safe!  if use env in WL, be sure data rd & wr is stable
inline
void *
PR_int__give_sem_env_from_process( PRProcess *process, int32 magicNumer );
#define PR_PI__give_sem_env_from_process  PR_int__give_sem_env_from_process
#define PR_SS__give_sem_env_from_process  PR_int__give_sem_env_from_process
//#define PR_WL__give_sem_env_from_process  PR_int__give_sem_env_from_process
//No WL version -- not safe!  if use env in WL, be sure data rd & wr is stable

inline
void *
PR_int__give_sem_data( SlaveVP *slave, int32 magicNumer );
#define PR_PI__give_sem_data  PR_int__give_sem_data
#define PR_SS__give_sem_data  PR_int__give_sem_data
#define PR_WL__give_sem_data  PR_int__give_sem_data


#define PR_int__give_lang_meta_task( slave, magicNumber )\
        slave->metaTask->langMetaTask;
#define PR_PI__give_lang_meta_task  PR_int__give_lang_meta_task
#define PR_SS__give_lang_meta_task  PR_int__give_lang_meta_task
#define PR_WL__give_lang_meta_task  PR_int__give_lang_meta_task

inline
SlaveVP *
PR_PI__give_slave_assigned_to( PRLangMetaTask *langMetaTask );
        
void 
idle_fn(void* data, SlaveVP *animatingSlv);

inline void
PR_int__get_master_lock();

#define PR_int__release_master_lock() _PRTopEnv->masterLock = UNLOCKED

inline uint32_t
PR_int__randomNumber();

//==============  Request Related  ===============

void
PR_WL__suspend_slaveVP_and_send_req( SlaveVP *callingSlv );

inline void
PR_WL__add_sem_request_in_mallocd_PRReqst( void *semReqData, SlaveVP *callingSlv );

inline void
PR_WL__send_sem_request( void *semReq, SlaveVP *callingSlv, int32 magicNum );

void
PR_WL__send_create_slaveVP_req( void *semReqData, SlaveVP *reqstingSlv );

void inline
PR_WL__send_dissipate_req( SlaveVP *prToDissipate );

inline void
PR_WL__send_service_request( void *semReqData, SlaveVP *callingSlv );

PRReqst *
PR_PI__take_next_request_out_of( SlaveVP *slaveWithReq );
//#define PR_PI__take_next_request_out_of( slave ) slave->requests

//inline void *
//PR_PI__take_sem_reqst_from( PRReqst *req );
#define PR_PI__take_sem_reqst_from( req ) req->semReqData

void inline
PR_int__handle_PRServiceReq( PRReqst *req, SlaveVP *requestingSlv, void *semEnv,
                       ResumeSlvFnPtr resumeSlvFnPtr );

//======================== MEASUREMENT ======================
uint64
PR_WL__give_num_plugin_cycles();
uint32
PR_WL__give_num_plugin_animations();


//========================= Utilities =======================
inline char *
PR_int__strDup( char *str );


//=========================  PR request handlers  ========================
void inline
handleMakeProbe( PRServReq *semReq, void *semEnv, ResumeSlvFnPtr resumeFn );

void inline
handleThrowException( PRServReq *semReq, void *semEnv, ResumeSlvFnPtr resumeFn );
//=======================================================================

//========================= Probes =======================
#include "Services_Offered_by_PR/Measurement_and_Stats/probes.h"

//================================================
#endif	/* _PR_H */

