Re: [boost] [MSM] Interrupt anonymous transitions or blocking process_events?

Hi Mathias, <snip>
By calling p.process_event(play()); the the player will start playing Song1 and then change to the next song by itself. But now in single threaded mode it blocks until it reaches "Song3". So it is not possible to process any other events during this time. In other words, it cannot process another process_event call. The "Playing" state has a "stop" event but calling this will not help as it will not be processed until the MSM has reached "Song3".
The problem is that a state machine is based on run-to-completion. This means in your case that a transition must be "short". An event comes, a transition is executed, the state machine is in a new stable state and can process a new event. Until this happens, events are queued (in the case of MSM).
I start the same p.process_event(play()); in thread 1. And in thread 2 I call p.process_event(stop()); The problem with this is that the stop() event does not get processed until it reaches Song3 because of the blocking call in p.process_event(play());.
Actually I'd think because of the mutex. In any case, your design will be hard to get right I'm afraid.
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). HTH, Christophe

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
participants (2)
-
anlmat
-
Christophe Henry