Skip to content

Commit fa12732

Browse files
committed
protofsm: allow multiple internal events to be emitted
In this commit, we update the execution logic to allow multiple internal events to be emitted. This is useful to handle potential out of order state transitions, as they can be cached, then emitted once the relevant pre-conditions have been met.
1 parent 5b510f0 commit fa12732

File tree

3 files changed

+34
-23
lines changed

3 files changed

+34
-23
lines changed

protofsm/log.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ var log btclog.Logger
1212

1313
// The default amount of logging is none.
1414
func init() {
15-
UseLogger(build.NewSubLogger("PRCL", nil))
15+
UseLogger(build.NewSubLogger("PFSM", nil))
1616
}
1717

1818
// DisableLog disables all library log output. Logging output is disabled

protofsm/state_machine.go

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type EmittedEvent[Event any] struct {
2727
// InternalEvent is an optional internal event that is to be routed
2828
// back to the target state. This enables state to trigger one or many
2929
// state transitions without a new external event.
30-
InternalEvent fn.Option[Event]
30+
InternalEvent fn.Option[[]Event]
3131

3232
// ExternalEvent is an optional external event that is to be sent to
3333
// the daemon for dispatch. Usually, this is some form of I/O.
@@ -342,9 +342,9 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent( //nolint:funlen
342342
// any preconditions as well as post-send events.
343343
case *SendMsgEvent[Event]:
344344
sendAndCleanUp := func() error {
345-
log.Debugf("FSM(%v): sending message to target(%v): "+
345+
log.Debugf("FSM(%v): sending message to target(%x): "+
346346
"%v", s.cfg.Env.Name(),
347-
daemonEvent.TargetPeer,
347+
daemonEvent.TargetPeer.SerializeCompressed(),
348348
newLogClosure(func() string {
349349
return spew.Sdump(daemonEvent.Msgs)
350350
}),
@@ -481,7 +481,11 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent( //nolint:funlen
481481
defer s.wg.Done()
482482
for {
483483
select {
484-
case spend := <-spendEvent.Spend:
484+
case spend, ok := <-spendEvent.Spend:
485+
if !ok {
486+
return
487+
}
488+
485489
// If there's a post-send event, then
486490
// we'll send that into the current
487491
// state now.
@@ -551,12 +555,6 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent( //nolint:funlen
551555
func (s *StateMachine[Event, Env]) applyEvents(currentState State[Event, Env],
552556
newEvent Event) (State[Event, Env], error) {
553557

554-
log.Debugf("FSM(%v): applying new event: %v", s.cfg.Env.Name(),
555-
newLogClosure(func() string {
556-
return spew.Sdump(newEvent)
557-
}),
558-
)
559-
560558
eventQueue := fn.NewQueue(newEvent)
561559

562560
// Given the next event to handle, we'll process the event, then add
@@ -614,18 +612,21 @@ func (s *StateMachine[Event, Env]) applyEvents(currentState State[Event, Env],
614612
// our event queue.
615613
//
616614
//nolint:lll
617-
events.InternalEvent.WhenSome(func(inEvent Event) {
618-
log.Debugf("FSM(%v): adding new "+
619-
"internal event to queue: %v",
620-
s.cfg.Env.Name(),
621-
newLogClosure(func() string {
622-
return spew.Sdump(
623-
inEvent,
624-
)
625-
}),
626-
)
615+
events.InternalEvent.WhenSome(func(es []Event) {
616+
for _, inEvent := range es {
617+
log.Debugf("FSM(%v): adding "+
618+
"new internal event "+
619+
"to queue: %v",
620+
s.cfg.Env.Name(),
621+
newLogClosure(func() string { //nolint:lll
622+
return spew.Sdump(
623+
inEvent,
624+
)
625+
}),
626+
)
627627

628-
eventQueue.Enqueue(inEvent)
628+
eventQueue.Enqueue(inEvent)
629+
}
629630
})
630631

631632
return nil

protofsm/state_machine_test.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ func (d *dummyStateStart) ProcessEvent(event dummyEvents, env *dummyEnv,
8686
return &StateTransition[dummyEvents, *dummyEnv]{
8787
NextState: &dummyStateStart{},
8888
NewEvents: fn.Some(EmittedEvent[dummyEvents]{
89-
InternalEvent: fn.Some(dummyEvents(&goToFin{})),
89+
InternalEvent: fn.Some(
90+
[]dummyEvents{&goToFin{}},
91+
),
9092
}),
9193
}, nil
9294

@@ -252,6 +254,9 @@ func TestStateMachineTerminateCleanup(t *testing.T) {
252254
stateMachine.Start()
253255
defer stateMachine.Stop()
254256

257+
stateSub := stateMachine.RegisterStateEvents()
258+
defer stateMachine.RemoveStateSub(stateSub)
259+
255260
// Now we'll send in a new dummy event to trigger out state machine.
256261
// We'll have the ProcessEvent method take is to our terminal state.
257262
//
@@ -260,6 +265,11 @@ func TestStateMachineTerminateCleanup(t *testing.T) {
260265
env.On("CleanUp").Return(nil)
261266
stateMachine.SendEvent(&goToFin{})
262267

268+
expectedStates := []State[dummyEvents, *dummyEnv]{
269+
&dummyStateStart{}, &dummyStateFin{},
270+
}
271+
assertStateTransitions(t, stateSub, expectedStates)
272+
263273
// The state machine should now also be on the final terminal state.
264274
assertState[dummyEvents, *dummyEnv](t, &stateMachine, &dummyStateFin{})
265275

0 commit comments

Comments
 (0)