Boost logo

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