eventfd_select_interrupter.hpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. //
  2. // eventfd_select_interrupter.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. // Copyright (c) 2008 Roelof Naude (roelof.naude at gmail dot com)
  7. //
  8. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  9. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  10. //
  11. #ifndef BOOST_ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP
  12. #define BOOST_ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP
  13. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  14. # pragma once
  15. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  16. #include <boost/asio/detail/push_options.hpp>
  17. #include <boost/asio/detail/push_options.hpp>
  18. #include <boost/config.hpp>
  19. #include <boost/throw_exception.hpp>
  20. #include <boost/system/system_error.hpp>
  21. #include <boost/asio/detail/pop_options.hpp>
  22. #if defined(linux)
  23. # if !defined(BOOST_ASIO_DISABLE_EVENTFD)
  24. # include <linux/version.h>
  25. # if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
  26. # define BOOST_ASIO_HAS_EVENTFD
  27. # endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
  28. # endif // !defined(BOOST_ASIO_DISABLE_EVENTFD)
  29. #endif // defined(linux)
  30. #if defined(BOOST_ASIO_HAS_EVENTFD)
  31. #include <boost/asio/detail/push_options.hpp>
  32. #include <fcntl.h>
  33. #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
  34. # include <asm/unistd.h>
  35. #else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
  36. # include <sys/eventfd.h>
  37. #endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
  38. #include <boost/asio/detail/pop_options.hpp>
  39. #include <boost/asio/error.hpp>
  40. #include <boost/asio/detail/socket_types.hpp>
  41. namespace boost {
  42. namespace asio {
  43. namespace detail {
  44. class eventfd_select_interrupter
  45. {
  46. public:
  47. // Constructor.
  48. eventfd_select_interrupter()
  49. {
  50. #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
  51. write_descriptor_ = read_descriptor_ = syscall(__NR_eventfd, 0);
  52. #else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
  53. write_descriptor_ = read_descriptor_ = ::eventfd(0, 0);
  54. #endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
  55. if (read_descriptor_ != -1)
  56. {
  57. ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK);
  58. }
  59. else
  60. {
  61. int pipe_fds[2];
  62. if (pipe(pipe_fds) == 0)
  63. {
  64. read_descriptor_ = pipe_fds[0];
  65. ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK);
  66. write_descriptor_ = pipe_fds[1];
  67. ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK);
  68. }
  69. else
  70. {
  71. boost::system::error_code ec(errno,
  72. boost::asio::error::get_system_category());
  73. boost::system::system_error e(ec, "eventfd_select_interrupter");
  74. boost::throw_exception(e);
  75. }
  76. }
  77. }
  78. // Destructor.
  79. ~eventfd_select_interrupter()
  80. {
  81. if (write_descriptor_ != -1 && write_descriptor_ != read_descriptor_)
  82. ::close(write_descriptor_);
  83. if (read_descriptor_ != -1)
  84. ::close(read_descriptor_);
  85. }
  86. // Interrupt the select call.
  87. void interrupt()
  88. {
  89. uint64_t counter(1UL);
  90. int result = ::write(write_descriptor_, &counter, sizeof(uint64_t));
  91. (void)result;
  92. }
  93. // Reset the select interrupt. Returns true if the call was interrupted.
  94. bool reset()
  95. {
  96. if (write_descriptor_ == read_descriptor_)
  97. {
  98. // Only perform one read. The kernel maintains an atomic counter.
  99. uint64_t counter(0);
  100. int bytes_read = ::read(read_descriptor_, &counter, sizeof(uint64_t));
  101. bool was_interrupted = (bytes_read > 0);
  102. return was_interrupted;
  103. }
  104. else
  105. {
  106. // Clear all data from the pipe.
  107. char data[1024];
  108. int bytes_read = ::read(read_descriptor_, data, sizeof(data));
  109. bool was_interrupted = (bytes_read > 0);
  110. while (bytes_read == sizeof(data))
  111. bytes_read = ::read(read_descriptor_, data, sizeof(data));
  112. return was_interrupted;
  113. }
  114. }
  115. // Get the read descriptor to be passed to select.
  116. int read_descriptor() const
  117. {
  118. return read_descriptor_;
  119. }
  120. private:
  121. // The read end of a connection used to interrupt the select call. This file
  122. // descriptor is passed to select such that when it is time to stop, a single
  123. // 64bit value will be written on the other end of the connection and this
  124. // descriptor will become readable.
  125. int read_descriptor_;
  126. // The write end of a connection used to interrupt the select call. A single
  127. // 64bit non-zero value may be written to this to wake up the select which is
  128. // waiting for the other end to become readable. This descriptor will only
  129. // differ from the read descriptor when a pipe is used.
  130. int write_descriptor_;
  131. };
  132. } // namespace detail
  133. } // namespace asio
  134. } // namespace boost
  135. #endif // defined(BOOST_ASIO_HAS_EVENTFD)
  136. #include <boost/asio/detail/pop_options.hpp>
  137. #endif // BOOST_ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP