ModelComponentTestbench works again
[Mograsim.git] / plugins / net.mograsim.logic.model / src / net / mograsim / logic / model / LogicExecuter.java
1 package net.mograsim.logic.model;
2
3 import java.util.concurrent.atomic.AtomicBoolean;
4 import java.util.concurrent.atomic.AtomicLong;
5
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;
9
10 //TODO maybe move to logic core?
11 public class LogicExecuter
12 {
13         // TODO replace with CoreModel when it exists
14         private final Timeline timeline;
15
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;
21
22         PauseableTimeFunction tf;
23
24         public LogicExecuter(Timeline timeline)
25         {
26                 this.timeline = timeline;
27
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(() ->
35                 {
36                         isRunningLive.set(true);
37                         synchronized (isRunningLive)
38                         {
39                                 isRunningLive.notify();
40                         }
41                         try
42                         {
43                                 while (shouldBeRunningLive.get())
44                                 {
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();
52                                         long sleepTime;
53                                         if (timeline.hasNext())
54                                                 sleepTime = (long) ((nextEventTime - current) * tf.getSimulTimeToRealTimeFactor());
55                                         else
56                                                 sleepTime = 10000;
57                                         try
58                                         {
59                                                 nextExecSimulTime.set(nextEventTime);
60                                                 if (sleepTime > 0)
61                                                         Thread.sleep(sleepTime);
62
63                                                 synchronized (isPaused)
64                                                 {
65                                                         while (isPaused.get())
66                                                                 isPaused.wait();
67                                                 }
68                                         }
69                                         catch (@SuppressWarnings("unused") InterruptedException e)
70                                         { // do nothing; it is normal execution flow to be interrupted
71                                         }
72                                 }
73                         }
74                         finally
75                         {
76                                 isRunningLive.set(false);
77                                 synchronized (isRunningLive)
78                                 {
79                                         isRunningLive.notify();
80                                 }
81                         }
82                 });
83                 timeline.addEventAddedListener(event ->
84                 {
85                         if (isRunningLive.get())
86                         {
87                                 long nextExecSimulTime = this.nextExecSimulTime.get();
88                                 if (nextExecSimulTime == -1 || Timeline.timeCmp(event.getTiming(), nextExecSimulTime) < 0)
89                                         simulationThread.interrupt();
90                         }
91                 });
92                 // not optimal; but we don't expect this to happen very often
93                 tf.addSimulTimeToRealTimeFactorChangedListener(d -> simulationThread.interrupt());
94         }
95
96         public void executeNextStep()
97         {
98                 timeline.executeNext();
99         }
100
101         public synchronized void startLiveExecution()
102         {
103                 if (shouldBeRunningLive.get())
104                         return;
105                 shouldBeRunningLive.set(true);
106                 simulationThread.start();
107                 waitForIsRunning(true);
108         }
109
110         public synchronized void stopLiveExecution()
111         {
112                 if (!shouldBeRunningLive.get())
113                         return;
114                 shouldBeRunningLive.set(false);
115                 simulationThread.interrupt();
116                 waitForIsRunning(false);
117         }
118
119         public void unpauseLiveExecution()
120         {
121                 synchronized (isPaused)
122                 {
123                         tf.unpause();
124                         isPaused.set(false);
125                         isPaused.notify();
126                 }
127         }
128
129         public void pauseLiveExecution()
130         {
131                 synchronized (isPaused)
132                 {
133                         tf.pause();
134                         isPaused.set(true);
135                 }
136         }
137
138         public boolean isPaused()
139         {
140                 return isPaused.get();
141         }
142
143         public double getSpeedFactor()
144         {
145                 return tf.getSpeedFactor();
146         }
147
148         public void setSpeedFactor(double factor)
149         {
150                 tf.setSpeedFactor(factor);
151         }
152
153         private void waitForIsRunning(boolean expectedState)
154         {
155                 while (isRunningLive.get() ^ expectedState)
156                         try
157                         {
158                                 synchronized (isRunningLive)
159                                 {
160                                         isRunningLive.wait();
161                                 }
162                         }
163                         catch (@SuppressWarnings("unused") InterruptedException e)
164                         {// no need to do anything
165                         }
166         }
167 }