// -*-c++-*- #ifndef STATEMANAGER_CC #define STATEMANAGER_CC //Copyright (c) 1994-1996 Ohio Board of Regents and the University of // Cincinnati. All Rights Reserved. // // BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY // FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT // PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, // EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE // PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME // THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. // // IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING // WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR // REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR // DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL // DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM // (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED // INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF // THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER // OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. // // // $Id: StateManager.cc,v 1.1.1.1 2007/03/15 15:45:06 rmadhoun Exp $ // //--------------------------------------------------------------------------- #include #include "StateManager.hh" #include "BasicTimeWarp.hh" #ifdef JACKY_NC_BP_STATE // [2006-02-02] #include "../../../pncoord.h" //test ParallelNodeCoordinator #endif //end JACKY_NC_BP_STATE [2006-02-02] // NEW_STATE_MANAGEMENT // With the new style of state management fully in place the default // constructor should be stripped off and this constructor will be the new // interface to StateManager. Ofcourse the template should go too. StateManager::StateManager(BasicTimeWarp *myProcessPtr) { processPointer = myProcessPtr; current = NULL; nameOfObject = NULL; memoryUseage = 0; maxQsize = 0; statesRolledBack = 0; returnMyLastOutput = true; } StateManager::~StateManager() { if(nameOfObject != NULL) delete [] nameOfObject; // delete the name delete current; // delete current State gcollect(PINFINITY); // delete all but last stateQ element stateQ.setCurrent(stateQ.getHead()); delete stateQ.removeCurrent(); // remove & delete last element from stateQ } //Jacky: this function restore the state of a TimeWarp obj to the state //before or at the "restoreTime" VTime StateManager::restoreState(VTime restoreTime) { BasicState *tmpState; // first find the closest state in the state queue #ifdef JACKY_DEBUG //======================================================================= ostream& jacky_os = JackyDebugStream::Instance().Stream(); jacky_os << "\t\t==a>== procId[" << getProcessPointer()->id <<"] StateManager::restoreState(" << restoreTime.asString() << ") BEGIN -> stateQ.size = " << stateQ.size() << endl << flush; jacky_os << "----------------- stateQ [procId " << getProcessPointer()->id << "]------------------" << endl << flush; printQ(jacky_os); #endif //END JACKY_DEBUG =================================================================== #ifndef JACKY_RB_ZERO // Nov. 7, 2005 this is the original version ******************************* //Jacky: get the LAST state with lVT < restoreTime tmpState = stateQ.find(restoreTime, LESS); #else //To solve the problem of rolling back to "before zero" ************************************ //Jacky Note: Nov. 7, 2005 //The following iff condition is added for handling "rolling back to time before zero" problem //For example, this problem happens when the NC has advanced its local time to 1000, and then it //received a message from other NCs that has a recvTime of 000. Thus, the NC will be rolled back //to a time BEFORE 000 (the recvTime of this received message), i.e. this function will be called //with restoreTime = VTime::Zero. However, we cannot find a state on the stateQ that has a lVT with //lVT < 0. This causes the simulation to be aborted. In this case, we should restore the state to //the initial state, which is the 1st state on the stateQ, and remove all state after it. if( restoreTime == VTime::Zero ){ tmpState = stateQ.getHead(); //this is the initial state cerr << "*** StateManager::restoreState(" << restoreTime.asString() << ") get the head of stateQ ***" << endl; } else { tmpState = stateQ.find(restoreTime, LESS); } #endif //end JACKY_RB_ZERO *********************************************************************** if (tmpState != NULL) { // found a replacement state // current needs to be a _copy_, not a pointer assignment // *(current) = *(UserState *)tmpState; //Jacky: this will restore the current state to the tmpState current->copyState(tmpState); #ifdef JACKY_DEBUG //====================================================================== //let's see how the state is restored after a rollback jacky_os << "\t***** procId[" << getProcessPointer()->id <<"]::StateManager::restoreState()" << endl << flush; jacky_os << "\t\t*** RESTORED CURRENT STATE ======> " << endl << flush; current->showStateContent( jacky_os ); jacky_os << "\t\t==b>== procId[" << getProcessPointer()->id <<"] StateManager::restoreState(" << restoreTime.asString() << ") state before the restoreTime found! ->" << endl << "tmpState = " << tmpState << " " << (*tmpState) << endl << " now, currentState = " << current << " " << (*current) << endl << flush; #endif //end JACKY_DEBUG ================================================================== } else { //tmpState == NULL cerr << "StateManager::restoreState --> state not found" << endl; stateQ.print(); #ifdef JACKY_DEBUG //===================================================================== jacky_os << "\t\t==b'>== procId[" << getProcessPointer()->id <<"] StateManager::restoreState(" << restoreTime.asString() << ") state before restoreTime NOT found! return INF" << endl << flush; #endif //end JACKY_DEBUG ================================================================= return PINFINITY; } #ifndef JACKY_RB_ZERO // Nov. 7, 2005 this is the original version ******************************* //[2006-02-02] Jacky Note: //set [currentPos = findPos] on the StateQueue stateQ.setCurrentToFind(); //[2006-02-02] Jacky Note: //But removing state from the StateQueue will reset the findPos = NULL !!! //And currentPos will slide along the queue while removing takes place, so at the end, currentPos = NULL removeStatesAfterCurrent(); //[2006-02-02] Jacky Note: //This has no use!!! Since at this time, findPos = NULL, currentPos = NULL, setting [currentPos = findPos] again //does nothing.... //this is done, because removeStatesAfterCurrent alters currentPos //can be removed - sanity check stateQ.setCurrentToFind(); #else //To solve the problem of rolling back to "before zero" ************************************ if( restoreTime == VTime::Zero ){ stateQ.setCurrentToHead(); removeStatesAfterCurrent(); stateQ.setCurrentToHead(); } else { stateQ.setCurrentToFind(); removeStatesAfterCurrent(); //this is done, because removeStatesAfterCurrent alters currentPos // can be removed - sanity check stateQ.setCurrentToFind(); } #endif //end JACKY_RB_ZERO *********************************************************************** #ifdef JACKY_DEBUG //================================================================== jacky_os << "\t\t==c>== procId[" << getProcessPointer()->id <<"] StateManager::restoreState(" << restoreTime.asString() << ") remove states after current done" << endl << flush; jacky_os << "----------------- stateQ [procId " << getProcessPointer()->id << "]------------------" << endl << flush; printQ(jacky_os); jacky_os << "\t\t==d>== procId[" << getProcessPointer()->id <<"] StateManager::restoreState(" << restoreTime.asString() << ") END -> return current->lVT = " << (current->lVT).asString() << endl << flush; #endif //============================================================================== //[2006-02-02] //after removing all states AFTER the findPos (the last state with lVT < rollbackTime) //the current state is copied from the state pointed by findPos, thus it is a copy of the //current END state of the stateQueue. return current->lVT; //time of the End state on the stateQ after removing all states after findPos } void StateManager::removeStatesAfterCurrent() { BasicState *tmpState; int counter = 0; #ifdef JACKY_DEBUG ostream& jacky_os = JackyDebugStream::Instance().Stream(); #endif // this is definitely non-optimal tmpState = stateQ.seek(1,CURRENT); //Jacky: move currentPos forward by 1 from current position //Jacky: before entering this function, currentPos = findPos = (LAST state with lVT < restoreTime) //Now, currentPos is the 1st state with lVT >= restoreTime, i.e. the 1st state to be removed from the queue // int Qsize = stateQ.size(); #ifdef JACKY_NC_BP_STATE //[2006-02-02] ---------------------------------------------------------------- //before we remove all states after the state found, check whether the 1st state to be removed //is a breakpoint state. This check needs only to be done on the NC's stateQ! if( (tmpState != NULL) && (tmpState->breakpoint == true) ){ //get the processor associated with this StateManager BasicTimeWarp* myProcessor = getProcessPointer(); ParallelNodeCoordinator* pnc = dynamic_cast(myProcessor); if( pnc != NULL ) { //this is a NC's stateQ and a breakpoint state is going to be processed! #ifdef JACKY_DEBUG int ncId = pnc->id(); //NC's id #endif int ncLocalId = pnc->localId; //Nc's local id #ifdef JACKY_DEBUG cerr << "\t\t\tNC " << ncId << "rollback to BP state!!!" << endl; jacky_os << endl << "2006-02-02 StateManager::removeStatesAfterCurrent()" << endl << flush; jacky_os << "\tprocessor address = " << myProcessor << " / NC address = " << pnc << endl << flush; jacky_os << "\tNC id = " << ncId << " / NC localId = " << ncLocalId << endl << flush; #endif //now, get the inputPos from the state BasicEvent* toExecute = tmpState->inputPos; #ifdef JACKY_DEBUG cerr << "\t\t\t\t->inputPos = " << toExecute << endl; jacky_os << "\tNC toExecute: inputPos = " << toExecute << " [" << (*toExecute) << "]" << endl << endl << flush; #endif //call LTSFInputQueue::reExecuteImmediately(BasicEvent *inputPos, int localId) //to unprocess and execute the event immediately BasicTimeWarp::inputQ.reExecuteImmediately( toExecute, ncLocalId ); } //end pnc != NULL } //end tmpState != NULL && tmpState->breakpoint == true #endif //end JACKY_NC_BP_STATE [2006-02-02] ------------------------------------------------------------ while(tmpState != NULL){ // this will advance currentPos one state forward stateQ.removeCurrent(); delete tmpState; // look at the next state... tmpState = stateQ.get(); counter++; } // This is for optimistic fossil collection and adaptive state savings statesRolledBack = counter; } void StateManager::saveState() { #ifdef JACKY_STATISTICS //[2006-04-02] BasicTimeWarp::jackyWatch.start(); #endif //end JACKY_STATISTICS //[2006-04-02] BasicState* tempState; #ifdef LPDEBUG *lpFile << nameOfObject << " Saved state with lVT: " << current->lVT << endl; #endif // This is the way the state manager will be allocating new state henceforth // for now, since TimeWarp is templatized its allocateState creates a new // UserState and returns it to us. Later when the templates are stripped off // the user's process should overload this function do allocate a new state tempState = (BasicState *) getProcessPointer()->allocateState(); // The copyState function should copy the BasicState* pointer passed // to it to itself. Ofcourse it has type cast it to the corresponding type // of the state in the corresponding overloaded method. // As of now, this function simply calls operator=() tempState->copyState(current); #ifdef JACKY_DEBUG //============================================================================ ostream& jacky_os = JackyDebugStream::Instance().Stream(); jacky_os << "\t****** procId[" << getProcessPointer()->id <<"]::StateManager::saveState() => copy current state, new allocated state address = " << tempState << endl << flush; jacky_os << "\t\tinputPos = " << tempState->inputPos << flush; if( tempState->inputPos != NULL ){ jacky_os << " [" << *(tempState->inputPos) << "]" << flush; } else { jacky_os << " [NULL]" << flush; } jacky_os << endl; jacky_os << "\t current->dirty = " << flush; if(tempState->dirty == true){ jacky_os << "T" << endl << flush; } else { jacky_os << "F" << endl << flush; } #ifdef JACKY_NC_BP_STATE //[2006-02-02] jacky_os << "\t current->BP = " << flush; if(tempState->breakpoint == true){ jacky_os << "T" << endl << flush; } else { jacky_os << "F" << endl << flush; } #endif //end JACKY_NC_BP_STATE [2006-02-02] jacky_os << "\t current->lVT = " << (tempState->lVT).asString() << endl << flush; jacky_os << "\t*** Copied State in stateQ =======> " << endl << flush; tempState->showStateContent( jacky_os ); #endif //end JACKY_DEBUG ====================================================================== stateQ.insert(tempState); #ifdef JACKY_NC_BP_STATE //[2006-02-02] //Note: the NC may set the "breakpoint" flag in its current state to true //After saving the current state onto the stateQ, we need to reset this flag to false! if( current->breakpoint == true ){ //breakpoint in the current state is true current->breakpoint = false; //reset it to false #ifdef JACKY_DEBUG jacky_os << endl << "[2006-03-27] StateManager::saveState() -> reset current->BP = False!" << endl << flush; #endif } //the value of the flag is saved on the stateQ now! #endif //end JACKY_NC_BP_STATE [2006-02-02] #ifdef JACKY_STATISTICS //[2006-04-02] BasicTimeWarp::totalNumberOfStatesSaved++; BasicTimeWarp::jackyWatch.stop(); BasicTimeWarp::totalTimeForStateSaving += BasicTimeWarp::jackyWatch.elapsed(); #endif //end JACKY_STATISTICS //[2006-04-02] } void StateManager::clear(){ stateQ.clear(); } //[2006-03-20] GVTManager::gcollect() Step 1 *************************************************************** VTime StateManager::gcollect(VTime gtime, BasicEvent*& inputQptr, Container*& outputQptr) { //1. we gcollect for stateQ and also get back appropriately initialized intputQPtr and outputQPtr . //2. since we want to call clear function of stateQ to null out the outputPos pointers of the head elements // of the stateQ before gcollecting for outputQ we store the appropriate outputPos value in returnMyLastOutput. int Qsize = stateQ.size(); if (Qsize > maxQsize) { maxQsize = Qsize; } VTime returnTime = stateQ.gcollect(gtime, inputQptr, outputQptr) ; returnMyLastOutput = stateQ.getHead()->myLastOutput; return returnTime ; } VTime StateManager::timeOfStateFromCurrent(int statesFromCurrent) { return stateQ.timeOfStateFromCurrent(statesFromCurrent); } // This function is needed for the new style of state management // This function will be called by timeWarpInit() to allocate the // initial state for the process in the system. void StateManager::createInitialState() { current = (BasicState *) getProcessPointer()->allocateState(); current->dirty = false; current->lVT = ZERO; } int StateManager::queueSize() { return stateQ.size(); } int StateManager::getStatePeriod() { return -1; } VTime StateManager::gcollect(VTime) { return ZERO; } bool StateManager::useTimeForGcollect() { return false; } void StateManager::clearHead() { stateQ.getHead()->outputPos = NULL; stateQ.getHead()->myLastOutput = false; } void StateManager::setTail(Container* outpos, bool lastOutput) { stateQ.getTail()->outputPos = outpos; stateQ.getHead()->myLastOutput = lastOutput; } #ifdef STATEDEBUG //Do not remove - Vital error checking function void StateManager::checkEvent(Container *checkEvent, int flag){ BasicState *tempState; tempState = stateQ.getHead(); while(tempState != NULL){ if(tempState->outputPos != NULL){ if(tempState->outputPos == checkEvent){ // checking outputPos in current state to first state if(tempState->outputPos != stateQ.getHead()->outputPos){ cout << "Head of State Queue at " << stateQ.getHead()->lVT << endl; switch(flag){ case 1: cout << "Anti-message for outputPos message at " << endl; cout << "Referenced in state at " << tempState->lVT << " to " << *checkEvent->object << endl; break; case 2: cout << "Warning -- Garbage collect outputPos message" << endl; cout << "Referenced in state at " << tempState->lVT << " to " << *checkEvent->object << endl; break; default: cout << "Scooby dooo !!" << endl; } } } } tempState = tempState->next; } } #endif //#ifdef STATEDEBUG #ifdef JACKY_RB_EVENT //eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee //Nov. 18, 2005 //function to find all states on the stateQ that have outputPos pointing to a given event list StateManager::findAllStateWithOutputPos( const BasicEvent* outputPosObject ){ #ifdef JACKY_DEBUG ostream& jacky_os = JackyDebugStream::Instance().Stream(); int index = 0; int num = 0; #endif list states; BasicState *tempState; tempState = stateQ.getHead(); //int index = 0; //int num = 0; while(tempState != NULL){ if(tempState->outputPos != NULL){ //not the 1st state if( tempState->outputPos->object == outputPosObject ){ #ifdef JACKY_DEBUG //state whose outputPos pointing to the given event found jacky_os << "[$$$==OOP==$$$] StateManager: state found with outputPos = " << tempState->outputPos << " / stateQ[" << index << "]" << endl << flush; #endif states.push_back( tempState ); #ifdef JACKY_DEBUG ++num; #endif } } tempState = tempState->next; #ifdef JACKY_DEBUG ++index; #endif } //end while #ifdef JACKY_DEBUG jacky_os << "[$$$==OOP==$$$] StateManager: totally {" << num << "} states found with the given outputPos" << endl << flush; #endif return states; } #endif //end JACKY_RB_EVENT eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee void StateManager::setFile(ofstream *outfile) { lpFile = outfile; stateQ.setStateFile(outfile); stateQ.setFile(outfile); } VTime StateManager::findMinimumState(const VTime &gTime) { BasicState *lastkeep; if ((lastkeep = stateQ.find(gTime, LESS)) != NULL) { return lastkeep->lVT; } return stateQ.getHead()->lVT; } #ifdef JACKY_DEBUG //======================================================================== void StateManager::printQ(ostream& os){ BasicState* pstate; unsigned i; i = 0; if (stateQ.size() == 0) { os << "stateQ = (NULL)\n"; } else { for (pstate = stateQ.getHead(); pstate != NULL; pstate = pstate->next) { os << "stateQ[" << i << "] = " << pstate << " " << *(pstate) << "\n"; i++; } } if(stateQ.getCurrent() != NULL) { os << "stateQ::currentPos = " << stateQ.getCurrent() << " " << *(stateQ.getCurrent()) <