52adeba0b44968fced83828086fcb2681171de1a
[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                                 if (Timeline.timeCmp(event.getTiming(), nextExecSimulTime.get()) < 0)
87                                         simulationThread.interrupt();
88                 });
89                 // not optimal; but we don't expect this to happen very often
90                 tf.addSimulTimeToRealTimeFactorChangedListener(d -> simulationThread.interrupt());
91         }
92
93         public void executeNextStep()
94         {
95                 timeline.executeNext();
96         }
97
98         public synchronized void startLiveExecution()
99         {
100                 if (shouldBeRunningLive.get())
101                         return;
102                 shouldBeRunningLive.set(true);
103                 simulationThread.start();
104                 waitForIsRunning(true);
105         }
106
107         public synchronized void stopLiveExecution()
108         {
109                 if (!shouldBeRunningLive.get())
110                         return;
111                 shouldBeRunningLive.set(false);
112                 simulationThread.interrupt();
113                 waitForIsRunning(false);
114         }
115
116         public void unpauseLiveExecution()
117         {
118                 synchronized (isPaused)
119                 {
120                         tf.unpause();
121                         isPaused.set(false);
122                         isPaused.notify();
123                 }
124         }
125
126         public void pauseLiveExecution()
127         {
128                 synchronized (isPaused)
129                 {
130                         tf.pause();
131                         isPaused.set(true);
132                 }
133         }
134
135         public boolean isPaused()
136         {
137                 return isPaused.get();
138         }
139
140         public double getSpeedFactor()
141         {
142                 return tf.getSpeedFactor();
143         }
144
145         public void setSpeedFactor(double factor)
146         {
147                 tf.setSpeedFactor(factor);
148         }
149
150         private void waitForIsRunning(boolean expectedState)
151         {
152                 while (isRunningLive.get() ^ expectedState)
153                         try
154                         {
155                                 synchronized (isRunningLive)
156                                 {
157                                         isRunningLive.wait();
158                                 }
159                         }
160                         catch (@SuppressWarnings("unused") InterruptedException e)
161                         {// no need to do anything
162                         }
163         }
164 }