|
Boost : |
Subject: Re: [boost] [MSM] Interrupt anonymous transitions or blocking process_events?
From: anlmat (anlmat_at_[hidden])
Date: 2011-04-19 16:27:15
Hi Christophe,
Thank you for the helpful reply!
<snip>
>> Perhaps I am going about this the wrong here and please tell me if it
>> can be done in some other much better way! I want to be able to have
>> automatic transitions that I can interrupt.
>
> You have to use run-to-completion. Base your design on short-terms
> events fed to the state machine, which triggers work and reacts to
> changes in forms of other events.
> In your case, one possibility would be to have the state machine
> delegate (post) some work upon state entry or transition action to a
> thread, then posting a new task upon state exit, etc.
> Make your state machine be serviced by only one thread, triggering
> work and reacting to events. Trust me on that one, having a state
> machine serviced by 2 threads is a good receipt for headaches ;-)
> If you know boost::asio, it's a bit like the async_xxx methods,
> trigger some work and get ready for the next action (which would be a
> callback, to continue with this asio example).
That helped me alot doing it that way. Thank you! It was a struggle
before I realized how to call a process_event(...) from within a member
function. I hope I am doing it the right way :-)
I also tried adding
boost::signals2::signal<void ()> sig;
to the statemachine but my compiler did not like that at all
(noncopyable errors). So I stayed with a mutex and a condition variable.
The way I did it can be improved I am sure...
Anyway, you can see an outline below of how I did this. Perhaps this is
an ok way doing it?
----------------------------
cdplayer_statemachine.h
struct player_ : ...
{
boost::mutex event_mutex_;
boost::thread thread_;
boost::condition_variable_any cond_;
bool trigger_;
void next_song();
void playing_thread();
...
//I choose to start the thread in the ctor...
player_():thread_(boost::thread(&player_::playing_thread, this))
{}
...
//action
struct play_song
{
template <class EVT,class FSM,class SourceState,class TargetState>
void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& )
{
std::cout << "starting to play song "<< current_name_ <<std::endl;
//triggers the thread to leave its waiting state
fsm.next_song();
}
};
...
//in transition_table
//NextSong event is generated in the playing_thread
Row < Playing , NextSong , Playing , play_song , exists_next >,
Row < Playing , NextSong , Stopped , stop_playing , no_more_songs >,
Row < Playing , Stop , Stopped , stop_playing >,
//...
...
};
// Pick a back-end
typedef msm::back::state_machine<player_> player;
---------------------------
cdplayer_statemachine.cpp
void player_::next_song()
{
trigger_ = true;
cond_.notify_one();
}
void player_::playing_thread()
{
while(running_)
{
boost::mutex::scoped_lock trigg_lock(trigg_mutex);
while(!trigger_)
cond_.wait(trigg_lock);
trigger_ = false;
//Simulate playing song...
//The state machine can now react to other events like Stop()
//for example.
boost::this_thread::sleep(boost::posix_time::millisec(2000));
boost::mutex::scoped_lock lock(event_mutex_);
//This is how you post a process_event from a member function.
// note: the "player" here is not the same as "player_" !!
// this "player" is the typedef choosen back-end name.
(static_cast<player*>(this))->process_event(NextSong());
}
}
Regards,
Mathias
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk