1 | #ifndef FACT_StateMachineAsio
|
---|
2 | #define FACT_StateMachineAsio
|
---|
3 |
|
---|
4 | #include <boost/asio.hpp>
|
---|
5 | #include <boost/bind.hpp>
|
---|
6 |
|
---|
7 | template <class T>
|
---|
8 | class StateMachineAsio : public T, public boost::asio::io_service, public boost::asio::io_service::work
|
---|
9 | {
|
---|
10 | boost::asio::deadline_timer fTrigger;
|
---|
11 |
|
---|
12 | void HandleTrigger(const boost::system::error_code &error)
|
---|
13 | {
|
---|
14 | // 125: Operation canceled (bs::error_code(125, bs::system_category))
|
---|
15 | if (error && error!=boost::asio::error::basic_errors::operation_aborted)
|
---|
16 | return;
|
---|
17 |
|
---|
18 | fTrigger.expires_from_now(boost::posix_time::microseconds(10000));
|
---|
19 | fTrigger.async_wait(boost::bind(&StateMachineAsio::HandleTrigger,
|
---|
20 | this, boost::asio::placeholders::error));
|
---|
21 |
|
---|
22 | if (!HandleNewState(Execute(), 0, "by HandleTrigger()"))
|
---|
23 | Stop(-1);
|
---|
24 | }
|
---|
25 |
|
---|
26 | void Handler()
|
---|
27 | {
|
---|
28 | const auto ptr = T::PopEvent();
|
---|
29 | if (!T::HandleEvent(*ptr))
|
---|
30 | Stop(-1);
|
---|
31 | }
|
---|
32 |
|
---|
33 | void PushEvent(Event *cmd)
|
---|
34 | {
|
---|
35 | T::PushEvent(cmd);
|
---|
36 | post(boost::bind(&StateMachineAsio::Handler, this));
|
---|
37 | }
|
---|
38 |
|
---|
39 | int Execute()=0;
|
---|
40 |
|
---|
41 | int Run(bool)
|
---|
42 | {
|
---|
43 | fTrigger.expires_from_now(boost::posix_time::microseconds(0));
|
---|
44 | fTrigger.async_wait(boost::bind(&StateMachineAsio::HandleTrigger,
|
---|
45 | this, boost::asio::placeholders::error));
|
---|
46 |
|
---|
47 | T::SetCurrentState(StateMachineImp::kSM_Ready, "by Run()");
|
---|
48 |
|
---|
49 | T::fRunning = true;
|
---|
50 |
|
---|
51 | while (run_one())
|
---|
52 | {
|
---|
53 | if (!HandleNewState(Execute(), 0, "by Run()"))
|
---|
54 | Stop(-1);
|
---|
55 | }
|
---|
56 | reset();
|
---|
57 |
|
---|
58 | T::fRunning = false;
|
---|
59 |
|
---|
60 | if (T::fExitRequested==-1)
|
---|
61 | {
|
---|
62 | T::Fatal("Fatal Error occured... shutting down.");
|
---|
63 | return -1;
|
---|
64 | }
|
---|
65 |
|
---|
66 | T::SetCurrentState(StateMachineImp::kSM_NotReady, "due to return from Run().");
|
---|
67 |
|
---|
68 | const int exitcode = T::fExitRequested-1;
|
---|
69 | T::fExitRequested = 0;
|
---|
70 | return exitcode;
|
---|
71 | }
|
---|
72 |
|
---|
73 |
|
---|
74 | public:
|
---|
75 | StateMachineAsio(std::ostream &out, const std::string &server) :
|
---|
76 | T(out, server), boost::asio::io_service::work(static_cast<boost::asio::io_service&>(*this)),
|
---|
77 | fTrigger(static_cast<boost::asio::io_service&>(*this))
|
---|
78 | {
|
---|
79 | // ba::io_service::work is a kind of keep_alive for the loop.
|
---|
80 | // It prevents the io_service to go to stopped state, which
|
---|
81 | // would prevent any consecutive calls to run()
|
---|
82 | // or poll() to do nothing. reset() could also revoke to the
|
---|
83 | // previous state but this might introduce some overhead of
|
---|
84 | // deletion and creation of threads and more.
|
---|
85 | }
|
---|
86 |
|
---|
87 | void Stop(int code=0)
|
---|
88 | {
|
---|
89 | T::Stop(code);
|
---|
90 | stop();
|
---|
91 | }
|
---|
92 | };
|
---|
93 |
|
---|
94 | #endif
|
---|