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(T::Execute(), 0, "by Trigger"))
|
---|
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 | dispatch(boost::bind(&StateMachineAsio::Handler, this));
|
---|
37 | }
|
---|
38 |
|
---|
39 | int Run(bool)
|
---|
40 | {
|
---|
41 | fTrigger.expires_from_now(boost::posix_time::microseconds(0));
|
---|
42 | fTrigger.async_wait(boost::bind(&StateMachineAsio::HandleTrigger,
|
---|
43 | this, boost::asio::placeholders::error));
|
---|
44 |
|
---|
45 | T::SetCurrentState(StateMachineImp::kSM_Ready, "by Run()");
|
---|
46 |
|
---|
47 | T::fRunning = true;
|
---|
48 |
|
---|
49 | while (1)
|
---|
50 | {
|
---|
51 | if (run_one()==0)
|
---|
52 | break;
|
---|
53 |
|
---|
54 | if (!HandleNewState(T::Execute(), 0, "by Trigger"))
|
---|
55 | Stop(-1);
|
---|
56 | }
|
---|
57 | reset();
|
---|
58 |
|
---|
59 | T::fRunning = false;
|
---|
60 |
|
---|
61 | if (T::fExitRequested==-1)
|
---|
62 | {
|
---|
63 | T::Fatal("Fatal Error occured... shutting down.");
|
---|
64 | return -1;
|
---|
65 | }
|
---|
66 |
|
---|
67 | T::SetCurrentState(StateMachineImp::kSM_NotReady, "due to return from Run().");
|
---|
68 |
|
---|
69 | const int exitcode = T::fExitRequested-1;
|
---|
70 | T::fExitRequested = 0;
|
---|
71 | return exitcode;
|
---|
72 | }
|
---|
73 |
|
---|
74 |
|
---|
75 | public:
|
---|
76 | StateMachineAsio(std::ostream &out, const std::string &server) :
|
---|
77 | T(out, server), boost::asio::io_service::work(static_cast<boost::asio::io_service&>(*this)),
|
---|
78 | fTrigger(static_cast<boost::asio::io_service&>(*this))
|
---|
79 | {
|
---|
80 | // ba::io_service::work is a kind of keep_alive for the loop.
|
---|
81 | // It prevents the io_service to go to stopped state, which
|
---|
82 | // would prevent any consecutive calls to run()
|
---|
83 | // or poll() to do nothing. reset() could also revoke to the
|
---|
84 | // previous state but this might introduce some overhead of
|
---|
85 | // deletion and creation of threads and more.
|
---|
86 | }
|
---|
87 |
|
---|
88 | void Stop(int code=0)
|
---|
89 | {
|
---|
90 | T::Stop(code);
|
---|
91 | stop();
|
---|
92 | }
|
---|
93 | };
|
---|
94 |
|
---|
95 | #endif
|
---|