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_;
};