简体   繁体   中英

How can I run several instance of the same sub state machine with MSM boost library

I am getting familiar with MSM boost library. I have found this really interesting questions about joining several orthogonal states.

Also, I have found in the boost documentation a way to reuse an sub state machine within a parent one and I have coded a couple of simple nested states machine.

However, I cannot wrap my head around how to mix this two things together.

My goal is to fork to several independent instances of the same sub state machine and wait till of them have finished before returning the control to the parent one.

However, the boost documentation says that it is not possible to fork to sub state machine directly.

Note (also valid for forks): At the moment, it is not possible to use a submachine as the target of an explicit entry. Please use entry pseudo states for an almost identical effect.

Example

Buying a ticket plane can be model as sub state machine, you request a price, then perform some evaluation and than you can purchase it (cancellation and no completion of order are also possible). But the same logic can be apply with several airline companies. Now, let's assume that we want to create a software that scan the market and book a certain amount of tickets from different providers. When we receive the order from the customer, we query the market and we decide to buy from 4 different airlines (the customer doesn't mind it). Now, the 4 sub state machine will work in parallel and the control will return back to parent state machine only when the last order is completed. (I know the example is not great, but I hope you got the idea).

Questions

Is it possible to fork and use several instance of the same state machine (or achieve the almost identical effect with pseudo states)? How do the state machine knows on which of the sub state machine an event is referring to?

If it is possible, could you please point me in to right direction? I know about this boost documentation example , but it doesn't really address my issue.

I am at the early stage of my project, so I can still move to a different state machine library if necessary (boost statechart is under evaluation as well).

I think that orthogonal states are worked well with different parts of states but handle the same events. In your case, you need to multiple state machine instances. In order to solve your case, I think that defining two different state machine is better approach. They don't have parent and sub relationship but sending event each other.

Let's say the parent state machine as manager, and sub state machine as worker. Here are two separated state machines:

worker    

    +-----------+
 +--|processing |
 |  |           |
 |  +-----------+
 |        |
 |        | e_found(price) / a_notify(price) (send e_found(price) to the manager
 |        V
 |  +-----------+
 |  |processed  |
 |  +-----------+
 |e_cancel|
 |        V
 |  +-----------+
 +->|end        |
    +-----------+


manager

    +-------------------------------------------------------+
    |comparing                                              |
    |entry/create 4 workers                                 |
    |e_found(price) / increment count and compare price, etc|
    +-------------------------------------------------------+
          | e_cancel/ propagate e_cancel to all workers
          V
    +-----------+
    |end        |
    +-----------+

I believe that the state machines illustrate your example scenario. And here is the code: http://melpon.org/wandbox/permlink/JLkDaZBpvnAYLn5E

Note: The above online compiler output is sometimes unstable, but I checked the code on my local environment and works fine.

I send a ticket price found event in the main(). It's not real. Maybe receiving price data from other part of the system. But it's not a essential point.

In order to send event between the state machines, I use shared_ptr of msm::back::state_machine. I need to get the backend of the state machine to call process_event, but template parameter Fsm is the frontend. So I embed weak_ptr of the backend of the state machine into the frontend of the state machine as a member variable. When I want to call process_event(), getting shared_ptr of the backend using get_sm() member function.

In this scenario, cancel event is sent from manager to worker. And found event is sent from worker to manager.

If you want to run workers as different threads, you need to use mutex as follows:

http://melpon.org/wandbox/permlink/TrexYnffiwEpazNk

See line 100 and 192.

Cancel event is handled by manager and propagate to all workers. To test cancel operation, you can insert cancel event as follows:

int main() {
    auto m = manager::create();
    m->workers_[0]->process_event(e_found {10});
    m->workers_[1]->process_event(e_found {20});
    m->process_event(e_cancel()); // Cancel !!
    m->workers_[2]->process_event(e_found {30});
    m->workers_[3]->process_event(e_found {40});
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM