1 //
2 // prioritised_handlers.cpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 //
10 
11 #include <boost/asio.hpp>
12 #include <boost/function.hpp>
13 #include <iostream>
14 #include <queue>
15 
16 using boost::asio::ip::tcp;
17 
18 class handler_priority_queue : public boost::asio::execution_context
19 {
20 public:
add(int priority,boost::function<void ()> function)21   void add(int priority, boost::function<void()> function)
22   {
23     handlers_.push(queued_handler(priority, function));
24   }
25 
execute_all()26   void execute_all()
27   {
28     while (!handlers_.empty())
29     {
30       queued_handler handler = handlers_.top();
31       handler.execute();
32       handlers_.pop();
33     }
34   }
35 
36   class executor
37   {
38   public:
executor(handler_priority_queue & q,int p)39     executor(handler_priority_queue& q, int p)
40       : context_(q), priority_(p)
41     {
42     }
43 
context() const44     handler_priority_queue& context() const
45     {
46       return context_;
47     }
48 
49     template <typename Function, typename Allocator>
dispatch(const Function & f,const Allocator &) const50     void dispatch(const Function& f, const Allocator&) const
51     {
52       context_.add(priority_, f);
53     }
54 
55     template <typename Function, typename Allocator>
post(const Function & f,const Allocator &) const56     void post(const Function& f, const Allocator&) const
57     {
58       context_.add(priority_, f);
59     }
60 
61     template <typename Function, typename Allocator>
defer(const Function & f,const Allocator &) const62     void defer(const Function& f, const Allocator&) const
63     {
64       context_.add(priority_, f);
65     }
66 
on_work_started() const67     void on_work_started() const {}
on_work_finished() const68     void on_work_finished() const {}
69 
operator ==(const executor & other) const70     bool operator==(const executor& other) const
71     {
72       return &context_ == &other.context_ && priority_ == other.priority_;
73     }
74 
operator !=(const executor & other) const75     bool operator!=(const executor& other) const
76     {
77       return !operator==(other);
78     }
79 
80   private:
81     handler_priority_queue& context_;
82     int priority_;
83   };
84 
85   template <typename Handler>
86   boost::asio::executor_binder<Handler, executor>
wrap(int priority,Handler handler)87   wrap(int priority, Handler handler)
88   {
89     return boost::asio::bind_executor(executor(*this, priority), handler);
90   }
91 
92 private:
93   class queued_handler
94   {
95   public:
queued_handler(int p,boost::function<void ()> f)96     queued_handler(int p, boost::function<void()> f)
97       : priority_(p), function_(f)
98     {
99     }
100 
execute()101     void execute()
102     {
103       function_();
104     }
105 
operator <(const queued_handler & a,const queued_handler & b)106     friend bool operator<(const queued_handler& a,
107         const queued_handler& b)
108     {
109       return a.priority_ < b.priority_;
110     }
111 
112   private:
113     int priority_;
114     boost::function<void()> function_;
115   };
116 
117   std::priority_queue<queued_handler> handlers_;
118 };
119 
120 //----------------------------------------------------------------------
121 
high_priority_handler(const boost::system::error_code &)122 void high_priority_handler(const boost::system::error_code& /*ec*/)
123 {
124   std::cout << "High priority handler\n";
125 }
126 
middle_priority_handler(const boost::system::error_code &)127 void middle_priority_handler(const boost::system::error_code& /*ec*/)
128 {
129   std::cout << "Middle priority handler\n";
130 }
131 
low_priority_handler()132 void low_priority_handler()
133 {
134   std::cout << "Low priority handler\n";
135 }
136 
main()137 int main()
138 {
139   boost::asio::io_context io_context;
140 
141   handler_priority_queue pri_queue;
142 
143   // Post a completion handler to be run immediately.
144   boost::asio::post(io_context, pri_queue.wrap(0, low_priority_handler));
145 
146   // Start an asynchronous accept that will complete immediately.
147   tcp::endpoint endpoint(boost::asio::ip::address_v4::loopback(), 0);
148   tcp::acceptor acceptor(io_context, endpoint);
149   tcp::socket server_socket(io_context);
150   acceptor.async_accept(server_socket,
151       pri_queue.wrap(100, high_priority_handler));
152   tcp::socket client_socket(io_context);
153   client_socket.connect(acceptor.local_endpoint());
154 
155   // Set a deadline timer to expire immediately.
156   boost::asio::steady_timer timer(io_context);
157   timer.expires_at(boost::asio::steady_timer::time_point::min());
158   timer.async_wait(pri_queue.wrap(42, middle_priority_handler));
159 
160   while (io_context.run_one())
161   {
162     // The custom invocation hook adds the handlers to the priority queue
163     // rather than executing them from within the poll_one() call.
164     while (io_context.poll_one())
165       ;
166 
167     pri_queue.execute_all();
168   }
169 
170   return 0;
171 }
172