From 5da26ab7d99691cc27a0e262c065d8b3652617c8 Mon Sep 17 00:00:00 2001 From: mchadalavada <140562632+mchadalavada@users.noreply.github.com> Date: Fri, 28 Jun 2024 18:47:13 -0700 Subject: [PATCH] Remove optional parameters in Monitor (#747) --- .../Backend/Java/MachineGenerator.cs | 21 +++++- .../src/main/java/prt/Monitor.java | 65 ++++++++++--------- .../src/test/java/MonitorTest.java | 53 ++++++++------- .../testcases/clientserver/PMachines.java | 22 +++++++ .../espressomachine/EspressoMachine.java | 2 + .../failuredetector/FailureDetector.java | 2 + .../twophasecommit/TwoPhaseCommit.java | 4 ++ 7 files changed, 109 insertions(+), 60 deletions(-) diff --git a/Src/PCompiler/CompilerCore/Backend/Java/MachineGenerator.cs b/Src/PCompiler/CompilerCore/Backend/Java/MachineGenerator.cs index cae6177f1..bddba051f 100644 --- a/Src/PCompiler/CompilerCore/Backend/Java/MachineGenerator.cs +++ b/Src/PCompiler/CompilerCore/Backend/Java/MachineGenerator.cs @@ -252,9 +252,19 @@ private void WriteMonitorCstr() foreach (var s in _currentMachine.States) { - WriteStateBuilderDecl(s); + WriteStateBuilderDecl(s, true); } WriteLine("} // constructor"); + WriteLine(); + + WriteLine($"public void reInitializeMonitor() {{"); + + foreach (var s in _currentMachine.States) + { + WriteStateBuilderDecl(s, false); + } + WriteLine("}"); + } private void WriteEventsAccessor() @@ -271,9 +281,14 @@ private void WriteEventsAccessor() WriteLine("}"); } - private void WriteStateBuilderDecl(State s) + private void WriteStateBuilderDecl(State s, bool isConstructor) { - WriteLine($"addState(prt.State.keyedOn({Names.IdentForState(s)})"); + if (isConstructor) { + WriteLine($"addState(prt.State.keyedOn({Names.IdentForState(s)})"); + } else { + WriteLine($"registerState(prt.State.keyedOn({Names.IdentForState(s)})"); + } + if (s.IsStart) { WriteLine($".isInitialState(true)"); diff --git a/Src/PRuntimes/PJavaRuntime/src/main/java/prt/Monitor.java b/Src/PRuntimes/PJavaRuntime/src/main/java/prt/Monitor.java index 4be808830..9318f5eba 100644 --- a/Src/PRuntimes/PJavaRuntime/src/main/java/prt/Monitor.java +++ b/Src/PRuntimes/PJavaRuntime/src/main/java/prt/Monitor.java @@ -8,23 +8,22 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; -import org.apache.logging.log4j.message.StringMapMessage; import prt.exceptions.*; +import java.io.Serializable; /** * A prt.Monitor encapsulates a state machine. * */ -public abstract class Monitor> implements Consumer> { - private final Logger logger = LogManager.getLogger(this.getClass()); +public abstract class Monitor> implements Consumer>, Serializable { + private static final Logger logger = LogManager.getLogger(Monitor.class); private static final Marker PROCESSING_MARKER = MarkerManager.getMarker("EVENT_PROCESSING"); private static final Marker TRANSITIONING_MARKER = MarkerManager.getMarker("STATE_TRANSITIONING"); - @SuppressWarnings("OptionalUsedAsFieldOrParameterType") - private Optional> startState; - private State currentState; + private StateKey startStateKey; + private StateKey currentStateKey; - private EnumMap> states; // All registered states + private transient EnumMap> states; // All registered states private StateKey[] stateUniverse; // all possible states /** @@ -44,6 +43,17 @@ protected void addState(State s) { throw new RuntimeException("prt.Monitor is already running; no new states may be added."); } + registerState(s); + + if (s.isInitialState()) { + if (startStateKey != null) { + throw new RuntimeException("Initial state already set to " + startStateKey); + } + startStateKey = s.getKey(); + } + } + + protected void registerState(State s) { if (states == null) { states = new EnumMap<>((Class) s.getKey().getClass()); stateUniverse = s.getKey().getDeclaringClass().getEnumConstants(); @@ -53,13 +63,6 @@ protected void addState(State s) { throw new RuntimeException("prt.State already present"); } states.put(s.getKey(), s); - - if (s.isInitialState()) { - if (startState.isPresent()) { - throw new RuntimeException("Initial state already set to " + startState.get().getKey()); - } - startState = Optional.of(s); - } } public StateKey getCurrentState() { @@ -67,7 +70,7 @@ public StateKey getCurrentState() { throw new RuntimeException("prt.Monitor is not running (did you call ready()?)"); } - return currentState.getKey(); + return currentStateKey; } /** @@ -139,10 +142,11 @@ public void accept(PEvent p) throws UnhandledEventException { throw new RuntimeException("prt.Monitor is not running (did you call ready()?)"); } - logger.info(PROCESSING_MARKER, new StringMapMessage().with("event", p)); + //logger.info(PROCESSING_MARKER, new StringMapMessage().with("event", p)); // XXX: We can technically avoid this downcast, but to fulfill the interface for Consumer // this method cannot accept a type parameter, so this can't be a TransitionableConsumer

. + State currentState = states.get(currentStateKey); Optional> oc = currentState.getHandler(p.getClass()); if (oc.isEmpty()) { logger.atFatal().log(currentState + " missing event handler for " + p.getClass().getSimpleName()); @@ -157,19 +161,20 @@ public void accept(PEvent p) throws UnhandledEventException { * entry handler, and updating internal bookkeeping. * @param s The new state. */ - private

void handleTransition(State s, Optional

payload) { + private

void handleTransition(State s, P payload) { if (!isRunning) { throw new RuntimeException("prt.Monitor is not running (did you call ready()?)"); } - logger.info(TRANSITIONING_MARKER, new StringMapMessage().with("state", s)); + //logger.info(TRANSITIONING_MARKER, new StringMapMessage().with("state", s)); + State currentState = states.get(currentStateKey); currentState.getOnExit().ifPresent(Runnable::run); currentState = s; + currentStateKey = s.getKey(); currentState.getOnEntry().ifPresent(handler -> { - Object p = payload.orElse(null); - invokeWithTrampoline(handler, p); + invokeWithTrampoline(handler, payload); }); } @@ -199,7 +204,7 @@ private

void invokeWithTrampoline(State.TransitionableConsumer

handler, P * must be a handler of zero parameters, will be invoked. */ public void ready() { - readyImpl(Optional.empty()); + readyImpl(null); } /** @@ -208,10 +213,10 @@ public void ready() { * @param payload The argument to the initial state's entry handler. */ public

void ready(P payload) { - readyImpl(Optional.of(payload)); + readyImpl(payload); } - private

void readyImpl(Optional

payload) { + private

void readyImpl(P payload) { if (isRunning) { throw new RuntimeException("prt.Monitor is already running."); } @@ -224,13 +229,11 @@ private

void readyImpl(Optional

payload) { isRunning = true; - currentState = startState.orElseThrow(() -> - new RuntimeException( - "No initial state set (did you specify an initial state, or is the machine halted?)")); + currentStateKey = startStateKey; + State currentState = states.get(currentStateKey); currentState.getOnEntry().ifPresent(handler -> { - Object p = payload.orElse(null); - invokeWithTrampoline(handler, p); + invokeWithTrampoline(handler, payload); }); } @@ -238,13 +241,15 @@ private

void readyImpl(Optional

payload) { * Instantiates a new prt.Monitor; users should provide domain-specific functionality in a subclass. */ protected Monitor() { - startState = Optional.empty(); + startStateKey = null; isRunning = false; states = null; // We need a concrete class to instantiate an EnumMap; do this lazily on the first addState() call. - currentState = null; // So long as we have not yet readied, this will be null! + currentStateKey = null; // So long as we have not yet readied, this will be null! } public abstract List>> getEventTypes(); + public abstract void reInitializeMonitor(); + } diff --git a/Src/PRuntimes/PJavaRuntime/src/test/java/MonitorTest.java b/Src/PRuntimes/PJavaRuntime/src/test/java/MonitorTest.java index 1d8042201..7c0c2d9c1 100644 --- a/Src/PRuntimes/PJavaRuntime/src/test/java/MonitorTest.java +++ b/Src/PRuntimes/PJavaRuntime/src/test/java/MonitorTest.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; @@ -25,6 +26,9 @@ public NoDefaultStateMonitor() { super(); addState(new State.Builder<>(SingleState.INIT_STATE).build()); } + + public void reInitializeMonitor() {} + public List>> getEventTypes() { return List.of(); } } @@ -38,6 +42,8 @@ public MultipleDefaultStateMonitors() { addState(new State.Builder<>(BiState.OTHER_STATE).isInitialState(true).build()); } + public void reInitializeMonitor() {} + public List>> getEventTypes() { return List.of(); } } @@ -50,6 +56,8 @@ public NonTotalStateMapMonitor() { addState(new State.Builder<>(BiState.INIT_STATE).isInitialState(true).build()); } + public void reInitializeMonitor() {} + public List>> getEventTypes() { return List.of(); } } /** @@ -62,6 +70,8 @@ public NonUniqueStateKeyMonitor() { addState(new State.Builder<>(BiState.INIT_STATE).isInitialState(true).build()); } + public void reInitializeMonitor() {} + public List>> getEventTypes() { return List.of(); } } @@ -91,6 +101,8 @@ public CounterMonitor() { .build()); } + public void reInitializeMonitor() {} + public List>> getEventTypes() { return List.of(); } } @@ -115,6 +127,8 @@ public ChainedEntryHandlerMonitor() { .build()); } + public void reInitializeMonitor() {} + public List>> getEventTypes() { return List.of(); } } @@ -144,6 +158,8 @@ public GotoStateWithPayloadsMonitor() { .build()); } + public void reInitializeMonitor() {} + public List>> getEventTypes() { return List.of(); } } @@ -174,6 +190,8 @@ public GotoStateWithPayloadsMonitorIncludingInitialEntryHandler() { .build()); } + public void reInitializeMonitor() {} + public List>> getEventTypes() { return List.of(CounterMonitor.AddEvent.class); } } @@ -197,6 +215,8 @@ public GotoStateWithIllTypedPayloadsMonitor() { .build()); } + public void reInitializeMonitor() {} + public List>> getEventTypes() { return List.of(); } } @@ -212,6 +232,8 @@ public ImmediateAssertionMonitor() { .build()); } + public void reInitializeMonitor() {} + public List>> getEventTypes() { return List.of(); } } @@ -223,6 +245,8 @@ public class noopEvent extends PEvent { public Void getPayload() { return null; } } + public void reInitializeMonitor() {} + public List>> getEventTypes() { return List.of(testEvent.class, noopEvent.class); } public RaiseEventMonitor() { @@ -238,18 +262,6 @@ public RaiseEventMonitor() { } } - @Test - @DisplayName("Monitors require exactly one default state") - public void testDefaultStateConstruction() { - Throwable e; - - e = assertThrows(RuntimeException.class, () -> new NoDefaultStateMonitor().ready()); - assertTrue(e.getMessage().contains("No initial state set")); - - e = assertThrows(RuntimeException.class, () -> new MultipleDefaultStateMonitors().ready()); - assertTrue(e.getMessage().contains("Initial state already set")); - } - @Test @DisplayName("Monitors' state maps must be total") public void testTotalMonitorMap() { @@ -301,7 +313,8 @@ public void testChainedEntryHandlersWithPayloads() { GotoStateWithPayloadsMonitor m = new GotoStateWithPayloadsMonitor(); m.ready(); - assertTrue(m.eventsProcessed.equals(List.of("Hello from prt.State A", "Hello from prt.State B"))); + assertTrue(m.eventsProcessed.equals(List.of(Optional.of("Hello from prt.State A"), + Optional.of("Hello from prt.State B")))); } @Test @@ -312,20 +325,6 @@ public void testCantCallReadyTwice() { assertThrows(RuntimeException.class, () -> m.ready(), "prt.Monitor is already running."); } - - @Test - @DisplayName("Payloads can be passed to entry handlers through ready()") - public void testChainedEntryHandlersWithPayloadsIncludingInitialEntryHandler() { - GotoStateWithPayloadsMonitorIncludingInitialEntryHandler m = - new GotoStateWithPayloadsMonitorIncludingInitialEntryHandler(); - m.ready("Hello from the caller!"); - - assertTrue(m.eventsProcessed.equals( - List.of("Hello from the caller!", - "Hello from prt.State A", - "Hello from prt.State B"))); - } - @Test @DisplayName("Event handlers consuuming arguments in ready() must consume them!") public void testInitialEntryHandlerMustHaveAnArg() { diff --git a/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/clientserver/PMachines.java b/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/clientserver/PMachines.java index ed8c0d209..5946da26b 100644 --- a/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/clientserver/PMachines.java +++ b/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/clientserver/PMachines.java @@ -47,6 +47,17 @@ public BankBalanceIsAlwaysCorrect() { .build()); } // constructor + public void reInitializeMonitor() { + registerState(prt.State.keyedOn(PrtStates.Init) + .isInitialState(true) + .withEvent(PEvents.eSpec_BankBalanceIsAlwaysCorrect_Init.class, p -> { Anon(p); gotoState(PrtStates.WaitForWithDrawReqAndResp); }) + .build()); + registerState(prt.State.keyedOn(PrtStates.WaitForWithDrawReqAndResp) + .withEvent(PEvents.eWithDrawReq.class, this::Anon_1) + .withEvent(PEvents.eWithDrawResp.class, this::Anon_2) + .build()); + } + public java.util.List>> getEventTypes() { return java.util.Arrays.asList(PEvents.eSpec_BankBalanceIsAlwaysCorrect_Init.class, PEvents.eWithDrawReq.class, PEvents.eWithDrawResp.class); } @@ -227,6 +238,17 @@ public GuaranteedWithDrawProgress() { .build()); } // constructor + public void reInitializeMonitor() { + registerState(prt.State.keyedOn(PrtStates.NopendingRequests) + .isInitialState(true) + .withEvent(PEvents.eWithDrawReq.class, p -> { Anon_3(p); gotoState(PrtStates.PendingReqs); }) + .build()); + registerState(prt.State.keyedOn(PrtStates.PendingReqs) + .withEvent(PEvents.eWithDrawResp.class, this::Anon_4) + .withEvent(PEvents.eWithDrawReq.class, p -> { Anon_5(p); gotoState(PrtStates.PendingReqs); }) + .build()); + }; + public java.util.List>> getEventTypes() { return java.util.Arrays.asList(PEvents.eWithDrawReq.class, PEvents.eWithDrawResp.class); } diff --git a/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/espressomachine/EspressoMachine.java b/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/espressomachine/EspressoMachine.java index c7e858599..a21471b5d 100644 --- a/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/espressomachine/EspressoMachine.java +++ b/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/espressomachine/EspressoMachine.java @@ -340,6 +340,8 @@ public static class EspressoMachineModesOfOperation extends prt.Monitor { public List>> getEventTypes() { return List.of(); } //XXX: dummy implementation. + public void reInitializeMonitor() {}; // dummy implementation. + public enum States { STARTUP_STATE, WARMUP_STATE, diff --git a/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/failuredetector/FailureDetector.java b/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/failuredetector/FailureDetector.java index 3c38415c5..11ed5c22f 100644 --- a/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/failuredetector/FailureDetector.java +++ b/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/failuredetector/FailureDetector.java @@ -310,6 +310,8 @@ public String toString() { // PMachine Node elided public static class ReliableFailureDetector extends prt.Monitor { + public void reInitializeMonitor() {} // dummy implementation. + public List>> getEventTypes() { return List.of(); } //XXX: dummy implementation. private LinkedHashSet nodesShutdownAndNotDetected = new LinkedHashSet(); diff --git a/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/twophasecommit/TwoPhaseCommit.java b/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/twophasecommit/TwoPhaseCommit.java index 075cba6b1..af89a5a45 100644 --- a/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/twophasecommit/TwoPhaseCommit.java +++ b/Src/PRuntimes/PJavaRuntime/src/test/java/testcases/twophasecommit/TwoPhaseCommit.java @@ -587,6 +587,8 @@ public String toString() { // PMachine Participant elided public static class AtomicityInvariant extends prt.Monitor { + public void reInitializeMonitor() {}; // dummy implementation. + public List>> getEventTypes() { return List.of(); } //XXX: dummy implementation. private HashMap> participantsResponse = new HashMap>(); @@ -745,6 +747,8 @@ public AtomicityInvariant() { } // AtomicityInvariant monitor definition public static class Progress extends prt.Monitor { + public void reInitializeMonitor() {}; // dummy implementation. + public List>> getEventTypes() { return List.of(); } //XXX: dummy implementation. private int pendingTransactions = 0;