Skip to content

Commit 90f3370

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 3cf619a commit 90f3370

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.
@@ -339,9 +339,9 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent( //nolint:funlen
339339
// any preconditions as well as post-send events.
340340
case *SendMsgEvent[Event]:
341341
sendAndCleanUp := func() error {
342-
log.Debugf("FSM(%v): sending message to target(%v): "+
342+
log.Debugf("FSM(%v): sending message to target(%x): "+
343343
"%v", s.cfg.Env.Name(),
344-
daemonEvent.TargetPeer,
344+
daemonEvent.TargetPeer.SerializeCompressed(),
345345
newLogClosure(func() string {
346346
return spew.Sdump(daemonEvent.Msgs)
347347
}),
@@ -465,7 +465,11 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent( //nolint:funlen
465465
defer s.wg.Done()
466466
for {
467467
select {
468-
case spend := <-spendEvent.Spend:
468+
case spend, ok := <-spendEvent.Spend:
469+
if !ok {
470+
return
471+
}
472+
469473
// If there's a post-send event, then
470474
// we'll send that into the current
471475
// state now.
@@ -535,12 +539,6 @@ func (s *StateMachine[Event, Env]) executeDaemonEvent( //nolint:funlen
535539
func (s *StateMachine[Event, Env]) applyEvents(currentState State[Event, Env],
536540
newEvent Event) (State[Event, Env], error) {
537541

538-
log.Debugf("FSM(%v): applying new event: %v", s.cfg.Env.Name(),
539-
newLogClosure(func() string {
540-
return spew.Sdump(newEvent)
541-
}),
542-
)
543-
544542
eventQueue := fn.NewQueue(newEvent)
545543

546544
// Given the next event to handle, we'll process the event, then add
@@ -598,18 +596,21 @@ func (s *StateMachine[Event, Env]) applyEvents(currentState State[Event, Env],
598596
// our event queue.
599597
//
600598
//nolint:lll
601-
events.InternalEvent.WhenSome(func(inEvent Event) {
602-
log.Debugf("FSM(%v): adding new "+
603-
"internal event to queue: %v",
604-
s.cfg.Env.Name(),
605-
newLogClosure(func() string {
606-
return spew.Sdump(
607-
inEvent,
608-
)
609-
}),
610-
)
599+
events.InternalEvent.WhenSome(func(es []Event) {
600+
for _, inEvent := range es {
601+
log.Debugf("FSM(%v): adding "+
602+
"new internal event "+
603+
"to queue: %v",
604+
s.cfg.Env.Name(),
605+
newLogClosure(func() string { //nolint:lll
606+
return spew.Sdump(
607+
inEvent,
608+
)
609+
}),
610+
)
611611

612-
eventQueue.Enqueue(inEvent)
612+
eventQueue.Enqueue(inEvent)
613+
}
613614
})
614615

615616
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

@@ -246,6 +248,9 @@ func TestStateMachineTerminateCleanup(t *testing.T) {
246248
stateMachine.Start()
247249
defer stateMachine.Stop()
248250

251+
stateSub := stateMachine.RegisterStateEvents()
252+
defer stateMachine.RemoveStateSub(stateSub)
253+
249254
// Now we'll send in a new dummy event to trigger out state machine.
250255
// We'll have the ProcessEvent method take is to our terminal state.
251256
//
@@ -254,6 +259,11 @@ func TestStateMachineTerminateCleanup(t *testing.T) {
254259
env.On("CleanUp").Return(nil)
255260
stateMachine.SendEvent(&goToFin{})
256261

262+
expectedStates := []State[dummyEvents, *dummyEnv]{
263+
&dummyStateStart{}, &dummyStateFin{},
264+
}
265+
assertStateTransitions(t, stateSub, expectedStates)
266+
257267
// The state machine should now also be on the final terminal state.
258268
assertState[dummyEvents, *dummyEnv](t, &stateMachine, &dummyStateFin{})
259269

0 commit comments

Comments
 (0)