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 while(!inState(StandardExperimentStates.Running.class) && inActiveState()) {
156 synchronized(tasks) {
157 int ran = runTasks();
160 tasks.wait(Integer.MAX_VALUE);
161 } catch (InterruptedException e) {
171 if(runTimeNs >= endTimeNs && inActiveState())
172 changeState(StandardExperimentStates.STOPPED);
178 Thread executorThread = this;
180 Semaphore beginSyncExec = new Semaphore(0);
181 Semaphore endSyncExec = new Semaphore(0);
183 Runnable scheduleSyncExec = () -> {
184 beginSyncExec.release();
186 endSyncExec.acquire();
187 } catch (InterruptedException e) {
191 public int runTasks() {
192 ArrayList<Runnable> todo = new ArrayList<>();
193 synchronized(tasks) {
197 todo.forEach(Runnable::run);
201 public void queue(Runnable runnable) {
202 synchronized(tasks) {
208 public <T> T syncExec(Callable<T> callable) throws InterruptedException {
210 if(executorThread == Thread.currentThread()) {
212 return callable.call();
213 } catch (Throwable t) {
214 LOGGER.error("syncExec in current thread failed", t);
220 queue(scheduleSyncExec);
222 beginSyncExec.acquire();
223 Thread oldThread = executorThread;
224 executorThread = Thread.currentThread();
226 return callable.call();
227 } catch (Throwable t) {
228 LOGGER.error("syncExec failed", t);
231 executorThread = oldThread;
232 endSyncExec.release();
237 public void asyncExec(Runnable runnable) {
239 if(executorThread == Thread.currentThread()) {
242 } catch (Throwable t) {
243 LOGGER.error("asyncExec failed", t);
253 public void setSimulationStepNs(long ns) {
254 simulationStepNs = ns;
255 stepInSeconds = BigDecimal.valueOf(simulationStepNs).multiply(BigDecimal.valueOf(1e-9)).doubleValue();
258 public void runDuration(long ns) {
259 runStart = System.nanoTime();
262 synchronized(tasks) {
263 changeState(StandardExperimentStates.RUNNING);
268 public ExperimentState getExperimentState() {
272 public void changeState(ExperimentState state) {
274 fireStateChanged(state);
277 public void addListener(DynamicExperimentThreadListener listener) {
278 if(!listeners.contains(listener))
279 listeners.add(listener);
282 public void removeListener(DynamicExperimentThreadListener listener) {
283 listeners.remove(listener);
286 protected void fireAfterStep() {
287 listeners.forEach(DynamicExperimentThreadListener::afterStep);
290 protected void fireBeforeStep() {
291 listeners.forEach(DynamicExperimentThreadListener::beforeStep);
294 protected void fireStateChanged(ExperimentState newState) {
295 listeners.forEach(l -> l.stateChanged(newState));