1 | package de.uka.ipd.sdq.simulation.abstractsimengine; |
2 | |
3 | import java.util.ArrayList; |
4 | import java.util.Observer; |
5 | import java.util.concurrent.atomic.AtomicBoolean; |
6 | |
7 | import org.apache.log4j.Logger; |
8 | |
9 | /** |
10 | * @author Steffen Becker (this code has been factored out from SimuCom) |
11 | * @author Philipp Merkle |
12 | */ |
13 | public abstract class AbstractExperiment implements ISimulationControl { |
14 | |
15 | private static Logger logger = Logger.getLogger(AbstractExperiment.class); |
16 | |
17 | protected final IEvent STOP_EVENT = new StopEvent(); |
18 | protected final IEvent CHECK_EVENT = new CheckEvent(); |
19 | |
20 | private final ArrayList<SimCondition> stopConditions = new ArrayList<SimCondition>(); |
21 | private final ArrayList<Observer> timeObservers = new ArrayList<Observer>(); |
22 | private final AtomicBoolean isRunning = new AtomicBoolean(); |
23 | private double lastNotificationTime = 0.0; |
24 | |
25 | protected final ISimulationModel model; |
26 | |
27 | public AbstractExperiment(ISimulationModel model) { |
28 | this.model = model; |
29 | } |
30 | |
31 | public void setMaxSimTime(final long simTime) { |
32 | scheduleEvent(new StopEvent(), simTime); |
33 | } |
34 | |
35 | public void addStopCondition(final SimCondition condition) { |
36 | this.stopConditions.add(condition); |
37 | } |
38 | |
39 | public void start() { |
40 | this.isRunning.set(true); |
41 | |
42 | // schedule initial events |
43 | this.model.init(); |
44 | |
45 | // start the simulator |
46 | final double start = System.nanoTime(); |
47 | logger.warn("Starting simulation..."); |
48 | startSimulator(); |
49 | |
50 | // the simulation has stopped, print a log message |
51 | logger.warn("Simulation terminated. Took " + ((System.nanoTime() - start) / Math.pow(10, 9)) |
52 | + " real time seconds."); |
53 | } |
54 | |
55 | public void stop() { |
56 | // isRunning indicates that this method has been already executed. |
57 | // The method must only be executed once, so we use an atomic operation |
58 | // to avoid multiple accesses. Setting isRunning to false allows all |
59 | // processes to clean up. |
60 | if (this.isRunning.compareAndSet(true, false)) { |
61 | logger.info("Simulation stop requested!"); |
62 | |
63 | // This method MUST be called before all resources are deactivated, |
64 | // otherwise new threads might request processing time after the |
65 | // resource has been removed. These threads will not be cleaned up |
66 | // correctly. |
67 | stopSimulator(); |
68 | |
69 | this.model.finalise(); |
70 | } else { |
71 | logger.warn("Tried to stop the simulation, which has already been stopped."); |
72 | } |
73 | |
74 | } |
75 | |
76 | public boolean isRunning() { |
77 | return this.isRunning.get(); |
78 | } |
79 | |
80 | public void checkStopConditions() { |
81 | if (this.lastNotificationTime != this.getCurrentSimulationTime()) { |
82 | this.lastNotificationTime = this.getCurrentSimulationTime(); |
83 | for (final Observer o : this.timeObservers) { |
84 | o.update(null, this.lastNotificationTime); |
85 | } |
86 | } |
87 | for (final SimCondition c : this.stopConditions) { |
88 | if (c.check()) { |
89 | scheduleEvent(STOP_EVENT, 0); |
90 | return; |
91 | } |
92 | } |
93 | } |
94 | |
95 | public void addTimeObserver(final Observer observer) { |
96 | this.timeObservers.add(observer); |
97 | } |
98 | |
99 | public abstract void scheduleEvent(final IEvent event, final double delay); |
100 | |
101 | public abstract void startSimulator(); |
102 | |
103 | public abstract void stopSimulator(); |
104 | |
105 | protected class StopEvent implements IEvent { |
106 | |
107 | @Override |
108 | public void run() { |
109 | if (AbstractExperiment.this.isRunning()) { |
110 | logger.debug("Executing Stop Event"); |
111 | AbstractExperiment.this.stop(); |
112 | } |
113 | } |
114 | |
115 | } |
116 | |
117 | protected class CheckEvent implements IEvent { |
118 | |
119 | @Override |
120 | public void run() { |
121 | AbstractExperiment.this.checkStopConditions(); |
122 | if (model.getSimulationControl().isRunning()) { |
123 | scheduleEvent(this, 1); |
124 | } |
125 | } |
126 | |
127 | } |
128 | |
129 | protected interface IEvent { |
130 | |
131 | public void run(); |
132 | |
133 | } |
134 | |
135 | } |