/*
* Title: CloudSim Toolkit
* Description: CloudSim (Cloud Simulation) Toolkit for Modeling and Simulation of Clouds
* Licence: GPL - http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2009-2012, The University of Melbourne, Australia
*/
package org.cloudbus.cloudsim.core;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.cloudbus.cloudsim.Log;
import org.cloudbus.cloudsim.core.predicates.Predicate;
import org.cloudbus.cloudsim.core.predicates.PredicateAny;
import org.cloudbus.cloudsim.core.predicates.PredicateNone;
/**
* This class extends the CloudSimCore to enable network simulation in CloudSim. Also, it disables
* all the network models from CloudSim, to provide a simpler simulation of networking. In the
* network model used by CloudSim, a topology file written in BRITE format is used to describe the
* network. Later, nodes in such file are mapped to CloudSim entities. Delay calculated from the
* BRITE model are added to the messages send through CloudSim. Messages using the old model are
* converted to the apropriate methods with the correct parameters.
*
* @author Rodrigo N. Calheiros
* @author Anton Beloglazov
* @since CloudSim Toolkit 1.0
*/
public class CloudSim {
/** The Constant CLOUDSIM_VERSION_STRING. */
private static final String CLOUDSIM_VERSION_STRING = "3.0";
/** The id of CIS entity. */
private static int cisId = -1;
/** The id of CloudSimShutdown entity. */
@SuppressWarnings("unused")
private static int shutdownId = -1;
/** The CIS object. */
private static CloudInformationService cis = null;
/** The Constant NOT_FOUND. */
private static final int NOT_FOUND = -1;
/** The trace flag. */
@SuppressWarnings("unused")
private static boolean traceFlag = false;
/** The calendar. */
private static Calendar calendar = null;
/** The termination time. */
private static double terminateAt = -1;
/** The minimal time between events. Events within shorter periods after the last event are discarded. */
private static double minTimeBetweenEvents = 0.1;
/**
* Initialises all the common attributes.
*
* @param _calendar the _calendar
* @param _traceFlag the _trace flag
* @param numUser number of users
* @throws Exception This happens when creating this entity before initialising CloudSim package
* or this entity name is <tt>null</tt> or empty
* @pre $none
* @post $none
*/
private static void initCommonVariable(Calendar _calendar, boolean _traceFlag, int numUser)
throws Exception {
initialize();
// NOTE: the order for the below 3 lines are important
traceFlag = _traceFlag;
// Set the current Wall clock time as the starting time of
// simulation
if (_calendar == null) {
calendar = Calendar.getInstance();
} else {
calendar = _calendar;
}
// creates a CloudSimShutdown object
CloudSimShutdown shutdown = new CloudSimShutdown("CloudSimShutdown", numUser);
shutdownId = shutdown.getId();
}
/**
* Initialises CloudSim parameters. This method should be called before creating any entities.
* <p>
* Inside this method, it will create the following CloudSim entities:
* <ul>
* <li>CloudInformationService.
* <li>CloudSimShutdown
* </ul>
* <p>
*
* @param numUser the number of User Entities created. This parameters indicates that
* {@link gridsim.CloudSimShutdown} first waits for all user entities's
* END_OF_SIMULATION signal before issuing terminate signal to other entities
* @param cal starting time for this simulation. If it is <tt>null</tt>, then the time will be
* taken from <tt>Calendar.getInstance()</tt>
* @param traceFlag <tt>true</tt> if CloudSim trace need to be written
* @see gridsim.CloudSimShutdown
* @see CloudInformationService.CloudInformationService
* @pre numUser >= 0
* @post $none
*/
public static void init(int numUser, Calendar cal, boolean traceFlag) {
try {
initCommonVariable(cal, traceFlag, numUser);
// create a GIS object
cis = new CloudInformationService("CloudInformationService");
// set all the above entity IDs
cisId = cis.getId();
} catch (IllegalArgumentException s) {
Log.printLine("CloudSim.init(): The simulation has been terminated due to an unexpected error");
Log.printLine(s.getMessage());
} catch (Exception e) {
Log.printLine("CloudSim.init(): The simulation has been terminated due to an unexpected error");
Log.printLine(e.getMessage());
}
}
/**
* Initialises CloudSim parameters. This method should be called before creating any entities.
* <p>
* Inside this method, it will create the following CloudSim entities:
* <ul>
* <li>CloudInformationService.
* <li>CloudSimShutdown
* </ul>
* <p>
*
* @param numUser the number of User Entities created. This parameters indicates that
* {@link gridsim.CloudSimShutdown} first waits for all user entities's
* END_OF_SIMULATION signal before issuing terminate signal to other entities
* @param cal starting time for this simulation. If it is <tt>null</tt>, then the time will be
* taken from <tt>Calendar.getInstance()</tt>
* @param traceFlag <tt>true</tt> if CloudSim trace need to be written
* @param periodBetweenEvents - the minimal period between events. Events within shorter periods
* after the last event are discarded.
* @see gridsim.CloudSimShutdown
* @see CloudInformationService.CloudInformationService
* @pre numUser >= 0
* @post $none
*/
public static void init(int numUser, Calendar cal, boolean traceFlag, double periodBetweenEvents) {
if (periodBetweenEvents <= 0) {
throw new IllegalArgumentException("The minimal time between events should be positive, but is:" + periodBetweenEvents);
}
init(numUser, cal, traceFlag);
minTimeBetweenEvents = periodBetweenEvents;
}
/**
* Starts the execution of CloudSim simulation. It waits for complete execution of all entities,
* i.e. until all entities threads reach non-RUNNABLE state or there are no more events in the
* future event queue.
* <p>
* <b>Note</b>: This method should be called after all the entities have been setup and added.
*
* @return the double
* @throws NullPointerException This happens when creating this entity before initialising
* CloudSim package or this entity name is <tt>null</tt> or empty.
* @see gridsim.CloudSim#init(int, Calendar, boolean)
* @pre $none
* @post $none
*/
public static double startSimulation() throws NullPointerException {
Log.printLine("Starting CloudSim version " + CLOUDSIM_VERSION_STRING);
try {
double clock = run();
// reset all static variables
cisId = -1;
shutdownId = -1;
cis = null;
calendar = null;
traceFlag = false;
return clock;
} catch (IllegalArgumentException e) {
e.printStackTrace();
throw new NullPointerException("CloudSim.startCloudSimulation() :"
+ " Error - you haven't initialized CloudSim.");
}
}
/**
* Stops Cloud Simulation (based on {@link Simulation#runStop()}). This should be only called if
* any of the user defined entities <b>explicitly</b> want to terminate simulation during
* execution.
*
* @throws NullPointerException This happens when creating this entity before initialising
* CloudSim package or this entity name is <tt>null</tt> or empty
* @see gridsim.CloudSim#init(int, Calendar, boolean)
* @see Simulation#runStop()
* @pre $none
* @post $none
*/
public static void stopSimulation() throws NullPointerException {
try {
runStop();
} catch (IllegalArgumentException e) {
throw new NullPointerException("CloudSim.stopCloudSimulation() : "
+ "Error - can't stop Cloud Simulation.");
}
}
/**
* This method is called if one wants to terminate the simulation.
*
* @return true, if successful; false otherwise.
*/
public static boolean terminateSimulation() {
running = false;
printMessage("Simulation: Reached termination time.");
return true;
}
/**
* This method is called if one wants to terminate the simulation at a given time.
*
* @param time the time at which the simulation has to be terminated
* @return true, if successful otherwise.
*/
public static boolean terminateSimulation(double time) {
if (time <= clock) {
return false;
} else {
terminateAt = time;
}
return true;
}
/**
* Returns the minimum time between events. Events within shorter periods after the last event are discarded.
* @return the minimum time between events.
*/
public static double getMinTimeBetweenEvents() {
return minTimeBetweenEvents;
}
/**
* Gets a new copy of initial simulation Calendar.
*
* @return a new copy of Calendar object or if CloudSim hasn't been initialized
* @see gridsim.CloudSim#init(int, Calendar, boolean, String[], String[], String)
* @see gridsim.CloudSim#init(int, Calendar, boolean)
* @pre $none
* @post $none
*/
public static Calendar getSimulationCalendar() {
// make a new copy
Calendar clone = calendar;
if (calendar != null) {
clone = (Calendar) calendar.clone();
}
return clone;
}
/**
* Gets the entity ID of <tt>CloudInformationService</tt>.
*
* @return the Entity ID or if it is not found
* @pre $none
* @post $result >= -1
*/
public static int getCloudInfoServiceEntityId() {
return cisId;
}
/**
* Sends a request to Cloud Information Service (GIS) entity to get the list of all Cloud
* hostList.
*
* @return A List containing CloudResource ID (as an Integer object) or if a CIS entity hasn't
* been created before
* @pre $none
* @post $none
*/
public static List<Integer> getCloudResourceList() {
if (cis == null) {
return null;
}
return cis.getList();
}
// ======== SIMULATION METHODS ===============//
/** The entities. */
private static List<SimEntity> entities;
/** The future event queue. */
protected static FutureQueue future;
/** The deferred event queue. */
protected static DeferredQueue deferred;
/** The simulation clock. */
private static double clock;
/** Flag for checking if the simulation is running. */
private static boolean running;
/** The entities by name. */
private static Map<String, SimEntity> entitiesByName;
// The predicates used in entity wait methods
/** The wait predicates. */
private static Map<Integer, Predicate> waitPredicates;
/** The paused. */
private static boolean paused = false;
/** The pause at. */
private static long pauseAt = -1;
/** The abrupt terminate. */
private static boolean abruptTerminate = false;
/**
* Initialise the simulation for stand alone simulations. This function should be called at the
* start of the simulation.
*/
protected static void initialize() {
Log.printLine("Initialising...");
entities = new ArrayList<SimEntity>();
entitiesByName = new LinkedHashMap<String, SimEntity>();
future = new FutureQueue();
deferred = new DeferredQueue();
waitPredicates = new HashMap<Integer, Predicate>();
clock = 0;
running = false;
}
// The two standard predicates
/** A standard predicate that matches any event. */
public final static PredicateAny SIM_ANY = new PredicateAny();
/** A standard predicate that does not match any events. */
public final static PredicateNone SIM_NONE = new PredicateNone();
// Public access methods
/**
* Get the current simulation time.
*
* @return the simulation time
*/
public static double clock() {
return clock;
}
/**
* Get the current number of entities in the simulation.
*
* @return The number of entities
*/
public static int getNumEntities() {
return entities.size();
}
/**
* Get the entity with a given id.
*
* @param id the entity's unique id number
* @return The entity, or if it could not be found
*/
public static SimEntity getEntity(int id) {
return entities.get(id);
}
/**
* Get the entity with a given name.
*
* @param name The entity's name
* @return The entity
*/
public static SimEntity getEntity(String name) {
return entitiesByName.get(name);
}
/**
* Get the id of an entity with a given name.
*
* @param name The entity's name
* @return The entity's unique id number
*/
public static int getEntityId(String name) {
SimEntity obj = entitiesByName.get(name);
if (obj == null) {
return NOT_FOUND;
} else {
return obj.getId();
}
}
/**
* Gets name of the entity given its entity ID.
*
* @param entityID the entity ID
* @return the Entity name or if this object does not have one
* @pre entityID > 0
* @post $none
*/
public static String getEntityName(int entityID) {
try {
return getEntity(entityID).getName();
} catch (IllegalArgumentException e) {
return null;
} catch (Exception e) {
return null;
}
}
/**
* Gets name of the entity given its entity ID.
*
* @param entityID the entity ID
* @return the Entity name or if this object does not have one
* @pre entityID > 0
* @post $none
*/
public static String getEntityName(Integer entityID) {
if (entityID != null) {
return getEntityName(entityID.intValue());
}
return null;
}
/**
* Returns a list of entities created for the simulation.
*
* @return the entity iterator
*/
public static List<SimEntity> getEntityList() {
// create a new list to prevent the user from changing
// the list of entities used by Simulation
List<SimEntity> list = new LinkedList<SimEntity>();
list.addAll(entities);
return list;
}
// Public update methods
/**
* Add a new entity to the simulation. This is present for compatibility with existing
* simulations since entities are automatically added to the simulation upon instantiation.
*
* @param e The new entity
*/
public static void addEntity(SimEntity e) {
SimEvent evt;
if (running) {
// Post an event to make this entity
evt = new SimEvent(SimEvent.CREATE, clock, 1, 0, 0, e);
future.addEvent(evt);
}
if (e.getId() == -1) { // Only add once!
int id = entities.size();
e.setId(id);
entities.add(e);
entitiesByName.put(e.getName(), e);
}
}
/**
* Internal method used to add a new entity to the simulation when the simulation is running. It
* should <b>not</b> be called from user simulations.
*
* @param e The new entity
*/
protected static void addEntityDynamically(SimEntity e) {
if (e == null) {
throw new IllegalArgumentException("Adding null entity.");
} else {
printMessage("Adding: " + e.getName());
}
e.startEntity();
}
/**
* Internal method used to run one tick of the simulation. This method should <b>not</b> be
* called in simulations.
*
* @return true, if successful otherwise
*/
public static boolean runClockTick() {
SimEntity ent;
boolean queue_empty;
int entities_size = entities.size();
for (int i = 0; i < entities_size; i++) {
ent = entities.get(i);
if (ent.getState() == SimEntity.RUNNABLE) {
ent.run();
}
}
// If there are more future events then deal with them
if (future.size() > 0) {
List<SimEvent> toRemove = new ArrayList<SimEvent>();
Iterator<SimEvent> fit = future.iterator();
queue_empty = false;
SimEvent first = fit.next();
processEvent(first);
future.remove(first);
fit = future.iterator();
// Check if next events are at same time...
boolean trymore = fit.hasNext();
while (trymore) {
SimEvent next = fit.next();
if (next.eventTime() == first.eventTime()) {
processEvent(next);
toRemove.add(next);
trymore = fit.hasNext();
} else {
trymore = false;
}
}
future.removeAll(toRemove);
} else {
queue_empty = true;
running = false;
printMessage("Simulation: No more future events");
}
return queue_empty;
}
/**
* Internal method used to stop the simulation. This method should <b>not</b> be used directly.
*/
public static void runStop() {
printMessage("Simulation completed.");
}
/**
* Used to hold an entity for some time.
*
* @param src the src
* @param delay the delay
*/
public static void hold(int src, long delay) {
SimEvent e = new SimEvent(SimEvent.HOLD_DONE, clock + delay, src);
future.addEvent(e);
entities.get(src).setState(SimEntity.HOLDING);
}
/**
* Used to pause an entity for some time.
*
* @param src the src
* @param delay the delay
*/
public static void pause(int src, double delay) {
SimEvent e = new SimEvent(SimEvent.HOLD_DONE, clock + delay, src);
future.addEvent(e);
entities.get(src).setState(SimEntity.HOLDING);
}
/**
* Used to send an event from one entity to another.
*
* @param src the src
* @param dest the dest
* @param delay the delay
* @param tag the tag
* @param data the data
*/
public static void send(int src, int dest, double delay, int tag, Object data) {
if (delay < 0) {
throw new IllegalArgumentException("Send delay can't be negative.");
}
SimEvent e = new SimEvent(SimEvent.SEND, clock + delay, src, dest, tag, data);
future.addEvent(e);
}
/**
* Used to send an event from one entity to another, with priority in the queue.
*
* @param src the src
* @param dest the dest
* @param delay the delay
* @param tag the tag
* @param data the data
*/
public static void sendFirst(int src, int dest, double delay, int tag, Object data) {
if (delay < 0) {
throw new IllegalArgumentException("Send delay can't be negative.");
}
SimEvent e = new SimEvent(SimEvent.SEND, clock + delay, src, dest, tag, data);
future.addEventFirst(e);
}
/**
* Sets an entity's state to be waiting. The predicate used to wait for an event is now passed
* to Sim_system. Only events that satisfy the predicate will be passed to the entity. This is
* done to avoid unnecessary context switches.
*
* @param src the src
* @param p the p
*/
public static void wait(int src, Predicate p) {
entities.get(src).setState(SimEntity.WAITING);
if (p != SIM_ANY) {
// If a predicate has been used store it in order to check it
waitPredicates.put(src, p);
}
}
/**
* Checks if events for a specific entity are present in the deferred event queue.
*
* @param d the d
* @param p the p
* @return the int
*/
public static int waiting(int d, Predicate p) {
int count = 0;
SimEvent event;
Iterator<SimEvent> iterator = deferred.iterator();
while (iterator.hasNext()) {
event = iterator.next();
if ((event.getDestination() == d) && (p.match(event))) {
count++;
}
}
return count;
}
/**
* Selects an event matching a predicate.
*
* @param src the src
* @param p the p
* @return the sim event
*/
public static SimEvent select(int src, Predicate p) {
SimEvent ev = null;
Iterator<SimEvent> iterator = deferred.iterator();
while (iterator.hasNext()) {
ev = iterator.next();
if (ev.getDestination() == src && p.match(ev)) {
iterator.remove();
break;
}
}
return ev;
}
/**
* Find first deferred event matching a predicate.
*
* @param src the src
* @param p the p
* @return the sim event
*/
public static SimEvent findFirstDeferred(int src, Predicate p) {
SimEvent ev = null;
Iterator<SimEvent> iterator = deferred.iterator();
while (iterator.hasNext()) {
ev = iterator.next();
if (ev.getDestination() == src && p.match(ev)) {
break;
}
}
return ev;
}
/**
* Removes an event from the event queue.
*
* @param src the src
* @param p the p
* @return the sim event
*/
public static SimEvent cancel(int src, Predicate p) {
SimEvent ev = null;
Iterator<SimEvent> iter = future.iterator();
while (iter.hasNext()) {
ev = iter.next();
if (ev.getSource() == src && p.match(ev)) {
iter.remove();
break;
}
}
return ev;
}
/**
* Removes all events that match a given predicate from the future event queue returns true if
* at least one event has been cancelled; false otherwise.
*
* @param src the src
* @param p the p
* @return true, if successful
*/
public static boolean cancelAll(int src, Predicate p) {
SimEvent ev = null;
int previousSize = future.size();
Iterator<SimEvent> iter = future.iterator();
while (iter.hasNext()) {
ev = iter.next();
if (ev.getSource() == src && p.match(ev)) {
iter.remove();
}
}
return previousSize < future.size();
}
//
// Private internal methods
//
/**
* Processes an event.
*
* @param e the e
*/
private static void processEvent(SimEvent e) {
int dest, src;
SimEntity dest_ent;
// Update the system's clock
if (e.eventTime() < clock) {
throw new IllegalArgumentException("Past event detected.");
}
clock = e.eventTime();
// Ok now process it
switch (e.getType()) {
case SimEvent.ENULL:
throw new IllegalArgumentException("Event has a null type.");
case SimEvent.CREATE:
SimEntity newe = (SimEntity) e.getData();
addEntityDynamically(newe);
break;
case SimEvent.SEND:
// Check for matching wait
dest = e.getDestination();
if (dest < 0) {
throw new IllegalArgumentException("Attempt to send to a null entity detected.");
} else {
int tag = e.getTag();
dest_ent = entities.get(dest);
if (dest_ent.getState() == SimEntity.WAITING) {
Integer destObj = Integer.valueOf(dest);
Predicate p = waitPredicates.get(destObj);
if ((p == null) || (tag == 9999) || (p.match(e))) {
dest_ent.setEventBuffer((SimEvent) e.clone());
dest_ent.setState(SimEntity.RUNNABLE);
waitPredicates.remove(destObj);
} else {
deferred.addEvent(e);
}
} else {
deferred.addEvent(e);
}
}
break;
case SimEvent.HOLD_DONE:
src = e.getSource();
if (src < 0) {
throw new IllegalArgumentException("Null entity holding.");
} else {
entities.get(src).setState(SimEntity.RUNNABLE);
}
break;
default:
break;
}
}
/**
* Internal method used to start the simulation. This method should <b>not</b> be used by user
* simulations.
*/
public static void runStart() {
running = true;
// Start all the entities
for (SimEntity ent : entities) {
ent.startEntity();
}
printMessage("Entities started.");
}
/**
* Check if the simulation is still running. This method should be used by entities to check if
* they should continue executing.
*
* @return if the simulation is still running, otherwise
*/
public static boolean running() {
return running;
}
/**
* This method is called if one wants to pause the simulation.
*
* @return true, if successful otherwise.
*/
public static boolean pauseSimulation() {
paused = true;
return paused;
}
/**
* This method is called if one wants to pause the simulation at a given time.
*
* @param time the time at which the simulation has to be paused
* @return true, if successful otherwise.
*/
public static boolean pauseSimulation(long time) {
if (time <= clock) {
return false;
} else {
pauseAt = time;
}
return true;
}
/**
* This method is called if one wants to resume the simulation that has previously been paused.
*
* @return if the simulation has been restarted or or otherwise.
*/
public static boolean resumeSimulation() {
paused = false;
if (pauseAt <= clock) {
pauseAt = -1;
}
return !paused;
}
/**
* Start the simulation running. This should be called after all the entities have been setup
* and added, and their ports linked.
*
* @return the double last clock value
*/
public static double run() {
if (!running) {
runStart();
}
while (true) {
if (runClockTick() || abruptTerminate) {
break;
}
// this block allows termination of simulation at a specific time
if (terminateAt > 0.0 && clock >= terminateAt) {
terminateSimulation();
clock = terminateAt;
break;
}
if (pauseAt != -1
&& ((future.size() > 0 && clock <= pauseAt && pauseAt <= future.iterator().next()
.eventTime()) || future.size() == 0 && pauseAt <= clock)) {
pauseSimulation();
clock = pauseAt;
}
while (paused) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
double clock = clock();
finishSimulation();
runStop();
return clock;
}
/**
* Internal method that allows the entities to terminate. This method should <b>not</b> be used
* in user simulations.
*/
public static void finishSimulation() {
// Allow all entities to exit their body method
if (!abruptTerminate) {
for (SimEntity ent : entities) {
if (ent.getState() != SimEntity.FINISHED) {
ent.run();
}
}
}
for (SimEntity ent : entities) {
ent.shutdownEntity();
}
// reset all static variables
// Private data members
entities = null;
entitiesByName = null;
future = null;
deferred = null;
clock = 0L;
running = false;
waitPredicates = null;
paused = false;
pauseAt = -1;
abruptTerminate = false;
}
/**
* Abruptally terminate.
*/
public static void abruptallyTerminate() {
abruptTerminate = true;
}
/**
* Prints a message about the progress of the simulation.
*
* @param message the message
*/
private static void printMessage(String message) {
Log.printLine(message);
}
/**
* Checks if is paused.
*
* @return true, if is paused
*/
public static boolean isPaused() {
return paused;
}
}