]>
Commit | Line | Data |
---|---|---|
12471842 PL |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
870a0fe4 AT |
22 | #ifdef HAVE_CONFIG_H |
23 | #include "config.h" | |
24 | #endif | |
a1dfcec8 BH |
25 | #include "mplexer.hh" |
26 | #include "sstuff.hh" | |
27 | #include <iostream> | |
28 | #include <unistd.h> | |
29 | #include "misc.hh" | |
3c19a35f | 30 | #ifdef __linux__ |
a1dfcec8 | 31 | #include <sys/epoll.h> |
3c19a35f | 32 | #endif |
a1dfcec8 | 33 | |
10f4eea8 | 34 | #include "namespaces.hh" |
a1dfcec8 | 35 | |
a1dfcec8 BH |
36 | class EpollFDMultiplexer : public FDMultiplexer |
37 | { | |
38 | public: | |
7c58a81f | 39 | EpollFDMultiplexer(unsigned int maxEventsHint); |
9a315393 | 40 | ~EpollFDMultiplexer() override |
a1dfcec8 | 41 | { |
e16e673d RG |
42 | if (d_epollfd >= 0) { |
43 | close(d_epollfd); | |
44 | } | |
a1dfcec8 BH |
45 | } |
46 | ||
e16e673d RG |
47 | int run(struct timeval* tv, int timeout = 500) override; |
48 | void getAvailableFDs(std::vector<int>& fds, int timeout) override; | |
a1dfcec8 | 49 | |
e16e673d RG |
50 | void addFD(int fd, FDMultiplexer::EventKind kind) override; |
51 | void removeFD(int fd, FDMultiplexer::EventKind kind) override; | |
510d368d | 52 | void alterFD(int fd, FDMultiplexer::EventKind from, FDMultiplexer::EventKind to) override; |
92329526 | 53 | |
5bdbb83d | 54 | string getName() const override |
1f4abb20 BH |
55 | { |
56 | return "epoll"; | |
57 | } | |
e16e673d | 58 | |
a1dfcec8 BH |
59 | private: |
60 | int d_epollfd; | |
d651ad51 | 61 | std::vector<epoll_event> d_eevents; |
a1dfcec8 BH |
62 | }; |
63 | ||
7c58a81f | 64 | static FDMultiplexer* makeEpoll(unsigned int maxEventsHint) |
a1dfcec8 | 65 | { |
7c58a81f | 66 | return new EpollFDMultiplexer(maxEventsHint); |
a1dfcec8 BH |
67 | } |
68 | ||
aa136564 | 69 | static struct EpollRegisterOurselves |
1f4abb20 | 70 | { |
e16e673d RG |
71 | EpollRegisterOurselves() |
72 | { | |
d90323cc | 73 | FDMultiplexer::getMultiplexerMap().emplace(0, &makeEpoll); // priority 0! |
1f4abb20 | 74 | } |
aa136564 | 75 | } doItEpoll; |
1f4abb20 | 76 | |
7c58a81f RG |
77 | EpollFDMultiplexer::EpollFDMultiplexer(unsigned int maxEventsHint) : |
78 | d_eevents(maxEventsHint) | |
a1dfcec8 | 79 | { |
7c58a81f | 80 | d_epollfd = epoll_create(static_cast<int>(maxEventsHint)); // not hard max, just a hint that is actually ignored since Linux 2.6.8 |
e16e673d RG |
81 | if (d_epollfd < 0) { |
82 | throw FDMultiplexerException("Setting up epoll: " + stringerror()); | |
83 | } | |
84 | int fd = socket(AF_INET, SOCK_DGRAM, 0); // for self-test | |
85 | ||
86 | if (fd < 0) { | |
98d0ee4a | 87 | return; |
e16e673d RG |
88 | } |
89 | ||
98d0ee4a BH |
90 | try { |
91 | addReadFD(fd, 0); | |
92 | removeReadFD(fd); | |
93 | close(fd); | |
94 | return; | |
95 | } | |
e16e673d | 96 | catch (const FDMultiplexerException& fe) { |
98d0ee4a | 97 | close(fd); |
0a7f24cb | 98 | close(d_epollfd); |
e16e673d | 99 | throw FDMultiplexerException("epoll multiplexer failed self-test: " + string(fe.what())); |
98d0ee4a | 100 | } |
a1dfcec8 BH |
101 | } |
102 | ||
e16e673d | 103 | static uint32_t convertEventKind(FDMultiplexer::EventKind kind) |
a1dfcec8 | 104 | { |
e16e673d RG |
105 | switch (kind) { |
106 | case FDMultiplexer::EventKind::Read: | |
107 | return EPOLLIN; | |
108 | case FDMultiplexer::EventKind::Write: | |
109 | return EPOLLOUT; | |
110 | case FDMultiplexer::EventKind::Both: | |
111 | return EPOLLIN | EPOLLOUT; | |
112 | } | |
4f579ed8 RG |
113 | |
114 | throw std::runtime_error("Unhandled event kind in the epoll multiplexer"); | |
e16e673d | 115 | } |
a1dfcec8 | 116 | |
e16e673d RG |
117 | void EpollFDMultiplexer::addFD(int fd, FDMultiplexer::EventKind kind) |
118 | { | |
a1dfcec8 | 119 | struct epoll_event eevent; |
92329526 | 120 | |
e16e673d | 121 | eevent.events = convertEventKind(kind); |
92329526 | 122 | |
e16e673d RG |
123 | eevent.data.u64 = 0; // placate valgrind (I love it so much) |
124 | eevent.data.fd = fd; | |
a1dfcec8 | 125 | |
b7eb6b6e | 126 | if (epoll_ctl(d_epollfd, EPOLL_CTL_ADD, fd, &eevent) < 0) { |
e16e673d | 127 | throw FDMultiplexerException("Adding fd to epoll set: " + stringerror()); |
c454d11b | 128 | } |
a1dfcec8 BH |
129 | } |
130 | ||
e16e673d | 131 | void EpollFDMultiplexer::removeFD(int fd, FDMultiplexer::EventKind) |
a1dfcec8 | 132 | { |
3368984f BH |
133 | struct epoll_event dummy; |
134 | dummy.events = 0; | |
135 | dummy.data.u64 = 0; | |
136 | ||
e16e673d RG |
137 | if (epoll_ctl(d_epollfd, EPOLL_CTL_DEL, fd, &dummy) < 0) { |
138 | throw FDMultiplexerException("Removing fd from epoll set: " + stringerror()); | |
139 | } | |
a1dfcec8 BH |
140 | } |
141 | ||
510d368d | 142 | void EpollFDMultiplexer::alterFD(int fd, FDMultiplexer::EventKind, FDMultiplexer::EventKind to) |
92329526 | 143 | { |
92329526 | 144 | struct epoll_event eevent; |
510d368d | 145 | eevent.events = convertEventKind(to); |
92329526 RG |
146 | eevent.data.u64 = 0; // placate valgrind (I love it so much) |
147 | eevent.data.fd = fd; | |
148 | ||
149 | if (epoll_ctl(d_epollfd, EPOLL_CTL_MOD, fd, &eevent) < 0) { | |
e16e673d | 150 | throw FDMultiplexerException("Altering fd in epoll set: " + stringerror()); |
92329526 RG |
151 | } |
152 | } | |
153 | ||
5bdbb83d RG |
154 | void EpollFDMultiplexer::getAvailableFDs(std::vector<int>& fds, int timeout) |
155 | { | |
7c58a81f | 156 | int ret = epoll_wait(d_epollfd, d_eevents.data(), d_eevents.size(), timeout); |
5bdbb83d | 157 | |
e16e673d RG |
158 | if (ret < 0 && errno != EINTR) { |
159 | throw FDMultiplexerException("epoll returned error: " + stringerror()); | |
160 | } | |
5bdbb83d | 161 | |
e16e673d | 162 | for (int n = 0; n < ret; ++n) { |
5bdbb83d RG |
163 | fds.push_back(d_eevents[n].data.fd); |
164 | } | |
165 | } | |
166 | ||
0e663c3b | 167 | int EpollFDMultiplexer::run(struct timeval* now, int timeout) |
a1dfcec8 | 168 | { |
e16e673d | 169 | if (d_inrun) { |
a1dfcec8 BH |
170 | throw FDMultiplexerException("FDMultiplexer::run() is not reentrant!\n"); |
171 | } | |
92329526 | 172 | |
7c58a81f | 173 | int ret = epoll_wait(d_epollfd, d_eevents.data(), d_eevents.size(), timeout); |
e16e673d | 174 | gettimeofday(now, nullptr); // MANDATORY |
5bdbb83d | 175 | |
e16e673d RG |
176 | if (ret < 0 && errno != EINTR) { |
177 | throw FDMultiplexerException("epoll returned error: " + stringerror()); | |
178 | } | |
a1dfcec8 | 179 | |
e16e673d | 180 | if (ret < 1) { // thanks AB! |
a1dfcec8 | 181 | return 0; |
e16e673d | 182 | } |
a1dfcec8 | 183 | |
e16e673d | 184 | d_inrun = true; |
c7a9f1b4 | 185 | int count = 0; |
e16e673d RG |
186 | for (int n = 0; n < ret; ++n) { |
187 | if ((d_eevents[n].events & EPOLLIN) || (d_eevents[n].events & EPOLLERR) || (d_eevents[n].events & EPOLLHUP)) { | |
188 | const auto& iter = d_readCallbacks.find(d_eevents[n].data.fd); | |
189 | if (iter != d_readCallbacks.end()) { | |
190 | iter->d_callback(iter->d_fd, iter->d_parameter); | |
c7a9f1b4 | 191 | count++; |
e16e673d | 192 | } |
0bff046b | 193 | } |
92329526 | 194 | |
e16e673d RG |
195 | if ((d_eevents[n].events & EPOLLOUT) || (d_eevents[n].events & EPOLLERR) || (d_eevents[n].events & EPOLLHUP)) { |
196 | const auto& iter = d_writeCallbacks.find(d_eevents[n].data.fd); | |
197 | if (iter != d_writeCallbacks.end()) { | |
198 | iter->d_callback(iter->d_fd, iter->d_parameter); | |
c7a9f1b4 | 199 | count++; |
e16e673d | 200 | } |
0bff046b | 201 | } |
a1dfcec8 | 202 | } |
e16e673d RG |
203 | |
204 | d_inrun = false; | |
c7a9f1b4 | 205 | return count; |
a1dfcec8 BH |
206 | } |
207 | ||
208 | #if 0 | |
d8f6d49f | 209 | void acceptData(int fd, funcparam_t& parameter) |
a1dfcec8 BH |
210 | { |
211 | cout<<"Have data on fd "<<fd<<endl; | |
d8f6d49f | 212 | Socket* sock=funcparam_t_cast<Socket*>(parameter); |
a1dfcec8 BH |
213 | string packet; |
214 | IPEndpoint rem; | |
215 | sock->recvFrom(packet, rem); | |
216 | cout<<"Received "<<packet.size()<<" bytes!\n"; | |
217 | } | |
218 | ||
219 | ||
220 | int main() | |
221 | { | |
a5794017 | 222 | Socket s(AF_INET, SOCK_DGRAM); |
92329526 | 223 | |
a1dfcec8 BH |
224 | IPEndpoint loc("0.0.0.0", 2000); |
225 | s.bind(loc); | |
226 | ||
227 | EpollFDMultiplexer sfm; | |
228 | ||
229 | sfm.addReadFD(s.getHandle(), &acceptData, &s); | |
230 | ||
231 | for(int n=0; n < 100 ; ++n) { | |
232 | sfm.run(); | |
233 | } | |
234 | sfm.removeReadFD(s.getHandle()); | |
235 | sfm.removeReadFD(s.getHandle()); | |
236 | } | |
237 | #endif |