> Replacing a state machine with callbacks... something something something you're gonna have a bad time
Having written quite a few networking systems, I fully agree. Especially if it's coupled in this text with "replacing a single-threaded state-machine" with "a multithreaded system, which uses callbacks". IMHO the latter is almost guaranteed a recipe for all kinds of multithreading issues, from race-conditions over deadlock to memory issues. And even if the author of the library gets it right, the issues are often at library user level (didn't expect where the callback is executed and accessed their data without necessary synchronization).
The described model of nanopb sounds very sane compared to that. Pure multithreaded systems without callbacks (like in Go) also work reasonably well. But even those do not work without state-machines in all situations. In all cases where state is manipulated by more than one code-path it's mostly the most reasonable thing to handle this in some kind of state-machine which runs on a single thread, instead of trying to fight it with dozens of fine-grained locks. And just as an example: The HTTP2 implementation in Go uses a state-machine which runs on a goroutine per connection. And user code, frame reader and frame writer code communicate via messaging with that state-machine.
Having written quite a few networking systems, I fully agree. Especially if it's coupled in this text with "replacing a single-threaded state-machine" with "a multithreaded system, which uses callbacks". IMHO the latter is almost guaranteed a recipe for all kinds of multithreading issues, from race-conditions over deadlock to memory issues. And even if the author of the library gets it right, the issues are often at library user level (didn't expect where the callback is executed and accessed their data without necessary synchronization).
The described model of nanopb sounds very sane compared to that. Pure multithreaded systems without callbacks (like in Go) also work reasonably well. But even those do not work without state-machines in all situations. In all cases where state is manipulated by more than one code-path it's mostly the most reasonable thing to handle this in some kind of state-machine which runs on a single thread, instead of trying to fight it with dozens of fine-grained locks. And just as an example: The HTTP2 implementation in Go uses a state-machine which runs on a goroutine per connection. And user code, frame reader and frame writer code communicate via messaging with that state-machine.