I am attempting to use Boost State Machine, but I have encountered a Segmentation Fault when running my machine in an infinite loop. Essentially I have the same example in the boost state machine functor example shown below:
The only difference is that I now trigger "event1" to occur as soon as I enter State4, hence creating a loop. This works for several thousand iterations but then it will seg fault. Am I breaking some kind of UML rule and overflowing the stack? I basically only have one blocking event and then I want all the other states to trigger automatically, and then end up in State4 (which in reality would be a blocking call waiting for a message from the network for example). How would I properly implement this using Meta State Machine so I don't blow up the stack?
UPDATE
I've included a the source code that is causing my problems here:
http://pastebin.com/fu6rzF0Q
This is basically the example in functor front end except with the following changes:
Added "pretend" blocking call function:
struct BlockingCall {
template <class EVT, class FSM, class SourceState, class TargetState>
void operator()(EVT const &, FSM &, SourceState &, TargetState &) {
std::cout << "my_machine::Waiting for a thing to happen..." << std::endl;
// Pretend I'm actually waiting for something
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "my_machine::OMG the the thing happened!" << std::endl;
}
};
And I also updated the last line in the transition table:
struct transition_table : mpl::vector<
// Start Event Next Action Guard
// +---------+-------------+---------+---------------------+----------------------+
Row < State1 , none , State2 >,
Row < State2 , none , State3 , State2ToState3 >,
Row < State3 , none , State4 , none , always_false >,
// +---------+-------------+---------+---------------------+----------------------+
Row < State3 , none , State4 , State3ToState4 , always_true >,
Row < State4 , none , State1 , BlockingCall >
// +---------+-------------+---------+---------------------+----------------------+
> {};
Notice that there is no longer an event that needs to triggered to move from State4 to State1. This code with out a doubt give you a seg fault and will have a stack trace that is 1000s of lines long.
I should also note that regardless of the time I wait, I always eventually seg fault. I've played around with changing the sleep to 1 - 100 and it will eventually die. I guess I need some way of unrolling the stack once a single loop has completed.
UPDATE 2 So I found that I don't seg fault when I trigger on the event in an infinite loop. Here is what I did:
First I set the transition table back to the original example:
struct transition_table : mpl::vector<
// Start Event Next Action Guard
// +---------+-------------+---------+---------------------+----------------------+
Row < State1 , none , State2 >,
Row < State2 , none , State3 , State2ToState3 >,
Row < State3 , none , State4 , none , always_false >,
// +---------+-------------+---------+---------------------+----------------------+
Row < State3 , none , State4 , State3ToState4 , always_true >,
Row < State4 , event1 , State1 , none >
// +---------+-------------+---------+---------------------+----------------------+
> {};
Then I changed the main program to the following:
void test() {
my_machine p;
// needed to start the highest-level SM. This will call on_entry and mark the
// start of the SM
// in this case it will also immediately trigger all anonymous transitions
p.start();
// this event will bring us back to the initial state and thus, a new "loop"
// will be started
while (true) {
p.process_event(event1());
}
}
And now I have been running at full speed (no sleeps) and I haven't seg faulted. Based on this, it seems there is no way to start a state machine and just have it run and process internal events, is that correct? I always have to have some process on the outside that triggers at least on even?
UPDATE 3 Ultimately my goal is to implement something like the following picture:
My intent is to have the state machine started, and then it will simply wait for incoming messages without any further intervention.
I have come to the conclusion that there is simply no way to have the state machine have an internal loop if you call fsm.process_event(e1)
inside any of the actions/guards/entry/exit statements. I believe that the problem is that every time you make that call, you push more information on the stack until eventually you overflow the stack. This is also true if you have anonymous transitions create an infinite loop in the state machine. So my conclusion is that you must have at least ONE external event to trigger the loop. Hence the following code is the best solution I have found so far:
void test() {
my_machine p;
p.start();
// Must have at least 1 triggering external event to not overflow the stack
while (true) {
p.process_event(event1());
}
}