1

Boost State Machine を使用しようとしていますが、マシンを無限ループで実行しているときにセグメンテーション フォールトが発生しました。基本的に、以下に示すブースト ステート マシン ファンクターの例に同じ例があります。

ここに画像の説明を入力

唯一の違いは、State4 に入るとすぐに "event1" が発生するようになり、ループが作成されることです。これは数千回の反復で機能しますが、その後はセグメント フォールトになります。ある種の UML ルールに違反していて、スタックがオーバーフローしていませんか? 私は基本的に 1 つのブロッキング イベントしか持たず、他のすべての状態を自動的にトリガーして、最終的に 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 に移行するためにトリガーする必要があるイベントがなくなったことに注意してください。このコードにより、間違いなくセグ フォールトが発生し、数千行に及ぶスタック トレースが生成されます。

また、待機時間に関係なく、最終的には常にセグフォルトになることにも注意してください。スリープを 1 ~ 100 に変更して遊んでみましたが、最終的には死んでしまいます。単一のループが完了したら、スタックを展開する何らかの方法が必要だと思います。

UPDATE 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 最終的に私の目標は、次の図のようなものを実装することです。

私の意図は、ステート マシンを開始することであり、それ以上の介入なしに着信メッセージを待つだけです。

4

1 に答える 1

0

fsm.process_event(e1)アクション/ガード/エントリ/終了ステートメントのいずれかの内部で呼び出した場合、ステート マシンに内部ループを持たせる方法はまったくないという結論に達しました。問題は、その呼び出しを行うたびに、最終的にスタックがオーバーフローするまで、スタックにより多くの情報をプッシュすることだと思います。これは、ステート マシンで無限ループを作成する匿名遷移がある場合にも当てはまります。したがって、ループをトリガーするには、少なくとも 1 つの外部イベント必要であるというのが私の結論です。したがって、次のコードは、これまでに見つけた最良のソリューションです。

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());
  }
}
于 2016-10-28T15:19:07.037 に答える