]> gerrit.simantics Code Review - simantics/platform.git/blob
2900fb07e3ee94538411c4db2ea8f9ff329f21c3
[simantics/platform.git] /
1 package org.simantics.simulation.sequences.action;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.List;
6 import java.util.PriorityQueue;
7
8 import org.simantics.scl.runtime.SCLContext;
9 import org.simantics.scl.runtime.function.Function1;
10 import org.simantics.scl.runtime.tuple.Tuple0;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13
14 public abstract class AbstractActionContext implements ActionContext {
15
16     private static final Logger LOGGER = LoggerFactory.getLogger(AbstractActionContext.class);
17
18     public static final double TIME_TOLERANCE = 1e-6;
19     
20     double currentTime;
21     volatile boolean stopped;
22     ArrayList<Function1<Tuple0, Object>> scheduledNow = new ArrayList<>();
23     ArrayList<Function1<Tuple0, Object>> scheduledNextStep = new ArrayList<>();
24     ArrayList<Function1<StopReason, Object>> scheduledWhenStopped = new ArrayList<>();
25     PriorityQueue<Task> scheduledAt = new PriorityQueue<>();
26
27         public List<Exception> exceptions; 
28     
29     private static class Task implements Comparable<Task> {
30         final double time;
31         final Function1<Tuple0, Object> continuation;
32         
33         public Task(double time, Function1<Tuple0, Object> continuation) {
34             this.time = time;
35             this.continuation = continuation;
36         }
37
38         @Override
39         public int compareTo(Task o) {
40             return Double.compare(time, o.time);
41         }
42     }
43     
44     @Override
45     public double time() {
46         return currentTime;
47     }
48     
49     @Override
50     public void scheduleNow(Function1<Tuple0, Object> continuation) {
51         scheduledNow.add(continuation);
52     }
53     
54     @Override
55     public void scheduleNextStep(Function1<Tuple0, Object> continuation) {
56         scheduledNextStep.add(continuation);
57     }
58     
59     @Override
60     public void scheduleAt(double time, Function1<Tuple0, Object> continuation) {
61         if(time <= currentTime)
62             scheduleNow(continuation);
63         else
64             scheduledAt.add(new Task(time, continuation));
65     }
66
67     @Override
68     public void scheduleWhenStopped(Function1<StopReason, Object> continuation) {
69         scheduledWhenStopped.add(continuation);
70     }
71
72     @Override
73     public void stop() {
74         stop(StopReason.STOPPED);
75     }
76
77     public void stop(StopReason reason) {
78         if (LOGGER.isTraceEnabled()) {
79             LOGGER.trace("stop context {}, reason={}", System.identityHashCode(this), reason, new Exception("trace"));
80         }
81         stopped = true;
82         handleStop(reason);
83     }
84
85     public boolean isStopped() {
86         synchronized (this) {
87                 return stopped;
88         }
89     }
90     
91     public double handleStep(double currentTime) {
92         synchronized (this) {
93                 if (stopped)
94                         return Double.POSITIVE_INFINITY;
95                 
96                 this.currentTime = currentTime;
97                 {
98                     ArrayList<Function1<Tuple0, Object>> temp = scheduledNow;
99                     scheduledNow = scheduledNextStep;
100                     scheduledNextStep = temp;
101                     Collections.reverse(scheduledNow);
102                 }
103                         
104                 SCLContext context = SCLContext.getCurrent();
105                 Object oldActionContext = context.put("sequenceAction", this);
106                 try {
107                     while(true) {
108                         while(!scheduledNow.isEmpty()) {
109                                 try {
110                                         Function1<Tuple0, Object> currentContinuation = scheduledNow.remove(scheduledNow.size()-1);
111                                     currentContinuation.apply(Tuple0.INSTANCE);
112                                         currentContinuation = null;
113                                 } catch (Exception e) {
114                                         if (this.exceptions == null)
115                                                 this.exceptions = new ArrayList<>();
116                                         this.exceptions.add(new RuntimeException("Action failure at " + currentTime + ": " + e.getMessage(), e));
117                                 }
118                         }
119                         Task firstTask = scheduledAt.peek();
120                         if(firstTask == null) {
121                             if (scheduledNextStep.isEmpty())
122                                 stopped = true;
123                             return Double.POSITIVE_INFINITY;
124                         } else if(firstTask.time > currentTime+TIME_TOLERANCE) {
125                             return firstTask.time;
126                         } else {
127                             scheduledAt.remove();
128                             firstTask.continuation.apply(Tuple0.INSTANCE);
129                         }
130                     }
131                 } finally {
132                     context.put("sequenceAction", oldActionContext);
133                 }
134         }
135     }
136
137     private void handleStop(StopReason reason) {
138         synchronized (this) {
139             List<Function1<StopReason, Object>> stopFunctions = new ArrayList<>(scheduledWhenStopped);
140             scheduledWhenStopped.clear();
141             
142             scheduledNextStep.clear();
143             scheduledAt.clear();
144
145             SCLContext context = SCLContext.getCurrent();
146             Object oldActionContext = context.put("sequenceAction", this);
147             try {
148                 stopFunctions.forEach(f -> {
149                     try {
150                         f.apply(reason);
151                     } catch (Exception e) {
152                         if (this.exceptions == null)
153                             this.exceptions = new ArrayList<>();
154                         this.exceptions.add(new RuntimeException("Stop action failure at " + currentTime + ": " + e.getMessage(), e));
155                     }
156                 });
157             } finally {
158                 context.put("sequenceAction", oldActionContext);
159             }
160         }
161     }
162 }