Неисправность сегмента бесконечной петли Boost Meta State Machine
Я пытаюсь использовать Boost State Machine, но у меня возникла ошибка сегментации при работе моей машины в бесконечном цикле. По сути, у меня есть тот же пример в примере функтора буста, показанном ниже:
Единственное отличие состоит в том, что теперь я запускаю "событие1", как только я вхожу в State4, создавая цикл. Это работает в течение нескольких тысяч итераций, но затем вызывает ошибку. Я нарушаю какое-то правило UML и переполняю стек? У меня в основном только одно событие блокировки, и затем я хочу, чтобы все остальные состояния автоматически запускались, а затем оказывались в State4 (что на самом деле было бы блокирующим вызовом, ожидающим сообщения из сети, например). Как бы я правильно реализовал это, используя Meta State Machine, чтобы не взорвать стек?
ОБНОВИТЬ
Я включил исходный код, который вызывает мои проблемы здесь: http://pastebin.com/fu6rzF0Q
Это в основном пример в интерфейсе функтора, за исключением следующих изменений:
Добавлена функция блокировки "притворяться":
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;
}
};
И я также обновил последнюю строку в таблице переходов:
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 >
// +---------+-------------+---------+---------------------+----------------------+
> {};
Обратите внимание, что больше нет события, которое должно инициироваться для перемещения из State4 в State1. Этот код без сомнения даст вам ошибку сегмента и будет иметь трассировку стека длиной в 1000 с.
Я также должен отметить, что независимо от того, сколько времени я жду, я всегда в конечном итоге вижу ошибку. Я играл с изменением сна на 1 - 100, и в конечном итоге он умрет. Я думаю, мне нужен какой-то способ развернуть стек после завершения одного цикла.
ОБНОВЛЕНИЕ 2 Итак, я обнаружил, что я не вижу ошибки, когда запускаю событие в бесконечном цикле. Вот что я сделал:
Сначала я вернул таблицу переходов к исходному примеру:
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 >
// +---------+-------------+---------+---------------------+----------------------+
> {};
Затем я изменил основную программу на следующую:
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());
}
}
И теперь я бегаю на полной скорости (не сплю), и я не сломался. Исходя из этого, кажется, что нет способа запустить конечный автомат и просто запустить его и обработать внутренние события, это правильно? У меня всегда должен быть какой-то процесс снаружи, который запускает, по крайней мере, даже?
ОБНОВЛЕНИЕ 3 В конечном счете моя цель состоит в том, чтобы реализовать что-то вроде следующего изображения:
Мое намерение состоит в том, чтобы запустить конечный автомат, и тогда он будет просто ждать входящих сообщений без какого-либо дальнейшего вмешательства.
1 ответ
Я пришел к выводу, что просто нет никакого способа, чтобы конечный автомат имел внутренний цикл, если вы вызываете fsm.process_event(e1)
внутри любых действий / охранников / входов / выходов. Я считаю, что проблема в том, что каждый раз, когда вы делаете этот вызов, вы помещаете в стек больше информации, пока в конечном итоге не переполните стек. Это также верно, если у вас есть анонимные переходы, создающие бесконечный цикл в автомате. Итак, мой вывод заключается в том, что у вас должно быть как минимум ОДНО внешнее событие для запуска цикла. Следовательно, следующий код - лучшее решение, которое я нашел до сих пор:
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());
}
}