PauseableTimeFunction now supports non-integral speed factors
[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                                         // always execute to keep timeline from "hanging behind" for too long
45                                         long current = tf.getAsLong();
46                                         timeline.executeUntil(timeline.laterThan(current), System.currentTimeMillis() + 10);
47                                         long nextEventTime = timeline.nextEventTime();
48                                         long sleepTime;
49                                         if (timeline.hasNext())
50                                                 sleepTime = tf.simulTimeDeltaToRealTimeMillis(nextEventTime - current);
51                                         else
52                                                 sleepTime = 10000;
53                                         try
54                                         {
55                                                 nextExecSimulTime.set(nextEventTime);
56                                                 if (sleepTime > 0)
57                                                         Thread.sleep(sleepTime);
58
59                                                 synchronized (isPaused)
60                                                 {
61                                                         while (isPaused.get())
62                                                                 isPaused.wait();
63                                                 }
64                                         }
65                                         catch (@SuppressWarnings("unused") InterruptedException e)
66                                         {// do nothing; it is normal execution flow to be interrupted
67                                         }
68                                 }
69                         }
70                         finally
71                         {
72                                 isRunningLive.set(false);
73                                 synchronized (isRunningLive)
74                                 {
75                                         isRunningLive.notify();
76                                 }
77                         }
78                 });
79                 timeline.addEventAddedListener(event ->
80                 {
81                         if (isRunningLive.get())
82                                 if (Timeline.timeCmp(event.getTiming(), nextExecSimulTime.get()) < 0)
83                                         simulationThread.interrupt();
84                 });
85         }
86
87         public void executeNextStep()
88         {
89                 timeline.executeNext();
90         }
91
92         public synchronized void startLiveExecution()
93         {
94                 if (shouldBeRunningLive.get())
95                         return;
96                 shouldBeRunningLive.set(true);
97                 simulationThread.start();
98                 waitForIsRunning(true);
99         }
100
101         public synchronized void stopLiveExecution()
102         {
103                 if (!shouldBeRunningLive.get())
104                         return;
105                 shouldBeRunningLive.set(false);
106                 simulationThread.interrupt();
107                 waitForIsRunning(false);
108         }
109
110         public void unpauseLiveExecution()
111         {
112                 synchronized (isPaused)
113                 {
114                         tf.unpause();
115                         isPaused.set(false);
116                         isPaused.notify();
117                 }
118         }
119
120         public void pauseLiveExecution()
121         {
122                 synchronized (isPaused)
123                 {
124                         tf.pause();
125                         isPaused.set(true);
126                 }
127         }
128
129         public boolean isPaused()
130         {
131                 return isPaused.get();
132         }
133
134         public void setSpeedFactor(double factor)
135         {
136                 tf.setSpeedFactor(factor);
137         }
138
139         private void waitForIsRunning(boolean expectedState)
140         {
141                 while (isRunningLive.get() ^ expectedState)
142                         try
143                         {
144                                 synchronized (isRunningLive)
145                                 {
146                                         isRunningLive.wait();
147                                 }
148                         }
149                         catch (@SuppressWarnings("unused") InterruptedException e)
150                         {// no need to do anything
151                         }
152         }
153 }