001/** 002 * This file is part of the Kompics P2P Framework. 003 * 004 * Copyright (C) 2009 Swedish Institute of Computer Science (SICS) Copyright (C) 005 * 2009 Royal Institute of Technology (KTH) 006 * 007 * Kompics is free software; you can redistribute it and/or modify it under the 008 * terms of the GNU General Public License as published by the Free Software 009 * Foundation; either version 2 of the License, or (at your option) any later 010 * version. 011 * 012 * This program is distributed in the hope that it will be useful, but WITHOUT 013 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 014 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 015 * details. 016 * 017 * You should have received a copy of the GNU General Public License along with 018 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 019 * Place - Suite 330, Boston, MA 02111-1307, USA. 020 */ 021package se.sics.kompics.simulator; 022 023import java.io.Serializable; 024import java.math.BigInteger; 025import java.security.AccessController; 026import java.security.PrivilegedAction; 027import java.util.Arrays; 028import java.util.HashSet; 029import java.util.LinkedList; 030import java.util.Random; 031import java.util.TimeZone; 032import javassist.ClassPool; 033import javassist.Loader; 034import javassist.LoaderClassPath; 035import javassist.Translator; 036import se.sics.kompics.ComponentDefinition; 037import se.sics.kompics.KompicsEvent; 038import se.sics.kompics.simulator.adaptor.ConcreteOperation; 039import se.sics.kompics.simulator.adaptor.Operation; 040import se.sics.kompics.simulator.adaptor.Operation1; 041import se.sics.kompics.simulator.adaptor.Operation2; 042import se.sics.kompics.simulator.adaptor.Operation3; 043import se.sics.kompics.simulator.adaptor.Operation4; 044import se.sics.kompics.simulator.adaptor.Operation5; 045import se.sics.kompics.simulator.adaptor.OperationGenerator; 046import se.sics.kompics.simulator.adaptor.distributions.BigIntegerExponentialDistribution; 047import se.sics.kompics.simulator.adaptor.distributions.BigIntegerNormalDistribution; 048import se.sics.kompics.simulator.adaptor.distributions.BigIntegerUniformDistribution; 049import se.sics.kompics.simulator.adaptor.distributions.ConstantDistribution; 050import se.sics.kompics.simulator.adaptor.distributions.Distribution; 051import se.sics.kompics.simulator.adaptor.distributions.DoubleExponentialDistribution; 052import se.sics.kompics.simulator.adaptor.distributions.DoubleNormalDistribution; 053import se.sics.kompics.simulator.adaptor.distributions.DoubleUniformDistribution; 054import se.sics.kompics.simulator.adaptor.distributions.LongExponentialDistribution; 055import se.sics.kompics.simulator.adaptor.distributions.LongNormalDistribution; 056import se.sics.kompics.simulator.adaptor.distributions.LongUniformDistribution; 057import se.sics.kompics.simulator.events.TakeSnapshot; 058import se.sics.kompics.simulator.instrumentation.CodeInterceptor; 059import se.sics.kompics.simulator.instrumentation.InstrumentationHelper; 060import se.sics.kompics.simulator.instrumentation.JarURLFixClassLoader; 061import se.sics.kompics.simulator.stochastic.events.StochasticProcessEvent; 062import se.sics.kompics.simulator.stochastic.events.StochasticProcessStartEvent; 063import se.sics.kompics.simulator.stochastic.events.StochasticProcessTerminatedEvent; 064import se.sics.kompics.simulator.stochastic.events.StochasticSimulationTerminatedEvent; 065import se.sics.kompics.simulator.stochastic.events.StochasticSimulatorEvent; 066import se.sics.kompics.simulator.stochastic.events.StochasticTakeSnapshotEvent; 067 068/** 069 * The <code>SimulationScenario</code> class. 070 * 071 * @author Cosmin Arad {@literal <[email protected]>} 072 * @version $Id$ 073 */ 074public abstract class SimulationScenario implements Serializable { 075 076 private static final long serialVersionUID = 5278102582431240537L; 077 078 private static long seed = 0l; 079 080 private static Random random = new Random(seed); 081 082 public static void setSeed(long seed) { 083 SimulationScenario.seed = seed; 084 SimulationScenario.random.setSeed(SimulationScenario.seed); 085 } 086 087 public static Random getRandom() { 088 return random; 089 } 090 091 private final LinkedList<StochasticProcess> processes = new LinkedList<>(); 092 private int processCount; 093 private StochasticSimulationTerminatedEvent terminatedEvent; 094 095 public SimulationScenario() { 096 processCount = 0; 097 } 098 099 protected abstract class StochasticProcess implements Serializable { 100 101 private static final long serialVersionUID = -6303689523381305745L; 102 103 private boolean relativeStartTime; 104 private long startTime; 105 private StochasticProcessStartEvent startEvent; 106 private StochasticProcessTerminatedEvent terminateEvent; 107 private StochasticProcessEvent stochasticEvent; 108 private Distribution<Long> interarrivalTime = null; 109 protected final LinkedList<OperationGenerator> generators = new LinkedList<>(); 110 private final String name; 111 private boolean started = false; 112 113 protected StochasticProcess(String name) { 114 this.name = name; 115 processCount++; 116 } 117 118 protected StochasticProcess() { 119 this("Process" + processCount); 120 } 121 122 @Deprecated 123 protected final void eventInterArrivalTime(Distribution<Long> interArrivalTime) { 124 this.eventInterarrivalTime(interArrivalTime); 125 } 126 127 protected final void eventInterarrivalTime(Distribution<Long> interarrivalTime) { 128 this.interarrivalTime = interarrivalTime; 129 } 130 131 protected final <E extends KompicsEvent> void raise(int count, Operation<E> op) { 132 if (count <= 0) { 133 throw new RuntimeException("Number of raised events must be strictly positive"); 134 } 135 OperationGenerator generator = new OperationGenerator(new ConcreteOperation<>(op), count); 136 generators.add(generator); 137 } 138 139 protected final <E extends KompicsEvent, P1 extends Number> void raise(int count, Operation1<E, P1> op1, 140 Distribution<P1> d1) { 141 if (count <= 0) { 142 throw new RuntimeException("Number of raised events must be strictly positive"); 143 } 144 OperationGenerator generator = new OperationGenerator(new ConcreteOperation<>(op1, d1), count); 145 generators.add(generator); 146 } 147 148 protected final <E extends KompicsEvent, P1 extends Number, P2 extends Number> void raise(int count, 149 Operation2<E, P1, P2> op2, Distribution<P1> d1, Distribution<P2> d2) { 150 if (count <= 0) { 151 throw new RuntimeException("Number of raised events must be strictly positive"); 152 } 153 OperationGenerator generator = new OperationGenerator(new ConcreteOperation<>(op2, d1, d2), count); 154 generators.add(generator); 155 } 156 157 protected final <E extends KompicsEvent, P1 extends Number, P2 extends Number, P3 extends Number> void raise( 158 int count, Operation3<E, P1, P2, P3> op3, Distribution<P1> d1, Distribution<P2> d2, 159 Distribution<P3> d3) { 160 if (count <= 0) { 161 throw new RuntimeException("Number of raised events must be strictly positive"); 162 } 163 OperationGenerator generator = new OperationGenerator(new ConcreteOperation<>(op3, d1, d2, d3), count); 164 generators.add(generator); 165 } 166 167 protected final <E extends KompicsEvent, P1 extends Number, P2 extends Number, P3 extends Number, P4 extends Number, P5 extends Number> void raise( 168 int count, Operation4<E, P1, P2, P3, P4> op4, Distribution<P1> d1, Distribution<P2> d2, 169 Distribution<P3> d3, Distribution<P4> d4) { 170 if (count <= 0) { 171 throw new RuntimeException("Number of raised events must be strictly positive"); 172 } 173 OperationGenerator generator = new OperationGenerator(new ConcreteOperation<>(op4, d1, d2, d3, d4), count); 174 generators.add(generator); 175 } 176 177 protected final <E extends KompicsEvent, P1 extends Number, P2 extends Number, P3 extends Number, P4 extends Number, P5 extends Number> void raise( 178 int count, Operation5<E, P1, P2, P3, P4, P5> op5, Distribution<P1> d1, Distribution<P2> d2, 179 Distribution<P3> d3, Distribution<P4> d4, Distribution<P5> d5) { 180 if (count <= 0) { 181 throw new RuntimeException("Number of raised events must be strictly positive"); 182 } 183 OperationGenerator generator = new OperationGenerator(new ConcreteOperation<>(op5, d1, d2, d3, d4, d5), 184 count); 185 generators.add(generator); 186 } 187 188 public final void start() { 189 relativeStartTime = false; 190 startTime = 0; 191 started = true; 192 terminateEvent = new StochasticProcessTerminatedEvent(0, new LinkedList<StochasticProcessStartEvent>(), 193 name); 194 stochasticEvent = new StochasticProcessEvent(0, interarrivalTime, terminateEvent, generators, name); 195 startEvent = new StochasticProcessStartEvent(startTime, new LinkedList<StochasticProcessStartEvent>(), 196 stochasticEvent, 0, name); 197 198 processes.remove(this); 199 processes.add(this); 200 } 201 202 public final void startAt(long time) { 203 relativeStartTime = false; 204 startTime = time; 205 started = true; 206 terminateEvent = new StochasticProcessTerminatedEvent(0, new LinkedList<StochasticProcessStartEvent>(), 207 name); 208 stochasticEvent = new StochasticProcessEvent(0, interarrivalTime, terminateEvent, generators, name); 209 startEvent = new StochasticProcessStartEvent(startTime, new LinkedList<StochasticProcessStartEvent>(), 210 stochasticEvent, 0, name); 211 212 processes.remove(this); 213 processes.add(this); 214 } 215 216 public final void startAtSameTimeWith(StochasticProcess process) { 217 relativeStartTime = true; 218 started = true; 219 startTime = 0; 220 terminateEvent = new StochasticProcessTerminatedEvent(0, new LinkedList<StochasticProcessStartEvent>(), 221 name); 222 stochasticEvent = new StochasticProcessEvent(0, interarrivalTime, terminateEvent, generators, name); 223 startEvent = new StochasticProcessStartEvent(startTime, new LinkedList<StochasticProcessStartEvent>(), 224 stochasticEvent, 0, name); 225 // we hook this process' start event to the referenced process' 226 // list of start events 227 if (!process.started) { 228 throw new RuntimeException(process.name + " not started"); 229 } 230 process.startEvent.getStartEvents().add(startEvent); 231 232 processes.remove(this); 233 processes.add(this); 234 } 235 236 public final void startAfterStartOf(long delay, StochasticProcess process) { 237 relativeStartTime = true; 238 started = true; 239 startTime = delay; 240 terminateEvent = new StochasticProcessTerminatedEvent(0, new LinkedList<StochasticProcessStartEvent>(), 241 name); 242 stochasticEvent = new StochasticProcessEvent(0, interarrivalTime, terminateEvent, generators, name); 243 startEvent = new StochasticProcessStartEvent(startTime, new LinkedList<StochasticProcessStartEvent>(), 244 stochasticEvent, 0, name); 245 // we hook this process' start event to the referenced process' 246 // list of start events 247 if (!process.started) { 248 throw new RuntimeException(process.name + " not started"); 249 } 250 process.startEvent.getStartEvents().add(startEvent); 251 252 processes.remove(this); 253 processes.add(this); 254 } 255 256 public final void startAfterTerminationOf(long delay, StochasticProcess... process) { 257 relativeStartTime = true; 258 started = true; 259 startTime = delay; 260 terminateEvent = new StochasticProcessTerminatedEvent(0, new LinkedList<StochasticProcessStartEvent>(), 261 name); 262 stochasticEvent = new StochasticProcessEvent(0, interarrivalTime, terminateEvent, generators, name); 263 startEvent = new StochasticProcessStartEvent(startTime, new LinkedList<StochasticProcessStartEvent>(), 264 stochasticEvent, process.length, name); 265 // we hook this process' start event to the referenced process' 266 // list of start events 267 HashSet<StochasticProcess> procs = new HashSet<>(Arrays.asList(process)); 268 for (StochasticProcess stochasticProcess : procs) { 269 if (!stochasticProcess.started) { 270 throw new RuntimeException(stochasticProcess.name + " not started"); 271 } 272 stochasticProcess.terminateEvent.getStartEvents().add(startEvent); 273 } 274 275 processes.remove(this); 276 processes.add(this); 277 } 278 } 279 280 protected final void terminateAt(long time) { 281 StochasticSimulationTerminatedEvent terminationEvent = new StochasticSimulationTerminatedEvent(time, 0, false); 282 terminatedEvent = terminationEvent; 283 } 284 285 protected final void terminateAfterTerminationOf(long delay, StochasticProcess... process) { 286 HashSet<StochasticProcess> procs = new HashSet<>(Arrays.asList(process)); 287 StochasticSimulationTerminatedEvent terminationEvent = new StochasticSimulationTerminatedEvent(delay, 288 procs.size(), true); 289 terminatedEvent = terminationEvent; 290 for (StochasticProcess stochasticProcess : procs) { 291 if (!stochasticProcess.started) { 292 throw new RuntimeException(stochasticProcess.name + " not started"); 293 } 294 stochasticProcess.terminateEvent.setTerminationEvent(terminationEvent); 295 } 296 } 297 298 protected final static class Snapshot { 299 300 private final TakeSnapshot takeSnapshotEvent; 301 302 public Snapshot(TakeSnapshot takeSnapshotEvent) { 303 this.takeSnapshotEvent = takeSnapshotEvent; 304 } 305 306 public void takeAfterTerminationOf(long delay, StochasticProcess... process) { 307 HashSet<StochasticProcess> procs = new HashSet<>(Arrays.asList(process)); 308 StochasticTakeSnapshotEvent snapshotEvent = new StochasticTakeSnapshotEvent(delay, takeSnapshotEvent, 309 procs.size()); 310 for (StochasticProcess stochasticProcess : procs) { 311 stochasticProcess.terminateEvent.setSnapshotEvent(snapshotEvent); 312 } 313 } 314 } 315 316 protected final Snapshot snapshot(TakeSnapshot takeSnapshotEvent) { 317 return new Snapshot(takeSnapshotEvent); 318 } 319 320 // ************************************************************************** 321 public final void simulate(Class<? extends ComponentDefinition> main) { 322 simulate(main, new CodeInterceptor(null, false)); 323 } 324 325 public final void simulate(Class<? extends ComponentDefinition> main, boolean allowThreads) { 326 simulate(main, new CodeInterceptor(null, allowThreads)); 327 } 328 329 public final void simulate(Class<? extends ComponentDefinition> main, Translator t) { 330 InstrumentationHelper.store(this); 331 final ClassLoader tcxtl = Thread.currentThread().getContextClassLoader(); 332 final ClassLoader fixedCL = new JarURLFixClassLoader(tcxtl); 333 final LoaderClassPath lcp = new LoaderClassPath(fixedCL); 334 final ClassPool cp = ClassPool.getDefault(); 335 cp.insertClassPath(lcp); 336 337 try { 338 Loader cl = AccessController.doPrivileged(new PrivilegedAction<Loader>() { 339 @Override 340 public Loader run() { 341 return new Loader(tcxtl, cp); 342 } 343 }); 344 cl.delegateLoadingOf("jdk.internal.misc.Unsafe"); 345 cl.delegateLoadingOf("jdk.internal.reflect.MethodAccessorImpl"); // needed for Mockito#mock 346 cl.delegateLoadingOf("jdk.internal.reflect.ConstructorAccessorImpl"); 347 cl.delegateLoadingOf("jdk.internal.reflect.SerializationConstructorAccessorImpl"); 348 cl.addTranslator(cp, t); 349 Thread.currentThread().setContextClassLoader(cl); 350 TimeZone.setDefault(TimeZone.getTimeZone("GMT")); 351 cl.run(main.getCanonicalName(), null); 352 } catch (Throwable e) { 353 throw new RuntimeException("Exception caught during simulation", e); 354 } finally { 355 Thread.currentThread().setContextClassLoader(tcxtl); // reset loader after simulation 356 } 357 } 358 359 public static SimulationScenario load(String scenarioFile) { 360 return InstrumentationHelper.load(scenarioFile); 361 } 362 363 // ************************************************************************** 364 public final LinkedList<StochasticSimulatorEvent> generateEventList() { 365 LinkedList<StochasticSimulatorEvent> eventList = new LinkedList<>(); 366 int started = 0; 367 for (StochasticProcess process : processes) { 368 if (!process.relativeStartTime) { 369 eventList.add(process.startEvent); 370 started++; 371 } 372 } 373 if (started == 0) { 374 throw new RuntimeException("Processes have circular relative start times"); 375 } 376 if (terminatedEvent != null && !terminatedEvent.isRelativeTime()) { 377 eventList.add(terminatedEvent); 378 } 379 return eventList; 380 } 381 382 protected final Distribution<Double> constant(double value) { 383 return new ConstantDistribution<>(Double.class, value); 384 } 385 386 protected final Distribution<Long> constant(long value) { 387 return new ConstantDistribution<>(Long.class, value); 388 } 389 390 protected final Distribution<BigInteger> constant(BigInteger value) { 391 return new ConstantDistribution<>(BigInteger.class, value); 392 } 393 394 protected final Distribution<Double> uniform(double min, double max) { 395 return new DoubleUniformDistribution(min, max, random); 396 } 397 398 protected final Distribution<Long> uniform(long min, long max) { 399 return new LongUniformDistribution(min, max, random); 400 } 401 402 protected final Distribution<BigInteger> uniform(BigInteger min, BigInteger max) { 403 return new BigIntegerUniformDistribution(min, max, random); 404 } 405 406 protected final Distribution<BigInteger> uniform(int numBits) { 407 return new BigIntegerUniformDistribution(numBits, random); 408 } 409 410 protected final Distribution<Double> exponential(double mean) { 411 return new DoubleExponentialDistribution(mean, random); 412 } 413 414 protected final Distribution<Long> exponential(long mean) { 415 return new LongExponentialDistribution(mean, random); 416 } 417 418 protected final Distribution<BigInteger> exponential(BigInteger mean) { 419 return new BigIntegerExponentialDistribution(mean, random); 420 } 421 422 protected final Distribution<Double> normal(double mean, double variance) { 423 return new DoubleNormalDistribution(mean, variance, random); 424 } 425 426 protected final Distribution<Long> normal(long mean, long variance) { 427 return new LongNormalDistribution(mean, variance, random); 428 } 429 430 protected final Distribution<BigInteger> normal(BigInteger mean, BigInteger variance) { 431 return new BigIntegerNormalDistribution(mean, variance, random); 432 } 433}