1 | // |
2 | // detail/impl/eventfd_select_interrupter.ipp |
3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
4 | // |
5 | // Copyright (c) 2003-2015 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 | |
12 | #ifndef BOOST_ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP |
13 | #define BOOST_ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP |
14 | |
15 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) |
16 | # pragma once |
17 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) |
18 | |
19 | #include <boost/asio/detail/config.hpp> |
20 | |
21 | #if defined(BOOST_ASIO_HAS_EVENTFD) |
22 | |
23 | #include <sys/stat.h> |
24 | #include <sys/types.h> |
25 | #include <fcntl.h> |
26 | #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 |
27 | # include <asm/unistd.h> |
28 | #else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 |
29 | # include <sys/eventfd.h> |
30 | #endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 |
31 | #include <boost/asio/detail/cstdint.hpp> |
32 | #include <boost/asio/detail/eventfd_select_interrupter.hpp> |
33 | #include <boost/asio/detail/throw_error.hpp> |
34 | #include <boost/asio/error.hpp> |
35 | |
36 | #include <boost/asio/detail/push_options.hpp> |
37 | |
38 | namespace boost { |
39 | namespace asio { |
40 | namespace detail { |
41 | |
42 | eventfd_select_interrupter::eventfd_select_interrupter() |
43 | { |
44 | open_descriptors(); |
45 | } |
46 | |
47 | void eventfd_select_interrupter::open_descriptors() |
48 | { |
49 | #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 |
50 | write_descriptor_ = read_descriptor_ = syscall(__NR_eventfd, 0); |
51 | if (read_descriptor_ != -1) |
52 | { |
53 | ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK); |
54 | ::fcntl(read_descriptor_, F_SETFD, FD_CLOEXEC); |
55 | } |
56 | #else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 |
57 | # if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) |
58 | write_descriptor_ = read_descriptor_ = |
59 | ::eventfd(count: 0, EFD_CLOEXEC | EFD_NONBLOCK); |
60 | # else // defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) |
61 | errno = EINVAL; |
62 | write_descriptor_ = read_descriptor_ = -1; |
63 | # endif // defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK) |
64 | if (read_descriptor_ == -1 && errno == EINVAL) |
65 | { |
66 | write_descriptor_ = read_descriptor_ = ::eventfd(count: 0, flags: 0); |
67 | if (read_descriptor_ != -1) |
68 | { |
69 | ::fcntl(fd: read_descriptor_, F_SETFL, O_NONBLOCK); |
70 | ::fcntl(fd: read_descriptor_, F_SETFD, FD_CLOEXEC); |
71 | } |
72 | } |
73 | #endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 |
74 | |
75 | if (read_descriptor_ == -1) |
76 | { |
77 | int pipe_fds[2]; |
78 | if (pipe(pipedes: pipe_fds) == 0) |
79 | { |
80 | read_descriptor_ = pipe_fds[0]; |
81 | ::fcntl(fd: read_descriptor_, F_SETFL, O_NONBLOCK); |
82 | ::fcntl(fd: read_descriptor_, F_SETFD, FD_CLOEXEC); |
83 | write_descriptor_ = pipe_fds[1]; |
84 | ::fcntl(fd: write_descriptor_, F_SETFL, O_NONBLOCK); |
85 | ::fcntl(fd: write_descriptor_, F_SETFD, FD_CLOEXEC); |
86 | } |
87 | else |
88 | { |
89 | boost::system::error_code ec(errno, |
90 | boost::asio::error::get_system_category()); |
91 | boost::asio::detail::throw_error(err: ec, location: "eventfd_select_interrupter" ); |
92 | } |
93 | } |
94 | } |
95 | |
96 | eventfd_select_interrupter::~eventfd_select_interrupter() |
97 | { |
98 | close_descriptors(); |
99 | } |
100 | |
101 | void eventfd_select_interrupter::close_descriptors() |
102 | { |
103 | if (write_descriptor_ != -1 && write_descriptor_ != read_descriptor_) |
104 | ::close(fd: write_descriptor_); |
105 | if (read_descriptor_ != -1) |
106 | ::close(fd: read_descriptor_); |
107 | } |
108 | |
109 | void eventfd_select_interrupter::recreate() |
110 | { |
111 | close_descriptors(); |
112 | |
113 | write_descriptor_ = -1; |
114 | read_descriptor_ = -1; |
115 | |
116 | open_descriptors(); |
117 | } |
118 | |
119 | void eventfd_select_interrupter::interrupt() |
120 | { |
121 | uint64_t counter(1UL); |
122 | int result = ::write(fd: write_descriptor_, buf: &counter, n: sizeof(uint64_t)); |
123 | (void)result; |
124 | } |
125 | |
126 | bool eventfd_select_interrupter::reset() |
127 | { |
128 | if (write_descriptor_ == read_descriptor_) |
129 | { |
130 | for (;;) |
131 | { |
132 | // Only perform one read. The kernel maintains an atomic counter. |
133 | uint64_t counter(0); |
134 | errno = 0; |
135 | int bytes_read = ::read(fd: read_descriptor_, buf: &counter, nbytes: sizeof(uint64_t)); |
136 | if (bytes_read < 0 && errno == EINTR) |
137 | continue; |
138 | bool was_interrupted = (bytes_read > 0); |
139 | return was_interrupted; |
140 | } |
141 | } |
142 | else |
143 | { |
144 | for (;;) |
145 | { |
146 | // Clear all data from the pipe. |
147 | char data[1024]; |
148 | int bytes_read = ::read(fd: read_descriptor_, buf: data, nbytes: sizeof(data)); |
149 | if (bytes_read < 0 && errno == EINTR) |
150 | continue; |
151 | bool was_interrupted = (bytes_read > 0); |
152 | while (bytes_read == sizeof(data)) |
153 | bytes_read = ::read(fd: read_descriptor_, buf: data, nbytes: sizeof(data)); |
154 | return was_interrupted; |
155 | } |
156 | } |
157 | } |
158 | |
159 | } // namespace detail |
160 | } // namespace asio |
161 | } // namespace boost |
162 | |
163 | #include <boost/asio/detail/pop_options.hpp> |
164 | |
165 | #endif // defined(BOOST_ASIO_HAS_EVENTFD) |
166 | |
167 | #endif // BOOST_ASIO_DETAIL_IMPL_EVENTFD_SELECT_INTERRUPTER_IPP |
168 | |