--- /dev/null
+/*
+ * Copyright (c) 2003-2005 The BISON Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+package peersim.edsim;
+
+import java.util.Arrays;
+
+import peersim.Simulator;
+import peersim.config.*;
+import peersim.core.*;
+import psgsim.PSGSimulator;
+
+/**
+ * Event-driven simulator engine. It is a fully static singleton class. For an
+ * event driven simulation the configuration has to describe a set of
+ * {@link Protocol}s, a set of {@link Control}s and their ordering and a set of
+ * initializers and their ordering. See parameters {@value #PAR_INIT},
+ * {@value #PAR_CTRL}.
+ * <p>
+ * One experiment run by {@link #nextExperiment} works as follows. First the
+ * initializers are run in the specified order. Then the first execution of all
+ * specified controls is scheduled in the event queue. This scheduling is
+ * defined by the {@link Scheduler} parameters of each control component. After
+ * this, the first event is taken from the event queue. If the event wraps a
+ * control, the control is executed, otherwise the event is delivered to the
+ * destination protocol, that must implement {@link EDProtocol}. This is
+ * iterated while the current time is less than {@value #PAR_ENDTIME} or the
+ * queue becomes empty. If more control events fall at the same time point, then
+ * the order given in the configuration is respected. If more non-control events
+ * fall at the same time point, they are processed in a random order.
+ * <p>
+ * The engine also provides the interface to add events to the queue. Note that
+ * this engine does not explicitly run the protocols. In all cases at least one
+ * control or initializer has to be defined that sends event(s) to protocols.
+ * <p>
+ * Controls can be scheduled (using the {@link Scheduler} parameters in the
+ * configuration) to run after the experiment has finished. That is, each
+ * experiment is finished by running the controls that are scheduled to be run
+ * after the experiment.
+ * <p>
+ * Any control can interrupt an experiment at any time it is executed by
+ * returning true in method {@link Control#execute}. However, the controls
+ * scheduled to run after the experiment are still executed completely,
+ * irrespective of their return value and even if the experiment was
+ * interrupted.
+ * <p>
+ * {@link CDScheduler} has to be mentioned that is a control that can bridge the
+ * gap between {@link peersim.cdsim} and the event driven engine. It can wrap
+ * {@link peersim.cdsim.CDProtocol} appropriately so that the execution of the
+ * cycles are scheduled in configurable ways for each node individually. In some
+ * cases this can add a more fine-grained control and more realism to
+ * {@link peersim.cdsim.CDProtocol} simulations, at the cost of some loss in
+ * performance.
+ * <p>
+ * When protocols at different nodes send messages to each other, they might
+ * want to use a model of the transport layer so that in the simulation message
+ * delay and message omissions can be modeled in a modular way. This
+ * functionality is implemented in package {@link peersim.transport}.
+ *
+ * @see Configuration
+ */
+public class EDSimulator {
+
+ // ---------------------------------------------------------------------
+ // Parameters
+ // ---------------------------------------------------------------------
+
+ /**
+ * The ending time for simulation. Only events that have a strictly smaller
+ * value are executed. It must be positive. Although in principle negative
+ * timestamps could be allowed, we assume time will be positive.
+ *
+ * @config
+ */
+ public static final String PAR_ENDTIME = "simulation.endtime";
+
+ /**
+ * This parameter specifies how often the simulator should log the current
+ * time on the standard error. The time is logged only if there were events
+ * in the respective interval, and only the time of some actual event is
+ * printed. That is, the actual log is not guaranteed to happen in identical
+ * intervals of time. It is merely a way of seeing whether the simulation
+ * progresses and how fast...
+ *
+ * @config
+ */
+ private static final String PAR_LOGTIME = "simulation.logtime";
+
+ /**
+ * This parameter specifies the event queue to be used. It must be an
+ * implementation of interface {@link PriorityQ}. If it is not defined, the
+ * internal implementation is used.
+ *
+ * @config
+ */
+ private static final String PAR_PQ = "simulation.eventqueue";
+
+ /**
+ * This is the prefix for initializers. These have to be of type
+ * {@link Control}. They are run at the beginning of each experiment, in the
+ * order specified by the configuration.
+ *
+ * @see Configuration
+ * @config
+ * @config
+ */
+ private static final String PAR_INIT = "init";
+
+ /**
+ * This is the prefix for {@link Control} components. They are run at the
+ * time points defined by the {@link Scheduler} associated to them. If some
+ * controls have to be executed at the same time point, they are executed in
+ * the order specified in the configuration.
+ *
+ * @see Configuration
+ * @config
+ */
+ private static final String PAR_CTRL = "control";
+
+ // ---------------------------------------------------------------------
+ // Fields
+ // ---------------------------------------------------------------------
+
+ /** Maximum time for simulation */
+ private static long endtime;
+
+ /** Log time */
+ private static long logtime;
+
+ /** holds the modifiers of this simulation */
+ private static Control[] controls = null;
+
+ /** Holds the control schedulers of this simulation */
+ private static Scheduler[] ctrlSchedules = null;
+
+ /** Ordered list of events (heap) */
+ private static PriorityQ heap = null;
+
+ private static long nextlog = 0;
+
+ // =============== initialization ======================================
+ // =====================================================================
+
+ /** to prevent construction */
+ private EDSimulator() {
+ }
+
+ // ---------------------------------------------------------------------
+ // Private methods
+ // ---------------------------------------------------------------------
+
+ /**
+ * Load and run initializers.
+ */
+ private static void runInitializers() {
+
+ Object[] inits = Configuration.getInstanceArray(PAR_INIT);
+ String names[] = Configuration.getNames(PAR_INIT);
+
+ for (int i = 0; i < inits.length; ++i) {
+
+ System.err.println("- Running initializer " + names[i] + ": "
+ + inits[i].getClass());
+ ((Control) inits[i]).execute();
+ }
+ }
+
+ // --------------------------------------------------------------------
+
+ private static void scheduleControls() {
+ // load controls
+ String[] names = Configuration.getNames(PAR_CTRL);
+ controls = new Control[names.length];
+ ctrlSchedules = new Scheduler[names.length];
+ for (int i = 0; i < names.length; ++i) {
+ controls[i] = (Control) Configuration.getInstance(names[i]);
+ ctrlSchedules[i] = new Scheduler(names[i], false);
+ }
+ System.err.println("EDSimulator: loaded controls "
+ + Arrays.asList(names));
+
+ // Schedule controls execution
+ if (controls.length > heap.maxPriority() + 1)
+ throw new IllegalArgumentException("Too many control objects");
+ for (int i = 0; i < controls.length; i++) {
+ new ControlEvent(controls[i], ctrlSchedules[i], i);
+ }
+ }
+
+ // ---------------------------------------------------------------------
+
+ /**
+ * Adds a new event to be scheduled, specifying the number of time units of
+ * delay, and the execution order parameter.
+ *
+ * @param time
+ * The actual time at which the next event should be scheduled.
+ * @param order
+ * The index used to specify the order in which control events
+ * should be executed, if they happen to be at the same time,
+ * which is typically the case.
+ * @param event
+ * The control event
+ */
+ static void addControlEvent(long time, int order, ControlEvent event) {
+ // we don't check whether time is negative or in the past: we trust
+ // the caller, which must be from this package
+ if (time >= endtime)
+ return;
+ heap.add(time, event, null, (byte) 0, order);
+ }
+
+ // ---------------------------------------------------------------------
+
+ /**
+ * This method is used to check whether the current configuration can be
+ * used for event driven simulations. It checks for the existence of config
+ * parameter {@value #PAR_ENDTIME}.
+ */
+ public static final boolean isConfigurationEventDriven() {
+ return Configuration.contains(PAR_ENDTIME);
+ }
+
+ // ---------------------------------------------------------------------
+
+ /**
+ * Execute and remove the next event from the ordered event list.
+ *
+ * @return true if the execution should be stopped.
+ */
+ private static boolean executeNext() {
+ PriorityQ.Event ev = heap.removeFirst();
+ if (ev == null) {
+ System.err.println("EDSimulator: queue is empty, quitting"
+ + " at time " + CommonState.getTime());
+ return true;
+ }
+
+ long time = ev.time;
+
+ if (time >= nextlog) {
+ // System.err.println("Current time: " + time);
+ // seemingly complicated: to prevent overflow
+ while (time - nextlog >= logtime)
+ nextlog += logtime;
+ if (endtime - nextlog >= logtime)
+ nextlog += logtime;
+ else
+ nextlog = endtime;
+ }
+ if (time >= endtime) {
+ System.err.println("EDSimulator: reached end time, quitting,"
+ + " leaving " + heap.size()
+ + " unprocessed events in the queue");
+ return true;
+ }
+
+ CommonState.setTime(time);
+ int pid = ev.pid;
+ if (ev.node == null) {
+ // might be control event; handled through a special method
+ ControlEvent ctrl = null;
+ try {
+ ctrl = (ControlEvent) ev.event;
+ } catch (ClassCastException e) {
+ throw new RuntimeException(
+ "No destination specified (null) for event " + ev);
+ }
+ return ctrl.execute();
+ } else if (ev.node != Network.prototype && ev.node.isUp()) {
+ CommonState.setPid(pid);
+ CommonState.setNode(ev.node);
+ if (ev.event instanceof NextCycleEvent) {
+ NextCycleEvent nce = (NextCycleEvent) ev.event;
+ nce.execute();
+ } else {
+ EDProtocol prot = null;
+ try {
+ prot = (EDProtocol) ev.node.getProtocol(pid);
+ // System.out.println("prot "+prot.getClass().getName());
+ } catch (ClassCastException e) {
+ e.printStackTrace();
+ throw new IllegalArgumentException("Protocol "
+ + Configuration.lookupPid(pid)
+ + " does not implement EDProtocol; "
+ + ev.event.getClass());
+ }
+ prot.processEvent(ev.node, pid, ev.event);
+ }
+ }
+
+ return false;
+ }
+
+ // ---------------------------------------------------------------------
+ // Public methods
+ // ---------------------------------------------------------------------
+
+ /**
+ * Runs an experiment, resetting everything except the random seed.
+ */
+ public static void nextExperiment() {
+ // Reading parameter
+ if (Configuration.contains(PAR_PQ))
+ heap = (PriorityQ) Configuration.getInstance(PAR_PQ);
+ else
+ heap = new Heap();
+ endtime = Configuration.getLong(PAR_ENDTIME);
+ if (CommonState.getEndTime() < 0) // not initialized yet
+ CommonState.setEndTime(endtime);
+ if (heap.maxTime() < endtime)
+ throw new IllegalParameterException(PAR_ENDTIME,
+ "End time is too large: configured event queue only"
+ + " supports " + heap.maxTime());
+ logtime = Configuration.getLong(PAR_LOGTIME, Long.MAX_VALUE);
+
+ // initialization
+ System.err.println("EDSimulator: resetting");
+ CommonState.setPhase(CommonState.PHASE_UNKNOWN);
+ CommonState.setTime(0); // needed here
+ controls = null;
+ ctrlSchedules = null;
+ nextlog = 0;
+ Network.reset();
+ System.err.println("EDSimulator: running initializers");
+ runInitializers();
+ scheduleControls();
+ // Perform the actual simulation; executeNext() will tell when to
+ // stop.
+ boolean exit = false;
+ while (!exit) {
+ exit = executeNext();
+ }
+
+ // analysis after the simulation
+ CommonState.setPhase(CommonState.POST_SIMULATION);
+ for (int j = 0; j < controls.length; ++j) {
+ if (ctrlSchedules[j].fin)
+ controls[j].execute();
+ }
+
+ }
+
+ // ---------------------------------------------------------------------
+
+ /**
+ * Adds a new event to be scheduled, specifying the number of time units of
+ * delay, and the node and the protocol identifier to which the event will
+ * be delivered.
+ *
+ * @param delay
+ * The number of time units before the event is scheduled. Has to
+ * be non-negative.
+ * @param event
+ * The object associated to this event
+ * @param node
+ * The node associated to the event.
+ * @param pid
+ * The identifier of the protocol to which the event will be
+ * delivered
+ */
+ public static void add(long delay, Object event, Node node, int pid) {
+ //if (event instanceof NextCycleEvent)
+ //System.err.println("************* edsim delay="+delay +" pid="+pid+" event="+event+" time="+CommonState.getTime());
+ if (Simulator.getSimID() == 2){
+ PSGSimulator.add(delay, event, node, pid);
+ }
+
+ else {
+ if (delay < 0)
+ throw new IllegalArgumentException("Protocol "
+ + node.getProtocol(pid) + " is trying to add event "
+ + event + " with a negative delay: " + delay);
+ if (pid > Byte.MAX_VALUE)
+ throw new IllegalArgumentException(
+ "This version does not support more than "
+ + Byte.MAX_VALUE + " protocols");
+
+ long time = CommonState.getTime();
+ if (endtime - time > delay) // check like this to deal with overflow
+ heap.add(time + delay, event, node, (byte) pid);
+ }
+ }
+
+}