Неисправность сегмента бесконечной петли 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());
  }
}
Другие вопросы по тегам