next up previous contents
Next: Data defined by the Up: The TimeWarp class Previous: Defining the state of

Methods the application provides to the kernel

There are three main methods that the kernel calls in the application code. The first is initialize. This method takes no parameters, and is called exactly once in each simulation object at the beginning of the simulation. Since WARPED requires that every object in the system is event driven, a good use of the initialize function is to have some objects send themselves events to ``prime'' the simulation. The PingObject with id == 0 does this using the following code:

void  
PingObject::initialize(){
  state.current->initialize();

  if( id == 0 ){
    // we need to give ourselves the first ball to start...
    BasicEvent *firstBall = new BasicEvent;
    firstBall->dest = id;
    firstBall->recvTime = 0;
    firstBall->size = sizeof( *firstBall );
    
    sendEvent( firstBall );
  }
  
}

Note the call to state.current->initialize(). As previously discussed, default constructors to initialize the states would be inefficient due to the fact that states are normally copied over once they have been allocated. This being the case, an explicit call to the user-defined initialize function is necessary once at the beginning of the simulation.

The rest of the method arbitrarily chooses object with id == 0 to start the simulation by sending an event to itself. The method sendEvent will be discussed in detail shortly.

The next application method that the kernel will call is executeProcess. This method defines the simulation object's behavior each time it gets scheduled for execution. If an object has no event to process, it is considered idle and won't get scheduled. Furthermore, once executeProcess has been called, control has passed completely from the kernel to the application. Until control is returned to the kernel, no other simulation objects on the same logical process can be scheduled.

A ``typical'' simulation will perform the following activities in a single simulation cycle:

The function executeProcess from the PingObject class follows:

 

void 
PingObject::executeProcess(){
  //we got an event
  //let's get the event
  BasicEvent *msgGot = getEvent();
  
  if(msgGot != NULL)
    {
      state.current->numBallsRecvd-- ;
      
      if(id != 0 || (id == 0 && state.current->numBallsRecvd != 0))
        {
          //we want to send another event
          BasicEvent *newMsg = (BasicEvent*) new char[sizeof(BasicEvent)] ;
          new (newMsg) BasicEvent();
          
          newMsg->dest = dest;
          newMsg->recvTime = state.current->lVT + 1 ;
          newMsg->size = sizeof(BasicEvent);
      
          state.current->numBallsSent++;
          sendEvent(newMsg);
        }
    }   
}

The last method defined in the application that is called by the kernel is finalize(). This optional method is called once at the end of a simulation in each object. This call is intended to allow the object to collect statistics or do any required housekeeping. The PingObject simply prints a summary of its final state information:

void 
PingObject::finalize(){
  cout << name << " sent " << state.current->numSent << " balls." << endl;
  cout << name << " received " << state.current->numReceived <<
    " balls." << endl;
}


next up previous contents
Next: Data defined by the Up: The TimeWarp class Previous: Defining the state of
Philip A. Wilsey
1/26/1998