2 * Copyright (c) 2003-2005 The BISON Project
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2 as
6 * published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 package peersim.edsim;
21 import java.util.Arrays;
23 import peersim.Simulator;
24 import peersim.config.*;
25 import peersim.core.*;
26 import psgsim.PSGSimulator;
29 * Event-driven simulator engine. It is a fully static singleton class. For an
30 * event driven simulation the configuration has to describe a set of
31 * {@link Protocol}s, a set of {@link Control}s and their ordering and a set of
32 * initializers and their ordering. See parameters {@value #PAR_INIT},
35 * One experiment run by {@link #nextExperiment} works as follows. First the
36 * initializers are run in the specified order. Then the first execution of all
37 * specified controls is scheduled in the event queue. This scheduling is
38 * defined by the {@link Scheduler} parameters of each control component. After
39 * this, the first event is taken from the event queue. If the event wraps a
40 * control, the control is executed, otherwise the event is delivered to the
41 * destination protocol, that must implement {@link EDProtocol}. This is
42 * iterated while the current time is less than {@value #PAR_ENDTIME} or the
43 * queue becomes empty. If more control events fall at the same time point, then
44 * the order given in the configuration is respected. If more non-control events
45 * fall at the same time point, they are processed in a random order.
47 * The engine also provides the interface to add events to the queue. Note that
48 * this engine does not explicitly run the protocols. In all cases at least one
49 * control or initializer has to be defined that sends event(s) to protocols.
51 * Controls can be scheduled (using the {@link Scheduler} parameters in the
52 * configuration) to run after the experiment has finished. That is, each
53 * experiment is finished by running the controls that are scheduled to be run
54 * after the experiment.
56 * Any control can interrupt an experiment at any time it is executed by
57 * returning true in method {@link Control#execute}. However, the controls
58 * scheduled to run after the experiment are still executed completely,
59 * irrespective of their return value and even if the experiment was
62 * {@link CDScheduler} has to be mentioned that is a control that can bridge the
63 * gap between {@link peersim.cdsim} and the event driven engine. It can wrap
64 * {@link peersim.cdsim.CDProtocol} appropriately so that the execution of the
65 * cycles are scheduled in configurable ways for each node individually. In some
66 * cases this can add a more fine-grained control and more realism to
67 * {@link peersim.cdsim.CDProtocol} simulations, at the cost of some loss in
70 * When protocols at different nodes send messages to each other, they might
71 * want to use a model of the transport layer so that in the simulation message
72 * delay and message omissions can be modeled in a modular way. This
73 * functionality is implemented in package {@link peersim.transport}.
77 public class EDSimulator {
79 // ---------------------------------------------------------------------
81 // ---------------------------------------------------------------------
84 * The ending time for simulation. Only events that have a strictly smaller
85 * value are executed. It must be positive. Although in principle negative
86 * timestamps could be allowed, we assume time will be positive.
90 public static final String PAR_ENDTIME = "simulation.endtime";
93 * This parameter specifies how often the simulator should log the current
94 * time on the standard error. The time is logged only if there were events
95 * in the respective interval, and only the time of some actual event is
96 * printed. That is, the actual log is not guaranteed to happen in identical
97 * intervals of time. It is merely a way of seeing whether the simulation
98 * progresses and how fast...
102 private static final String PAR_LOGTIME = "simulation.logtime";
105 * This parameter specifies the event queue to be used. It must be an
106 * implementation of interface {@link PriorityQ}. If it is not defined, the
107 * internal implementation is used.
111 private static final String PAR_PQ = "simulation.eventqueue";
114 * This is the prefix for initializers. These have to be of type
115 * {@link Control}. They are run at the beginning of each experiment, in the
116 * order specified by the configuration.
122 private static final String PAR_INIT = "init";
125 * This is the prefix for {@link Control} components. They are run at the
126 * time points defined by the {@link Scheduler} associated to them. If some
127 * controls have to be executed at the same time point, they are executed in
128 * the order specified in the configuration.
133 private static final String PAR_CTRL = "control";
135 // ---------------------------------------------------------------------
137 // ---------------------------------------------------------------------
139 /** Maximum time for simulation */
140 private static long endtime;
143 private static long logtime;
145 /** holds the modifiers of this simulation */
146 private static Control[] controls = null;
148 /** Holds the control schedulers of this simulation */
149 private static Scheduler[] ctrlSchedules = null;
151 /** Ordered list of events (heap) */
152 private static PriorityQ heap = null;
154 private static long nextlog = 0;
156 // =============== initialization ======================================
157 // =====================================================================
159 /** to prevent construction */
160 private EDSimulator() {
163 // ---------------------------------------------------------------------
165 // ---------------------------------------------------------------------
168 * Load and run initializers.
170 private static void runInitializers() {
172 Object[] inits = Configuration.getInstanceArray(PAR_INIT);
173 String names[] = Configuration.getNames(PAR_INIT);
175 for (int i = 0; i < inits.length; ++i) {
177 System.err.println("- Running initializer " + names[i] + ": "
178 + inits[i].getClass());
179 ((Control) inits[i]).execute();
183 // --------------------------------------------------------------------
185 private static void scheduleControls() {
187 String[] names = Configuration.getNames(PAR_CTRL);
188 controls = new Control[names.length];
189 ctrlSchedules = new Scheduler[names.length];
190 for (int i = 0; i < names.length; ++i) {
191 controls[i] = (Control) Configuration.getInstance(names[i]);
192 ctrlSchedules[i] = new Scheduler(names[i], false);
194 System.err.println("EDSimulator: loaded controls "
195 + Arrays.asList(names));
197 // Schedule controls execution
198 if (controls.length > heap.maxPriority() + 1)
199 throw new IllegalArgumentException("Too many control objects");
200 for (int i = 0; i < controls.length; i++) {
201 new ControlEvent(controls[i], ctrlSchedules[i], i);
205 // ---------------------------------------------------------------------
208 * Adds a new event to be scheduled, specifying the number of time units of
209 * delay, and the execution order parameter.
212 * The actual time at which the next event should be scheduled.
214 * The index used to specify the order in which control events
215 * should be executed, if they happen to be at the same time,
216 * which is typically the case.
220 static void addControlEvent(long time, int order, ControlEvent event) {
221 // we don't check whether time is negative or in the past: we trust
222 // the caller, which must be from this package
225 heap.add(time, event, null, (byte) 0, order);
228 // ---------------------------------------------------------------------
231 * This method is used to check whether the current configuration can be
232 * used for event driven simulations. It checks for the existence of config
233 * parameter {@value #PAR_ENDTIME}.
235 public static final boolean isConfigurationEventDriven() {
236 return Configuration.contains(PAR_ENDTIME);
239 // ---------------------------------------------------------------------
242 * Execute and remove the next event from the ordered event list.
244 * @return true if the execution should be stopped.
246 private static boolean executeNext() {
247 PriorityQ.Event ev = heap.removeFirst();
249 System.err.println("EDSimulator: queue is empty, quitting"
250 + " at time " + CommonState.getTime());
256 if (time >= nextlog) {
257 // System.err.println("Current time: " + time);
258 // seemingly complicated: to prevent overflow
259 while (time - nextlog >= logtime)
261 if (endtime - nextlog >= logtime)
266 if (time >= endtime) {
267 System.err.println("EDSimulator: reached end time, quitting,"
268 + " leaving " + heap.size()
269 + " unprocessed events in the queue");
273 CommonState.setTime(time);
275 if (ev.node == null) {
276 // might be control event; handled through a special method
277 ControlEvent ctrl = null;
279 ctrl = (ControlEvent) ev.event;
280 } catch (ClassCastException e) {
281 throw new RuntimeException(
282 "No destination specified (null) for event " + ev);
284 return ctrl.execute();
285 } else if (ev.node != Network.prototype && ev.node.isUp()) {
286 CommonState.setPid(pid);
287 CommonState.setNode(ev.node);
288 if (ev.event instanceof NextCycleEvent) {
289 NextCycleEvent nce = (NextCycleEvent) ev.event;
292 EDProtocol prot = null;
294 prot = (EDProtocol) ev.node.getProtocol(pid);
295 // System.out.println("prot "+prot.getClass().getName());
296 } catch (ClassCastException e) {
298 throw new IllegalArgumentException("Protocol "
299 + Configuration.lookupPid(pid)
300 + " does not implement EDProtocol; "
301 + ev.event.getClass());
303 prot.processEvent(ev.node, pid, ev.event);
310 // ---------------------------------------------------------------------
312 // ---------------------------------------------------------------------
315 * Runs an experiment, resetting everything except the random seed.
317 public static void nextExperiment() {
319 if (Configuration.contains(PAR_PQ))
320 heap = (PriorityQ) Configuration.getInstance(PAR_PQ);
323 endtime = Configuration.getLong(PAR_ENDTIME);
324 if (CommonState.getEndTime() < 0) // not initialized yet
325 CommonState.setEndTime(endtime);
326 if (heap.maxTime() < endtime)
327 throw new IllegalParameterException(PAR_ENDTIME,
328 "End time is too large: configured event queue only"
329 + " supports " + heap.maxTime());
330 logtime = Configuration.getLong(PAR_LOGTIME, Long.MAX_VALUE);
333 System.err.println("EDSimulator: resetting");
334 CommonState.setPhase(CommonState.PHASE_UNKNOWN);
335 CommonState.setTime(0); // needed here
337 ctrlSchedules = null;
340 System.err.println("EDSimulator: running initializers");
343 // Perform the actual simulation; executeNext() will tell when to
345 boolean exit = false;
347 exit = executeNext();
350 // analysis after the simulation
351 CommonState.setPhase(CommonState.POST_SIMULATION);
352 for (int j = 0; j < controls.length; ++j) {
353 if (ctrlSchedules[j].fin)
354 controls[j].execute();
359 // ---------------------------------------------------------------------
362 * Adds a new event to be scheduled, specifying the number of time units of
363 * delay, and the node and the protocol identifier to which the event will
367 * The number of time units before the event is scheduled. Has to
370 * The object associated to this event
372 * The node associated to the event.
374 * The identifier of the protocol to which the event will be
377 public static void add(long delay, Object event, Node node, int pid) {
378 //if (event instanceof NextCycleEvent)
379 //System.err.println("************* edsim delay="+delay +" pid="+pid+" event="+event+" time="+CommonState.getTime());
380 if (Simulator.getSimID() == 2){
381 PSGSimulator.add(delay, event, node, pid);
386 throw new IllegalArgumentException("Protocol "
387 + node.getProtocol(pid) + " is trying to add event "
388 + event + " with a negative delay: " + delay);
389 if (pid > Byte.MAX_VALUE)
390 throw new IllegalArgumentException(
391 "This version does not support more than "
392 + Byte.MAX_VALUE + " protocols");
394 long time = CommonState.getTime();
395 if (endtime - time > delay) // check like this to deal with overflow
396 heap.add(time + delay, event, node, (byte) pid);