This is one of those problems that Tail Call Elimination cleanly addresses. The "obvious" approach to writing an FSM does exactly what you'd expect.
But you can do the same with an event loop that cleanly avoids goto.
[1] https://clang.llvm.org/docs/AttributeReference.html#musttail
[2] https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html