1 package org.simantics.simulator.toolkit;
3 import java.math.BigDecimal;
4 import java.util.ArrayList;
5 import java.util.concurrent.Callable;
6 import java.util.concurrent.CopyOnWriteArrayList;
7 import java.util.concurrent.Semaphore;
9 import org.simantics.simulator.ExperimentState;
10 import org.slf4j.Logger;
11 import org.slf4j.LoggerFactory;
14 * @author Antti Villberg
17 abstract public class DynamicExperimentThread extends Thread {
19 private static final Logger LOGGER = LoggerFactory.getLogger(DynamicExperimentThread.class);
21 private CopyOnWriteArrayList<DynamicExperimentThreadListener> listeners = new CopyOnWriteArrayList<>();
23 private ExperimentState state = StandardExperimentStates.CREATED;
25 private long runStart = 0;
27 private double desiredRealtimeRatio = 1000.0;
28 private double obtainedRealtimeRatio = 1.0;
30 private long runTimeNs = 0;
31 private long endTimeNs = 0;
32 protected long simulationStepNs = 0;
33 protected double stepInSeconds = 1.0;
35 public DynamicExperimentThread() {
38 private void updateTimes() {
39 long time = System.nanoTime();
40 long elapsed = time-runStart;
42 obtainedRealtimeRatio = longToDoubleDivision(runTimeNs, elapsed);
48 protected double longToDoubleDivision(long l1, long l2) {
51 double d = ((double)rt_l)/((double)l2);
56 protected ArrayList<Runnable> tasks = new ArrayList<>();
58 abstract public void step(double stepLengthNanoSeconds);
60 public boolean inState(Class<? extends ExperimentState> state) {
61 return state.isInstance(this.state);
64 public void initialize() throws Exception {
67 public void deinitialize() throws Exception {
84 } catch (Exception e) {
85 LOGGER.error("Unhandled exception while running simulation thread", e);
88 } catch (Exception e) {
89 LOGGER.error("Unhandled exception while initializing simulation thread", e);
96 } catch (Exception e) {
97 LOGGER.error("Error while deinitializing simulation thread", e);
104 protected boolean inActiveState() {
106 inState(StandardExperimentStates.Disposed.class)
107 || inState(StandardExperimentStates.Disposing.class)
108 //|| inState(StandardExperimentStates.Failure.class)
109 || inState(StandardExperimentStates.ToBeDisposed.class)
113 private void runReally() {
115 while(inActiveState()) {
117 if(inState(StandardExperimentStates.Running.class)) {
119 long asd = System.nanoTime();
120 step(simulationStepNs);
121 stepTime += System.nanoTime() - asd;
122 runTimeNs += simulationStepNs;
124 long asd2 = System.nanoTime();
126 taskTime += System.nanoTime() - asd2;
128 //System.err.println(" st = " + 1e-9*stepTime + " tt = " + 1e-9*taskTime);
130 while(obtainedRealtimeRatio > desiredRealtimeRatio) {
131 int ran = runTasks();
133 long elapsed = System.nanoTime()-runStart;
134 long deltaNs = BigDecimal.valueOf(runTimeNs).divide(BigDecimal.valueOf(desiredRealtimeRatio)).longValue() - elapsed;
136 long deltaMs = deltaNs / 1000000;
137 int deltaNsRem = (int)(deltaNs % 1000000);
140 synchronized(tasks) {
142 tasks.wait(deltaMs, deltaNsRem);
143 } catch (InterruptedException e) {
154 synchronized(tasks) {
155 while(!inState(StandardExperimentStates.Running.class) && inActiveState()) {
156 int ran = runTasks();
159 tasks.wait(Integer.MAX_VALUE);
160 } catch (InterruptedException e) {
170 if(runTimeNs >= endTimeNs && inActiveState())
171 changeState(StandardExperimentStates.STOPPED);
177 Thread executorThread = this;
179 Semaphore beginSyncExec = new Semaphore(0);
180 Semaphore endSyncExec = new Semaphore(0);
182 Runnable scheduleSyncExec = () -> {
183 beginSyncExec.release();
185 endSyncExec.acquire();
186 } catch (InterruptedException e) {
190 public int runTasks() {
191 ArrayList<Runnable> todo = new ArrayList<>();
192 synchronized(tasks) {
196 todo.forEach(Runnable::run);
200 public void queue(Runnable runnable) {
201 synchronized(tasks) {
207 public <T> T syncExec(Callable<T> callable) throws InterruptedException {
209 if(executorThread == Thread.currentThread()) {
211 return callable.call();
212 } catch (Throwable t) {
213 LOGGER.error("syncExec in current thread failed", t);
219 queue(scheduleSyncExec);
221 beginSyncExec.acquire();
222 Thread oldThread = executorThread;
223 executorThread = Thread.currentThread();
225 return callable.call();
226 } catch (Throwable t) {
227 LOGGER.error("syncExec failed", t);
230 executorThread = oldThread;
231 endSyncExec.release();
236 public void asyncExec(Runnable runnable) {
238 if(executorThread == Thread.currentThread()) {
241 } catch (Throwable t) {
242 LOGGER.error("asyncExec failed", t);
252 public void setSimulationStepNs(long ns) {
253 simulationStepNs = ns;
254 stepInSeconds = BigDecimal.valueOf(simulationStepNs).multiply(BigDecimal.valueOf(1e-9)).doubleValue();
257 public void runDuration(long ns) {
258 runStart = System.nanoTime();
261 synchronized(tasks) {
262 changeState(StandardExperimentStates.RUNNING);
267 public ExperimentState getExperimentState() {
271 public void changeState(ExperimentState state) {
273 fireStateChanged(state);
276 public void addListener(DynamicExperimentThreadListener listener) {
277 if(!listeners.contains(listener))
278 listeners.add(listener);
281 public void removeListener(DynamicExperimentThreadListener listener) {
282 listeners.remove(listener);
285 protected void fireAfterStep() {
286 listeners.forEach(DynamicExperimentThreadListener::afterStep);
289 protected void fireBeforeStep() {
290 listeners.forEach(DynamicExperimentThreadListener::beforeStep);
293 protected void fireStateChanged(ExperimentState newState) {
294 listeners.forEach(l -> l.stateChanged(newState));