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}