Merge branch 'development' of
[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), current + 10);
47                                         long sleepTime;
48                                         if (timeline.hasNext())
49                                                 sleepTime = timeline.nextEventTime() - current;
50                                         else
51                                                 sleepTime = 10000;
52                                         try
53                                         {
54                                                 nextExecSimulTime.set(current + sleepTime);
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         }
85
86         public void executeNextStep()
87         {
88                 timeline.executeNext();
89         }
90
91         public synchronized void startLiveExecution()
92         {
93                 if (shouldBeRunningLive.get())
94                         return;
95                 shouldBeRunningLive.set(true);
96                 simulationThread.start();
97                 waitForIsRunning(true);
98         }
99
100         public synchronized void stopLiveExecution()
101         {
102                 if (!shouldBeRunningLive.get())
103                         return;
104                 shouldBeRunningLive.set(false);
105                 simulationThread.interrupt();
106                 waitForIsRunning(false);
107         }
108
109         public void unpauseLiveExecution()
110         {
111                 synchronized (isPaused)
112                 {
113                         tf.unpause();
114                         isPaused.set(false);
115                         isPaused.notify();
116                 }
117         }
118
119         public void pauseLiveExecution()
120         {
121                 synchronized (isPaused)
122                 {
123                         tf.pause();
124                         isPaused.set(true);
125                 }
126         }
127
128         public boolean isPaused()
129         {
130                 return isPaused.get();
131         }
132
133         public void setSpeedPercentage(int percentage)
134         {
135                 tf.setSpeedPercentage(percentage);
136         }
137
138         private void waitForIsRunning(boolean expectedState)
139         {
140                 while (isRunningLive.get() ^ expectedState)
141                         try
142                         {
143                                 synchronized (isRunningLive)
144                                 {
145                                         isRunningLive.wait();
146                                 }
147                         }
148                         catch (@SuppressWarnings("unused") InterruptedException e)
149                         {// no need to do anything
150                         }
151         }
152 }