coro_signal.hpp 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. #ifndef SOPHIAR2_CORO_SIGNAL_HPP
  2. #define SOPHIAR2_CORO_SIGNAL_HPP
  3. #include <boost/asio/awaitable.hpp>
  4. #include <boost/asio/experimental/channel.hpp>
  5. #include <boost/asio/io_context.hpp>
  6. #include <boost/asio/use_awaitable.hpp>
  7. #include <boost/core/noncopyable.hpp>
  8. #include <boost/system/error_code.hpp>
  9. #include <cassert>
  10. namespace sophiar {
  11. class coro_signal : private boost::noncopyable {
  12. public:
  13. class signal_token : private boost::noncopyable {
  14. public:
  15. explicit signal_token(coro_signal &_s)
  16. : signal(_s) {
  17. ++signal.waiting_count;
  18. }
  19. ~signal_token() {
  20. if (!is_fulfilled) {
  21. --signal.waiting_count;
  22. }
  23. // handle leaking signal
  24. if (signal.waiting_count == 0 &&
  25. signal.channel.ready()) {
  26. signal.channel.reset();
  27. signal.is_notifying = false;
  28. }
  29. }
  30. void fulfill() {
  31. is_fulfilled = true;
  32. --signal.waiting_count;
  33. }
  34. private:
  35. coro_signal &signal;
  36. bool is_fulfilled = false;
  37. };
  38. coro_signal(boost::asio::io_context &context)
  39. : channel(context, 1) {}
  40. auto new_token() {
  41. return signal_token{*this};
  42. }
  43. boost::asio::awaitable<void> coro_wait(signal_token &token) {
  44. co_await channel.async_receive(boost::asio::use_awaitable);
  45. token.fulfill();
  46. try_notify_more();
  47. co_return;
  48. }
  49. boost::asio::awaitable<void> coro_wait() {
  50. signal_token token(*this);
  51. co_await coro_wait(token);
  52. co_return;
  53. }
  54. bool try_wait(signal_token &token) {
  55. if (!channel.ready()) return false;
  56. token.fulfill();
  57. try_notify_more();
  58. return true;
  59. }
  60. void try_notify_all() {
  61. if (is_notifying || waiting_count == 0) return;
  62. is_notifying = true;
  63. send_signal();
  64. }
  65. private:
  66. using channel_type = boost::asio::experimental::channel<
  67. void(boost::system::error_code, bool)>;
  68. channel_type channel;
  69. size_t waiting_count = 0;
  70. bool is_notifying = false;
  71. void send_signal() {
  72. assert(!channel.ready());
  73. channel.try_send(boost::system::error_code{}, true);
  74. }
  75. void try_notify_more() {
  76. if (waiting_count > 0) {
  77. send_signal();
  78. } else {
  79. is_notifying = false;
  80. }
  81. }
  82. };
  83. }
  84. #endif //SOPHIAR2_CORO_SIGNAL_HPP