0

I am trying to use std::thread to implement parallel behavior in a state machine coded with the Boost MSM library. I am using std::thread to start a thread from the on_entry method of state A and I want to know how to call the process_event method in order to fire an event from within that thread. Here is a minimal working example:

#include <iostream>
#include <thread>
#include <unistd.h>

#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>

namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;

// Events
struct timeout {};

struct outer_:msmf::state_machine_def<outer_> {
  struct inner_:msmf::state_machine_def<inner_> {
    template <class Event, class Fsm>
    void on_entry(Event const&, Fsm&) {
      std::cout << "[state machine entry] inner" << std::endl;
    }
    struct A:msmf::state<> {
      template <class Event, class Fsm>
      void on_entry(Event const&, Fsm& f) {
        std::cout << "[state entry] A" << std::endl;
        stop_threads_ = false;
        thread_ = new std::thread(&A::func,this);
      }
      template <class Event, class Fsm>
      void on_exit(Event const&, Fsm&) {
        stop_threads_ = true;
        thread_->join(); // wait for threads to finish
        delete thread_;
        std::cout << "[state exit] A" << std::endl;
      }
      void func() {
        while (!stop_threads_) {
          usleep(1000000);
          std::cout << "Hello" << std::endl;
          // QUESTION: how to call process_event(timeout()) here?
        }
      }
     public:
      std::thread* thread_;
      bool stop_threads_;
    };
    struct Action {
      template <class Event, class Fsm, class SourceState, class TargetState>
      void operator()(Event const&, Fsm&, SourceState&, TargetState&) {
        std::cout << "Trying again..." << std::endl;
      }
    };
    typedef A initial_state;
    struct transition_table:mpl::vector<
        msmf::Row <A,timeout,A,Action>
    > {};
  };
  typedef msm::back::state_machine<inner_> inner;
  typedef inner initial_state;
};
typedef msm::back::state_machine<outer_> outer;

void waiting_thread() {
  while(true) {
    usleep(2000000);
  }
}

int main() {
  outer sm;
  sm.start();
  std::thread wait(waiting_thread);
  wait.join();
}

My question is at the comment // QUESTION..., where I want a means to execute the process_event method. Thank you for your help!

space_voyager
  • 1,984
  • 3
  • 20
  • 31
  • As stated already, you can cannot directly do it at that point. What I've seen done is to use a thread safe queue and push event objects there, then use a thread to process_events in serialized and thread-safe order in the state machine. – sebkraemer Jul 01 '19 at 18:55

1 Answers1

1

Unfortunately, you cannot do that.

Here is the code that is trying to process event in func(). That is similar strategy to Boost MSM parallel behavior with delayed self-transitions?

It comples successfully. But I got an exception.

#include <iostream>
#include <thread>
#include <mutex>
#include <unistd.h>

#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>

namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;

// Events
struct timeout {};

struct outer_:msmf::state_machine_def<outer_> {
  std::mutex mtx;
  typedef msm::back::state_machine<outer_> outer;
  std::weak_ptr<outer> wp;

  static std::shared_ptr<outer> create() {
    auto p = std::make_shared<outer>();
    p->wp = p; // set wp after creation.
    return p;
  }

  template <typename Ev>
  void process(Ev&& ev) {
    // process_event via backend weak_ptr
    std::lock_guard<std::mutex> g(wp.lock()->mtx);
    wp.lock()->process_event(std::forward<Ev>(ev));
  }
  struct inner_:msmf::state_machine_def<inner_> {
    std::weak_ptr<outer> wp;

    template <class Event, class Fsm>
    void on_entry(Event const&, Fsm& f) {
      std::cout << "[state machine entry] inner" << std::endl;
      wp = f.wp;
    }
    struct A:msmf::state<> {
      template <class Event, class Fsm>
      void on_entry(Event const&, Fsm& f) {
        std::cout << "[state entry] A" << std::endl;
        stop_threads_ = false;
        thread_ = new std::thread(&A::func,this, std::ref(f));
      }
      template <class Event, class Fsm>
      void on_exit(Event const&, Fsm&) {
        stop_threads_ = true;
        thread_->join(); // wait for threads to finish
        delete thread_;
        std::cout << "[state exit] A" << std::endl;
      }
      void func(inner_& f) {
        while (!stop_threads_) {
          usleep(1000000);
          std::cout << "Hello" << std::endl;
          // QUESTION: how to call process_event(timeout()) here?
          f.wp.lock()->process(timeout());
        }
      }
     public:
      std::thread* thread_;
      bool stop_threads_;
    };
    struct Action {
      template <class Event, class Fsm, class SourceState, class TargetState>
      void operator()(Event const&, Fsm&, SourceState&, TargetState&) {
        std::cout << "Trying again..." << std::endl;
      }
    };
    typedef A initial_state;
    struct transition_table:mpl::vector<
        msmf::Row <A,timeout,A,Action>
    > {};
    template <class FSM,class Event>
    void exception_caught (Event const&,FSM&,std::exception& e) {
        std::cout << e.what() << std::endl;
    }
  };
  typedef msm::back::state_machine<inner_> inner;
  typedef inner initial_state;
  template <class FSM,class Event>
  void exception_caught (Event const&,FSM&,std::exception& e) {
      std::cout << e.what() << std::endl;
  }
};

