prioritised_handlers.cpp 4.7 KB

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