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())
86 if (Timeline.timeCmp(event.getTiming(), nextExecSimulTime.get()) < 0)
87 simulationThread.interrupt();
89 // not optimal; but we don't expect this to happen very often
90 tf.addSimulTimeToRealTimeFactorChangedListener(d -> simulationThread.interrupt());
93 public void executeNextStep()
95 timeline.executeNext();
98 public synchronized void startLiveExecution()
100 if (shouldBeRunningLive.get())
102 shouldBeRunningLive.set(true);
103 simulationThread.start();
104 waitForIsRunning(true);
107 public synchronized void stopLiveExecution()
109 if (!shouldBeRunningLive.get())
111 shouldBeRunningLive.set(false);
112 simulationThread.interrupt();
113 waitForIsRunning(false);
116 public void unpauseLiveExecution()
118 synchronized (isPaused)
126 public void pauseLiveExecution()
128 synchronized (isPaused)
135 public boolean isPaused()
137 return isPaused.get();
140 public double getSpeedFactor()
142 return tf.getSpeedFactor();
145 public void setSpeedFactor(double factor)
147 tf.setSpeedFactor(factor);
150 private void waitForIsRunning(boolean expectedState)
152 while (isRunningLive.get() ^ expectedState)
155 synchronized (isRunningLive)
157 isRunningLive.wait();
160 catch (@SuppressWarnings("unused") InterruptedException e)
161 {// no need to do anything