Hi Christophe,
Did you consider adding build-in multi-thread support? (via policies maybe?)
For example, I can guaranteed that all access to sub-fsm within its
handlers (process_event()) will be synchronized.
The problem is, sub-fsm "knows" about its parent and internally
communicates with it (forwarding events etc). I can't control it from my
side.
typical scenario:
an action from sub-fsm
struct A_Init
{
template
Hi,
Did you consider adding build-in multi-thread support? (via policies maybe?)
I did. This is still work in progress (see https://github.com/boostorg/msm/blob/msm3/test/Lockfree1.cpp). There is a new lockfree_policy.
For example, I can guaranteed that all access to sub-fsm within its handlers (process_event()) will be synchronized. The problem is, sub-fsm "knows" about its parent and internally communicates with it (forwarding events etc). I can't control it from my side.
typical scenario: an action from sub-fsm
struct A_Init { template
void operator()(Evt&, FSM& fsm, SourceState&, TargetState&) const { std::thread([=]() mutable { std::lock_guardstd::mutex l(access); fsm.process_event(some_event()); }).detach(); } }; locking proccess_event() in sub-fsm doesn't help if it alters the root.
Actually your example is probably wrong (it's my worst design decision) because the fsm parameter in an action of a submachine itself is the submachine, not the outer. But yes, if you have a pseudo exit then you will call the outer from the sub fsm. I unsure of what your problem is, you could have your mutex in the outer and it would work. The sub fsm would lock the whole fsm, then call process_event, which runs to completion, done. Normally, MSM is not thread-safe if that's what you mean... Clearly, locking such a long time is still undesirable, which is why I went lockfree. Your actions run at the same time as the active state switching. It's really fast, but quite hard to debug and understand. For example, I am quite sure my implementation works for simple fsms, for internal transitions, for orthogonal regions, soon for deferred events and event queue, but my test for submachines does not and I still did not figure out if the code or the test is wrong... If you want to try it out, I'm interested in your experience. Otherwise, it's all I have at the moment, I had no time to push it further yet. My priority at the moment goes to finishing the upcoming asynchronous, which solves this problem nicely (we use it at work for quite some time now) by taking care of enqueing calls between threads. I run my fsms in a single thread, avoiding concurrency on the state machine. HTH, Christophe
On 26.01.2015 23:10, christophe.j.henry@gmail.com wrote:
Actually your example is probably wrong (it's my worst design decision) because the fsm parameter in an action of a submachine itself is the submachine, not the outer. But yes, if you have a pseudo exit then you will call the outer from the sub fsm.
That's my case. Another thread triggers the sub-fsm to move the exit state.
I unsure of what your problem is, you could have your mutex in the outer and it would work. The sub fsm would lock the whole fsm, then call process_event, which runs to completion, done.
Unfortunately, there is no "a natural way" for subs to have access to the root... Now I use implementation that recursively goes through the states, but still it is ugly and has restrictions.
Normally, MSM is not thread-safe if that's what you mean...
Clearly, locking such a long time is still undesirable, which is why I went lockfree. Your actions run at the same time as the active state switching. It's really fast, but quite hard to debug and understand. For example, I am quite sure my implementation works for simple fsms, for internal transitions, for orthogonal regions, soon for deferred events and event queue, but my test for submachines does not and I still did not figure out if the code or the test is wrong...
If you want to try it out, I'm interested in your experience.
I'll take a look to msm3 Do you have a roadmap for v3?
Otherwise, it's all I have at the moment, I had no time to push it further yet. My priority at the moment goes to finishing the upcoming asynchronous, which solves this problem nicely (we use it at work for quite some time now) by taking care of enqueing calls between threads. I run my fsms in a single thread, avoiding concurrency on the state machine.
What is "asynchronous"? Looking through recent subjects does reveal nothing. I wanna ask another thing. As I see currently sub-fsms work as unconditional jump when returning. Consider I have a fsm "Foo" with a generic code that isolated to a sub-fsm "Bar" At the transition form Bar's exit point to Foo I don't know about the original state that calls Bar So, if Bar uses more the one time, I have to invent extra conditional/flags etc to make a decision what state to move. Foo's transition table: // Start Event Next Action, Guard // +--------+---------+--------+---------+--------+ Row < State1, evt1, Bar, none, none >, Row < State2, evt2, Bar, none, none >, // +--------+---------+--------+---------+--------+ Row < Bar::Exit, none, State1, none, ??? >, Row < Bar::Exit, none, State2, none, ??? > // +--------+---------+--------+---------+--------+ It would be nice, if the sub-fsm remembers the source state from the outer that causes call. That state can be used in guard.
Actually your example is probably wrong (it's my worst design decision) because the fsm parameter in an action of a submachine itself is the submachine, not the outer. But yes, if you have a pseudo exit then you will call the outer from the sub fsm.
That's my case. Another thread triggers the sub-fsm to move the exit state.
In the meantime I did a bit more and implemented your use case (if I understood it correctly) in the msm3 branch and added a test (https://github.com/boostorg/msm/blob/msm3/test/LockfreeMessageQueue.cpp). I did not try it out with composite states though.
I unsure of what your problem is, you could have your mutex in the outer and it would work. The sub fsm would lock the whole fsm, then call process_event, which runs to completion, done.
Unfortunately, there is no "a natural way" for subs to have access to the root...
True. As I said, it was a mistake, one I want to correct in msm3.
Now I use implementation that recursively goes through the states, but still it is ugly and has restrictions.
The "standard" way is usually to have the outer fsm give a pointer or boost::function to the sub. Not exactly beautiful but at least faster and slightly less ugly.
Normally, MSM is not thread-safe if that's what you mean...
Clearly, locking such a long time is still undesirable, which is why I went lockfree. Your actions run at the same time as the active state switching. It's really fast, but quite hard to debug and understand. For example, I am quite sure my implementation works for simple fsms, for internal transitions, for orthogonal regions, soon for deferred events and event queue, but my test for submachines does not and I still did not figure out if the code or the test is wrong...
If you want to try it out, I'm interested in your experience.
I'll take a look to msm3 Do you have a roadmap for v3?
No. At the moment I do it as low prio because most of my free time goes to my new library, asynchronous. I hope to make some progress on msm3 this year, little by little.
Otherwise, it's all I have at the moment, I had no time to push it further yet. My priority at the moment goes to finishing the upcoming asynchronous, which solves this problem nicely (we use it at work for quite some time now) by taking care of enqueing calls between threads. I run my fsms in a single thread, avoiding concurrency on the state machine.
What is "asynchronous"? Looking through recent subjects does reveal nothing.
Oh, sorry. I made some talks at CppNow! and Meeting C++ last year but did not talk on this list about it yet. You can find the doc at: https://github.com/henry-ch/asynchronous . I think for your use case it might be pretty much what you want. I wanted to do this since I started msm. We now use it heavily at work so it's also good tested. In your case, you'd make a Servant, which would hold your fsm and live in a thread (along with potentially many more). Then your other threads would only get a thread-safe Proxy to it and call the proxy members (say, process_event). The Proxy would enqueue the call into the queue of the thread where your fsm lives, keeping it safe. The library has much more but I hope you get the idea.
I wanna ask another thing. As I see currently sub-fsms work as unconditional jump when returning.
Consider I have a fsm "Foo" with a generic code that isolated to a sub-fsm "Bar"
At the transition form Bar's exit point to Foo I don't know about the original state that calls Bar
So, if Bar uses more the one time, I have to invent extra conditional/flags etc to make a decision what state to move.
Foo's transition table:
// Start Event Next Action, Guard // +--------+---------+--------+---------+--------+ Row < State1, evt1, Bar, none, none >, Row < State2, evt2, Bar, none, none >, // +--------+---------+--------+---------+--------+ Row < Bar::Exit, none, State1, none, ??? >, Row < Bar::Exit, none, State2, none, ??? > // +--------+---------+--------+---------+--------+
It would be nice, if the sub-fsm remembers the source state from the outer that causes call. That state can be used in guard.
Hmm I did not think about this. I need to think about it, maybe some kind of history would do. Normally, I would solve this by entering Bar with an event containing this information, saving it in Bar, then exiting Bar with another event than none, holding this same information back, so it would be usable in a guard. HTH, Christophe
participants (2)
-
christophe.j.henry@gmail.com
-
Sam Fisher