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