Skip to content

Commit 5da26ab

Browse files
authored
Remove optional parameters in Monitor (#747)
1 parent e629bad commit 5da26ab

File tree

7 files changed

+109
-60
lines changed

7 files changed

+109
-60
lines changed

Src/PCompiler/CompilerCore/Backend/Java/MachineGenerator.cs

+18-3
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,19 @@ private void WriteMonitorCstr()
252252

253253
foreach (var s in _currentMachine.States)
254254
{
255-
WriteStateBuilderDecl(s);
255+
WriteStateBuilderDecl(s, true);
256256
}
257257
WriteLine("} // constructor");
258+
WriteLine();
259+
260+
WriteLine($"public void reInitializeMonitor() {{");
261+
262+
foreach (var s in _currentMachine.States)
263+
{
264+
WriteStateBuilderDecl(s, false);
265+
}
266+
WriteLine("}");
267+
258268
}
259269

260270
private void WriteEventsAccessor()
@@ -271,9 +281,14 @@ private void WriteEventsAccessor()
271281
WriteLine("}");
272282
}
273283

274-
private void WriteStateBuilderDecl(State s)
284+
private void WriteStateBuilderDecl(State s, bool isConstructor)
275285
{
276-
WriteLine($"addState(prt.State.keyedOn({Names.IdentForState(s)})");
286+
if (isConstructor) {
287+
WriteLine($"addState(prt.State.keyedOn({Names.IdentForState(s)})");
288+
} else {
289+
WriteLine($"registerState(prt.State.keyedOn({Names.IdentForState(s)})");
290+
}
291+
277292
if (s.IsStart)
278293
{
279294
WriteLine($".isInitialState(true)");

Src/PRuntimes/PJavaRuntime/src/main/java/prt/Monitor.java

+35-30
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,22 @@
88
import org.apache.logging.log4j.Logger;
99
import org.apache.logging.log4j.Marker;
1010
import org.apache.logging.log4j.MarkerManager;
11-
import org.apache.logging.log4j.message.StringMapMessage;
1211
import prt.exceptions.*;
12+
import java.io.Serializable;
1313

1414
/**
1515
* A prt.Monitor encapsulates a state machine.
1616
*
1717
*/
18-
public abstract class Monitor<StateKey extends Enum<StateKey>> implements Consumer<PEvent<?>> {
19-
private final Logger logger = LogManager.getLogger(this.getClass());
18+
public abstract class Monitor<StateKey extends Enum<StateKey>> implements Consumer<PEvent<?>>, Serializable {
19+
private static final Logger logger = LogManager.getLogger(Monitor.class);
2020
private static final Marker PROCESSING_MARKER = MarkerManager.getMarker("EVENT_PROCESSING");
2121
private static final Marker TRANSITIONING_MARKER = MarkerManager.getMarker("STATE_TRANSITIONING");
2222

23-
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
24-
private Optional<State<StateKey>> startState;
25-
private State<StateKey> currentState;
23+
private StateKey startStateKey;
24+
private StateKey currentStateKey;
2625

27-
private EnumMap<StateKey, State<StateKey>> states; // All registered states
26+
private transient EnumMap<StateKey, State<StateKey>> states; // All registered states
2827
private StateKey[] stateUniverse; // all possible states
2928

3029
/**
@@ -44,6 +43,17 @@ protected void addState(State<StateKey> s) {
4443
throw new RuntimeException("prt.Monitor is already running; no new states may be added.");
4544
}
4645

46+
registerState(s);
47+
48+
if (s.isInitialState()) {
49+
if (startStateKey != null) {
50+
throw new RuntimeException("Initial state already set to " + startStateKey);
51+
}
52+
startStateKey = s.getKey();
53+
}
54+
}
55+
56+
protected void registerState(State<StateKey> s) {
4757
if (states == null) {
4858
states = new EnumMap<>((Class<StateKey>) s.getKey().getClass());
4959
stateUniverse = s.getKey().getDeclaringClass().getEnumConstants();
@@ -53,21 +63,14 @@ protected void addState(State<StateKey> s) {
5363
throw new RuntimeException("prt.State already present");
5464
}
5565
states.put(s.getKey(), s);
56-
57-
if (s.isInitialState()) {
58-
if (startState.isPresent()) {
59-
throw new RuntimeException("Initial state already set to " + startState.get().getKey());
60-
}
61-
startState = Optional.of(s);
62-
}
6366
}
6467

6568
public StateKey getCurrentState() {
6669
if (!isRunning) {
6770
throw new RuntimeException("prt.Monitor is not running (did you call ready()?)");
6871
}
6972

70-
return currentState.getKey();
73+
return currentStateKey;
7174
}
7275

7376
/**
@@ -139,10 +142,11 @@ public void accept(PEvent<?> p) throws UnhandledEventException {
139142
throw new RuntimeException("prt.Monitor is not running (did you call ready()?)");
140143
}
141144

142-
logger.info(PROCESSING_MARKER, new StringMapMessage().with("event", p));
145+
//logger.info(PROCESSING_MARKER, new StringMapMessage().with("event", p));
143146

144147
// XXX: We can technically avoid this downcast, but to fulfill the interface for Consumer<T>
145148
// this method cannot accept a type parameter, so this can't be a TransitionableConsumer<P>.
149+
State<StateKey> currentState = states.get(currentStateKey);
146150
Optional<State.TransitionableConsumer<Object>> oc = currentState.getHandler(p.getClass());
147151
if (oc.isEmpty()) {
148152
logger.atFatal().log(currentState + " missing event handler for " + p.getClass().getSimpleName());
@@ -157,19 +161,20 @@ public void accept(PEvent<?> p) throws UnhandledEventException {
157161
* entry handler, and updating internal bookkeeping.
158162
* @param s The new state.
159163
*/
160-
private <P> void handleTransition(State<StateKey> s, Optional<P> payload) {
164+
private <P> void handleTransition(State<StateKey> s, P payload) {
161165
if (!isRunning) {
162166
throw new RuntimeException("prt.Monitor is not running (did you call ready()?)");
163167
}
164168

165-
logger.info(TRANSITIONING_MARKER, new StringMapMessage().with("state", s));
169+
//logger.info(TRANSITIONING_MARKER, new StringMapMessage().with("state", s));
166170

171+
State<StateKey> currentState = states.get(currentStateKey);
167172
currentState.getOnExit().ifPresent(Runnable::run);
168173
currentState = s;
174+
currentStateKey = s.getKey();
169175

170176
currentState.getOnEntry().ifPresent(handler -> {
171-
Object p = payload.orElse(null);
172-
invokeWithTrampoline(handler, p);
177+
invokeWithTrampoline(handler, payload);
173178
});
174179
}
175180

@@ -199,7 +204,7 @@ private <P> void invokeWithTrampoline(State.TransitionableConsumer<P> handler, P
199204
* must be a handler of zero parameters, will be invoked.
200205
*/
201206
public void ready() {
202-
readyImpl(Optional.empty());
207+
readyImpl(null);
203208
}
204209

205210
/**
@@ -208,10 +213,10 @@ public void ready() {
208213
* @param payload The argument to the initial state's entry handler.
209214
*/
210215
public <P> void ready(P payload) {
211-
readyImpl(Optional.of(payload));
216+
readyImpl(payload);
212217
}
213218

214-
private <P> void readyImpl(Optional<P> payload) {
219+
private <P> void readyImpl(P payload) {
215220
if (isRunning) {
216221
throw new RuntimeException("prt.Monitor is already running.");
217222
}
@@ -224,27 +229,27 @@ private <P> void readyImpl(Optional<P> payload) {
224229

225230
isRunning = true;
226231

227-
currentState = startState.orElseThrow(() ->
228-
new RuntimeException(
229-
"No initial state set (did you specify an initial state, or is the machine halted?)"));
232+
currentStateKey = startStateKey;
233+
State<StateKey> currentState = states.get(currentStateKey);
230234

231235
currentState.getOnEntry().ifPresent(handler -> {
232-
Object p = payload.orElse(null);
233-
invokeWithTrampoline(handler, p);
236+
invokeWithTrampoline(handler, payload);
234237
});
235238
}
236239

237240
/**
238241
* Instantiates a new prt.Monitor; users should provide domain-specific functionality in a subclass.
239242
*/
240243
protected Monitor() {
241-
startState = Optional.empty();
244+
startStateKey = null;
242245
isRunning = false;
243246

244247
states = null; // We need a concrete class to instantiate an EnumMap; do this lazily on the first addState() call.
245-
currentState = null; // So long as we have not yet readied, this will be null!
248+
currentStateKey = null; // So long as we have not yet readied, this will be null!
246249
}
247250

248251
public abstract List<Class<? extends PEvent<?>>> getEventTypes();
249252

253+
public abstract void reInitializeMonitor();
254+
250255
}

Src/PRuntimes/PJavaRuntime/src/test/java/MonitorTest.java

+26-27
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.util.ArrayList;
99
import java.util.List;
10+
import java.util.Optional;
1011

1112
import static org.junit.jupiter.api.Assertions.*;
1213

@@ -25,6 +26,9 @@ public NoDefaultStateMonitor() {
2526
super();
2627
addState(new State.Builder<>(SingleState.INIT_STATE).build());
2728
}
29+
30+
public void reInitializeMonitor() {}
31+
2832
public List<Class<? extends PEvent<?>>> getEventTypes() { return List.of(); }
2933
}
3034

@@ -38,6 +42,8 @@ public MultipleDefaultStateMonitors() {
3842
addState(new State.Builder<>(BiState.OTHER_STATE).isInitialState(true).build());
3943
}
4044

45+
public void reInitializeMonitor() {}
46+
4147
public List<Class<? extends PEvent<?>>> getEventTypes() { return List.of(); }
4248
}
4349

@@ -50,6 +56,8 @@ public NonTotalStateMapMonitor() {
5056
addState(new State.Builder<>(BiState.INIT_STATE).isInitialState(true).build());
5157
}
5258

59+
public void reInitializeMonitor() {}
60+
5361
public List<Class<? extends PEvent<?>>> getEventTypes() { return List.of(); }
5462
}
5563
/**
@@ -62,6 +70,8 @@ public NonUniqueStateKeyMonitor() {
6270
addState(new State.Builder<>(BiState.INIT_STATE).isInitialState(true).build());
6371
}
6472

73+
public void reInitializeMonitor() {}
74+
6575
public List<Class<? extends PEvent<?>>> getEventTypes() { return List.of(); }
6676
}
6777

@@ -91,6 +101,8 @@ public CounterMonitor() {
91101
.build());
92102
}
93103

104+
public void reInitializeMonitor() {}
105+
94106
public List<Class<? extends PEvent<?>>> getEventTypes() { return List.of(); }
95107
}
96108

@@ -115,6 +127,8 @@ public ChainedEntryHandlerMonitor() {
115127
.build());
116128
}
117129

130+
public void reInitializeMonitor() {}
131+
118132
public List<Class<? extends PEvent<?>>> getEventTypes() { return List.of(); }
119133
}
120134

@@ -144,6 +158,8 @@ public GotoStateWithPayloadsMonitor() {
144158
.build());
145159
}
146160

161+
public void reInitializeMonitor() {}
162+
147163
public List<Class<? extends PEvent<?>>> getEventTypes() { return List.of(); }
148164
}
149165

@@ -174,6 +190,8 @@ public GotoStateWithPayloadsMonitorIncludingInitialEntryHandler() {
174190
.build());
175191
}
176192

193+
public void reInitializeMonitor() {}
194+
177195
public List<Class<? extends PEvent<?>>> getEventTypes() { return List.of(CounterMonitor.AddEvent.class); }
178196
}
179197

@@ -197,6 +215,8 @@ public GotoStateWithIllTypedPayloadsMonitor() {
197215
.build());
198216
}
199217

218+
public void reInitializeMonitor() {}
219+
200220
public List<Class<? extends PEvent<?>>> getEventTypes() { return List.of(); }
201221
}
202222

@@ -212,6 +232,8 @@ public ImmediateAssertionMonitor() {
212232
.build());
213233
}
214234

235+
public void reInitializeMonitor() {}
236+
215237
public List<Class<? extends PEvent<?>>> getEventTypes() { return List.of(); }
216238
}
217239

@@ -223,6 +245,8 @@ public class noopEvent extends PEvent<Void> {
223245
public Void getPayload() { return null; }
224246
}
225247

248+
public void reInitializeMonitor() {}
249+
226250
public List<Class<? extends PEvent<?>>> getEventTypes() { return List.of(testEvent.class, noopEvent.class); }
227251

228252
public RaiseEventMonitor() {
@@ -238,18 +262,6 @@ public RaiseEventMonitor() {
238262
}
239263
}
240264

241-
@Test
242-
@DisplayName("Monitors require exactly one default state")
243-
public void testDefaultStateConstruction() {
244-
Throwable e;
245-
246-
e = assertThrows(RuntimeException.class, () -> new NoDefaultStateMonitor().ready());
247-
assertTrue(e.getMessage().contains("No initial state set"));
248-
249-
e = assertThrows(RuntimeException.class, () -> new MultipleDefaultStateMonitors().ready());
250-
assertTrue(e.getMessage().contains("Initial state already set"));
251-
}
252-
253265
@Test
254266
@DisplayName("Monitors' state maps must be total")
255267
public void testTotalMonitorMap() {
@@ -301,7 +313,8 @@ public void testChainedEntryHandlersWithPayloads() {
301313
GotoStateWithPayloadsMonitor m = new GotoStateWithPayloadsMonitor();
302314
m.ready();
303315

304-
assertTrue(m.eventsProcessed.equals(List.of("Hello from prt.State A", "Hello from prt.State B")));
316+
assertTrue(m.eventsProcessed.equals(List.of(Optional.of("Hello from prt.State A"),
317+
Optional.of("Hello from prt.State B"))));
305318
}
306319

307320
@Test
@@ -312,20 +325,6 @@ public void testCantCallReadyTwice() {
312325
assertThrows(RuntimeException.class, () -> m.ready(), "prt.Monitor is already running.");
313326
}
314327

315-
316-
@Test
317-
@DisplayName("Payloads can be passed to entry handlers through ready()")
318-
public void testChainedEntryHandlersWithPayloadsIncludingInitialEntryHandler() {
319-
GotoStateWithPayloadsMonitorIncludingInitialEntryHandler m =
320-
new GotoStateWithPayloadsMonitorIncludingInitialEntryHandler();
321-
m.ready("Hello from the caller!");
322-
323-
assertTrue(m.eventsProcessed.equals(
324-
List.of("Hello from the caller!",
325-
"Hello from prt.State A",
326-
"Hello from prt.State B")));
327-
}
328-
329328
@Test
330329
@DisplayName("Event handlers consuuming arguments in ready() must consume them!")
331330
public void testInitialEntryHandlerMustHaveAnArg() {

Src/PRuntimes/PJavaRuntime/src/test/java/testcases/clientserver/PMachines.java

+22
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ public BankBalanceIsAlwaysCorrect() {
4747
.build());
4848
} // constructor
4949

50+
public void reInitializeMonitor() {
51+
registerState(prt.State.keyedOn(PrtStates.Init)
52+
.isInitialState(true)
53+
.withEvent(PEvents.eSpec_BankBalanceIsAlwaysCorrect_Init.class, p -> { Anon(p); gotoState(PrtStates.WaitForWithDrawReqAndResp); })
54+
.build());
55+
registerState(prt.State.keyedOn(PrtStates.WaitForWithDrawReqAndResp)
56+
.withEvent(PEvents.eWithDrawReq.class, this::Anon_1)
57+
.withEvent(PEvents.eWithDrawResp.class, this::Anon_2)
58+
.build());
59+
}
60+
5061
public java.util.List<Class<? extends prt.events.PEvent<?>>> getEventTypes() {
5162
return java.util.Arrays.asList(PEvents.eSpec_BankBalanceIsAlwaysCorrect_Init.class, PEvents.eWithDrawReq.class, PEvents.eWithDrawResp.class);
5263
}
@@ -227,6 +238,17 @@ public GuaranteedWithDrawProgress() {
227238
.build());
228239
} // constructor
229240

241+
public void reInitializeMonitor() {
242+
registerState(prt.State.keyedOn(PrtStates.NopendingRequests)
243+
.isInitialState(true)
244+
.withEvent(PEvents.eWithDrawReq.class, p -> { Anon_3(p); gotoState(PrtStates.PendingReqs); })
245+
.build());
246+
registerState(prt.State.keyedOn(PrtStates.PendingReqs)
247+
.withEvent(PEvents.eWithDrawResp.class, this::Anon_4)
248+
.withEvent(PEvents.eWithDrawReq.class, p -> { Anon_5(p); gotoState(PrtStates.PendingReqs); })
249+
.build());
250+
};
251+
230252
public java.util.List<Class<? extends prt.events.PEvent<?>>> getEventTypes() {
231253
return java.util.Arrays.asList(PEvents.eWithDrawReq.class, PEvents.eWithDrawResp.class);
232254
}

0 commit comments

Comments
 (0)