hekailiang / squirrel

squirrel-foundation is a State Machine library, which provided a lightweight, easy use, type safe and programmable state machine implementation for Java.
http://hekailiang.github.io/squirrel/
Other
2.19k stars 540 forks source link

Self-transition executes too many entry & exit actions #116

Closed npetryk closed 4 years ago

npetryk commented 4 years ago

I would expect an external transition from S -> S to produce the following actions:

  1. exitS
  2. entryS

i.e. S -> (exitS) -> (entryS) -> S

Instead, I see twice as many

  1. exitS
  2. entryS
  3. exitS
  4. entryS

i.e. S -> (exitS) -> (entryS) -> (exitS) -> (entryS) -> S

Is this the expected behavior?


Example code:

import org.squirrelframework.foundation.fsm.StateMachineBuilderFactory;
import org.squirrelframework.foundation.fsm.StateMachineConfiguration;
import org.squirrelframework.foundation.fsm.UntypedStateMachine;
import org.squirrelframework.foundation.fsm.UntypedStateMachineBuilder;
import org.squirrelframework.foundation.fsm.annotation.StateMachineParameters;
import org.squirrelframework.foundation.fsm.impl.AbstractUntypedStateMachine;

public class Example {

  enum FSMEvent {
    INTERNAL_TRANSITION,
    SELF_TRANSITION
  }

  public static void main(String[] args) {
    UntypedStateMachineBuilder builder = StateMachineBuilderFactory.create(MyStateMachine.class);
    builder.defineState("S");
    builder.internalTransition().within("S").on(FSMEvent.INTERNAL_TRANSITION);
    builder.transition().from("S").to("S").on(FSMEvent.SELF_TRANSITION);
    UntypedStateMachine fsm =
        builder.newStateMachine("S", StateMachineConfiguration.create().enableDebugMode(true));
    fsm.start();

    System.out.println("Internal");
    fsm.fire(FSMEvent.INTERNAL_TRANSITION);
    System.out.println("Self");
    fsm.fire(FSMEvent.SELF_TRANSITION);
  }

  @StateMachineParameters(
      stateType = String.class,
      eventType = FSMEvent.class,
      contextType = String.class)
  static class MyStateMachine extends AbstractUntypedStateMachine {

    protected void entryS(String from, String to, FSMEvent event, String ctx) {}

    protected void exitS(String from, String to, FSMEvent event, String ctx) {}
  }
}

Output:

Internal
21:40:36.839 [main] INFO  o.s.f.fsm.StateMachineLogger - MyStateMachine(2fEFYCwbIl): Transition from "S" on "INTERNAL_TRANSITION" with context "null" begin.
21:40:36.844 [main] INFO  o.s.f.fsm.StateMachineLogger - MyStateMachine(2fEFYCwbIl): Transition from "S" to "S" on "INTERNAL_TRANSITION" complete which took 4.437 ms.
Self
21:40:36.844 [main] INFO  o.s.f.fsm.StateMachineLogger - MyStateMachine(2fEFYCwbIl): Transition from "S" on "SELF_TRANSITION" with context "null" begin.
21:40:36.845 [main] INFO  o.s.f.fsm.StateMachineLogger - Before execute method call action "exitS:-10" (1 of 4).
21:40:36.845 [main] INFO  o.s.f.fsm.StateMachineLogger - After execute method call action "exitS:-10" which took 209.2 μs.
21:40:36.846 [main] INFO  o.s.f.fsm.StateMachineLogger - Before execute method call action "entryS:-10" (2 of 4).
21:40:36.846 [main] INFO  o.s.f.fsm.StateMachineLogger - After execute method call action "entryS:-10" which took 168.6 μs.
21:40:36.846 [main] INFO  o.s.f.fsm.StateMachineLogger - Before execute method call action "exitS:-10" (3 of 4).
21:40:36.846 [main] INFO  o.s.f.fsm.StateMachineLogger - After execute method call action "exitS:-10" which took 103.8 μs.
21:40:36.846 [main] INFO  o.s.f.fsm.StateMachineLogger - Before execute method call action "entryS:-10" (4 of 4).
21:40:36.846 [main] INFO  o.s.f.fsm.StateMachineLogger - After execute method call action "entryS:-10" which took 101.6 μs.
21:40:36.847 [main] INFO  o.s.f.fsm.StateMachineLogger - MyStateMachine(2fEFYCwbIl): Transition from "S" to "S" on "SELF_TRANSITION" complete which took 2.093 ms.
hekailiang commented 4 years ago

thx for reporting this issue, fixed in latest version. changelist

npetryk commented 4 years ago

Wow, thanks for the quick follow up!

Would you like me to close this issue out?