1 package net.mograsim.logic.model;
3 import java.util.concurrent.atomic.AtomicBoolean;
4 import java.util.concurrent.atomic.AtomicLong;
6 import net.mograsim.logic.core.timeline.PauseableTimeFunction;
7 import net.mograsim.logic.core.timeline.Timeline;
8 import net.mograsim.logic.core.timeline.Timeline.ExecutionResult;
10 //TODO maybe move to logic core?
11 public class LogicExecuter
13 // TODO replace with CoreModel when it exists
14 private final Timeline timeline;
16 private final AtomicBoolean shouldBeRunningLive;
17 private final AtomicBoolean isRunningLive;
18 private final AtomicBoolean isPaused;
19 private final AtomicLong nextExecSimulTime;
20 private final Thread simulationThread;
22 PauseableTimeFunction tf;
24 public LogicExecuter(Timeline timeline)
26 this.timeline = timeline;
28 tf = new PauseableTimeFunction();
29 timeline.setTimeFunction(tf);
30 shouldBeRunningLive = new AtomicBoolean();
31 isRunningLive = new AtomicBoolean();
32 isPaused = new AtomicBoolean();
33 nextExecSimulTime = new AtomicLong();
34 simulationThread = new Thread(() ->
36 isRunningLive.set(true);
37 synchronized (isRunningLive)
39 isRunningLive.notify();
43 while (shouldBeRunningLive.get())
45 long current = tf.getTime();
46 // The tf.isPaused() condition is justified, because timeline.getSimulationTime() returns the timestamp of the last
47 // processed event during executeUntil()
48 if (timeline.executeUntil(() -> timeline.laterThan(current).getAsBoolean() || tf.isPaused(),
49 System.currentTimeMillis() + 10) == ExecutionResult.EXEC_OUT_OF_TIME)
50 timeline.synchTime(); // TODO: should this also be called if tf.isPaused() condition is met?
51 long nextEventTime = timeline.nextEventTime();
53 if (timeline.hasNext())
54 sleepTime = (long) ((nextEventTime - current) * tf.getSimulTimeToRealTimeFactor());
59 nextExecSimulTime.set(nextEventTime);
61 Thread.sleep(sleepTime);
63 synchronized (isPaused)
65 while (isPaused.get())
69 catch (@SuppressWarnings("unused") InterruptedException e)
70 { // do nothing; it is normal execution flow to be interrupted
76 isRunningLive.set(false);
77 synchronized (isRunningLive)
79 isRunningLive.notify();
83 timeline.addEventAddedListener(event ->
85 if (isRunningLive.get())
87 long nextExecSimulTime = this.nextExecSimulTime.get();
88 if (nextExecSimulTime == -1 || Timeline.timeCmp(event.getTiming(), nextExecSimulTime) < 0)
89 simulationThread.interrupt();
92 // not optimal; but we don't expect this to happen very often
93 tf.addSimulTimeToRealTimeFactorChangedListener(d -> simulationThread.interrupt());
96 public void executeNextStep()
98 timeline.executeNext();
101 public synchronized void startLiveExecution()
103 if (shouldBeRunningLive.get())
105 shouldBeRunningLive.set(true);
106 simulationThread.start();
107 waitForIsRunning(true);
110 public synchronized void stopLiveExecution()
112 if (!shouldBeRunningLive.get())
114 shouldBeRunningLive.set(false);
115 simulationThread.interrupt();
116 waitForIsRunning(false);
119 public void unpauseLiveExecution()
121 synchronized (isPaused)
129 public void pauseLiveExecution()
131 synchronized (isPaused)
138 public boolean isPaused()
140 return isPaused.get();
143 public double getSpeedFactor()
145 return tf.getSpeedFactor();
148 public void setSpeedFactor(double factor)
150 tf.setSpeedFactor(factor);
153 private void waitForIsRunning(boolean expectedState)
155 while (isRunningLive.get() ^ expectedState)
158 synchronized (isRunningLive)
160 isRunningLive.wait();
163 catch (@SuppressWarnings("unused") InterruptedException e)
164 {// no need to do anything