void waiting_thread() {
  while(true) {
    usleep(2000000);
  }
}

int main() {
  std::shared_ptr<outer_::outer> sm = outer_::create();
  sm->start();
  std::cout << "started" << std::endl;
  std::thread wait(waiting_thread);
  wait.join();
  std::cout << "joined" << std::endl;
}

I added exception printing function as follows. exception_caught is called when Boost.MSM catches exception during process_event.

    template <class FSM,class Event>
    void exception_caught (Event const&,FSM&,std::exception& e) {
        std::cout << e.what() << std::endl;
    }

The message I got is Resource deadlock avoided. It is originally caused by pthread library. If you create a thread and then join the thread from the different thread, then the exception is thrown.

Let's see where the code creates the thread.

Here is the point:

      template <class Event, class Fsm>
      void on_entry(Event const&, Fsm& f) {
        std::cout << "[state entry] A" << std::endl;
        stop_threads_ = false;
        thread_ = new std::thread(&A::func,this, std::ref(f));
      }

It is called from the main thread. Because it is called from sm->start().

Then let's check the joining point/

Here is the point:

      template <class Event, class Fsm>
      void on_exit(Event const&, Fsm&) {
        stop_threads_ = true;
        thread_->join(); // wait for threads to finish
        delete thread_;
        std::cout << "[state exit] A" << std::endl;
      }

It is called from f.wp.lock()->process(timeout()); in the void func(inner_& f). And func is the entry-point of the thread that is created by thread_ = new std::thread(&A::func,this, std::ref(f));. That means thread_->join(); is waiting itself.

That is the reason that Resource deadlock avoided is thrown.

edit

It is an answer for the comment Boost MSM call process_event from a custom function (not an Action) inside a State?

It is NOT an complete solution but could be a hint of the design.

If I detach the thread and remove join(), then the program works as you expected. Of course detach() is NOT the best solution. And thread_ never deleted. You need to care them in the product code.

The purpose of the following code is demonstrating the state-machine behavior.

struct A:msmf::state<> {
  template <class Event, class Fsm>
  void on_entry(Event const&, Fsm& f) {
    std::cout << "[state entry] A" << std::endl;
    stop_threads_ = false;
    thread_ = new std::thread(&A::func,this, std::ref(f));
    thread_->detach();
  }
  template <class Event, class Fsm>
  void on_exit(Event const&, Fsm&) {
    stop_threads_ = true;
    std::cout << "[state exit] A" << std::endl;
  }
  struct func {
    func(inner_& f):f(f) {}
      void operator()() {
      while (!stop_threads_) {
        usleep(1000000);
        std::cout << "Hello" << std::endl;
        // QUESTION: how to call process_event(timeout()) here?
        f.wp.lock()->process(timeout());
      }
    }
  };
 public:
  std::thread* thread_;
  bool stop_threads_;
};
Community
  • 1
  • 1
Takatoshi Kondo
  • 3,111
  • 17
  • 36
  • What would then be the work around in order to achieve what I want : i.e. Have a function which I can execute in a parallel thread, but also be able to quit (transition from) the state from within that function if necessary? – space_voyager Mar 22 '17 at 10:38
  • 1
    I updated my answer. After that updating, the code behaves as you expected. But it includes detached thread and resource leak. I don't have much time to solve it. But it indicates the way of the solution. – Takatoshi Kondo Mar 22 '17 at 11:15
  • In order to join timer thread correctly, you can use condition variables with the thread. The thread is created from the main thread and then waiting. In the action of state-machine, notify to the condition variable. Then waiting thread is unblocked. In the thread usleep several duration and then, call process_event. – Takatoshi Kondo Mar 23 '17 at 02:53