///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Copyright (C) 2006 by Intel Coproration and Carnegie Mellon University    //
// Contacts: casey.j.helfrich @ intel.com                                    //
//           bdr @ cs.cmu.edu                                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#include "CodeModule.hxx"
#include "CatomSim.hxx"
#include "CatomWorld.hxx"
#include "Debugging.hxx"
#include "SmartYield.hxx"

CodeModule::CodeModule() {
  exit(-1);
}

CodeModule::CodeModule(catomID _hostCatom) : hostCatom(_hostCatom) { }

CodeModule::~CodeModule() {  }

void CodeModule::simulationStart() {
  // don't do anything by default
}

void CodeModule::newTick() {
  // don't do anything by default
}

void CodeModule::endTick() {
  // don't do anything by default
}

StateFile::Module* CodeModule::StateFileConstructor() {
  myModule_ = NULL;
  return NULL;
}

void CodeModule::loadModule() {
  // don't do anything by default
}

void CodeModule::saveModule() {
  // don't do anything by default
}

void CodeModule::simulationEnd() {
  // don't do anything by default
}

void CodeModule::oracle() {
  // don't do anything by default
}

void CodeModule::thread() {
  // don't do anything by default
}


// oracle and thread support

// SmartYield structure
extern pt_yield yield;

// flag signifying whether loop is done
// set by DPRSim main loop
extern bool isComplete;
extern bool NC_Oracles;
extern pthread_mutex_t NC_lock;

void oracleExit() {
//  cout << "ENDING ORACLE THREAD\n";
  if (NC_Oracles) pthread_mutex_unlock( &NC_lock );
  decr_nthreads( &yield );
  pthread_exit(NULL);
}

static inline void oracleYieldHelper() {
  do_yield( &yield );
  if (NC_Oracles) pthread_mutex_lock( &NC_lock );
  if (isComplete) oracleExit();
}

static inline void oracleYieldInitial() { oracleYieldHelper(); }

void oracleYield() {
  if (NC_Oracles) pthread_mutex_unlock( &NC_lock );
  oracleYieldHelper();
}

static void* oracleMain( void* m ) {
  CodeModule *cm = (CodeModule*) m;
  // first yield to synchronize start
  //do_yield( &yield );
  oracleYieldInitial();
  if (!isComplete) {
    cm->oracle();
  }
  oracleExit();
  return NULL;
}

void startCodeModuleOracle( CodeModule* m ) {
//  CodeModule tmp(0);
//  if ( &(m->oracle) == &(tmp.oracle) ) return;
//  cout << "STARTING ORACLE THREAD\n";

#ifdef __THREADED__
  int ret;
  pthread_t tid;

  // Set stack size
  pthread_attr_t attrs;
  pthread_attr_init(&attrs);
  pthread_attr_setstacksize(&attrs, (1<<15));

  incr_nthreads( &yield );  // new: need to increment number of threads that yield
  
  // create a thread for this catom by catomMain
  if( (ret = pthread_create(&tid, &attrs, oracleMain, m)) != 0 ) {
    cerr << "Error: unable to create thread" << " :: " 
	 << strerror(ret) << endl;
    exit(0);
  }
  
  // store thread ID in the list in CatomWorld
  catomThreadList.push_back(tid);
#endif
}


// global vars for automatic registration of linked-in modules

CodeModuleListEntry* CodeModuleListEntry::ModuleList = 0;
int* CODE_MODULE_DECLARATIONS::module_id_remap = 0;
int CodeModuleID::max_value=0;

