]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/epollmplexer.cc
Merge pull request #14032 from rgacogne/ddist-192-changelog-secpoll
[thirdparty/pdns.git] / pdns / epollmplexer.cc
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 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "mplexer.hh"
26 #include "sstuff.hh"
27 #include <iostream>
28 #include <unistd.h>
29 #include "misc.hh"
30 #ifdef __linux__
31 #include <sys/epoll.h>
32 #endif
33
34 #include "namespaces.hh"
35
36 class EpollFDMultiplexer : public FDMultiplexer
37 {
38 public:
39 EpollFDMultiplexer(unsigned int maxEventsHint);
40 ~EpollFDMultiplexer() override
41 {
42 if (d_epollfd >= 0) {
43 close(d_epollfd);
44 }
45 }
46
47 int run(struct timeval* tv, int timeout = 500) override;
48 void getAvailableFDs(std::vector<int>& fds, int timeout) override;
49
50 void addFD(int fd, FDMultiplexer::EventKind kind) override;
51 void removeFD(int fd, FDMultiplexer::EventKind kind) override;
52 void alterFD(int fd, FDMultiplexer::EventKind from, FDMultiplexer::EventKind to) override;
53
54 string getName() const override
55 {
56 return "epoll";
57 }
58
59 private:
60 int d_epollfd;
61 std::vector<epoll_event> d_eevents;
62 };
63
64 static FDMultiplexer* makeEpoll(unsigned int maxEventsHint)
65 {
66 return new EpollFDMultiplexer(maxEventsHint);
67 }
68
69 static struct EpollRegisterOurselves
70 {
71 EpollRegisterOurselves()
72 {
73 FDMultiplexer::getMultiplexerMap().emplace(0, &makeEpoll); // priority 0!
74 }
75 } doItEpoll;
76
77 EpollFDMultiplexer::EpollFDMultiplexer(unsigned int maxEventsHint) :
78 d_eevents(maxEventsHint)
79 {
80 d_epollfd = epoll_create(static_cast<int>(maxEventsHint)); // not hard max, just a hint that is actually ignored since Linux 2.6.8
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) {
87 return;
88 }
89
90 try {
91 addReadFD(fd, 0);
92 removeReadFD(fd);
93 close(fd);
94 return;
95 }
96 catch (const FDMultiplexerException& fe) {
97 close(fd);
98 close(d_epollfd);
99 throw FDMultiplexerException("epoll multiplexer failed self-test: " + string(fe.what()));
100 }
101 }
102
103 static uint32_t convertEventKind(FDMultiplexer::EventKind kind)
104 {
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 }
113
114 throw std::runtime_error("Unhandled event kind in the epoll multiplexer");
115 }
116
117 void EpollFDMultiplexer::addFD(int fd, FDMultiplexer::EventKind kind)
118 {
119 struct epoll_event eevent;
120
121 eevent.events = convertEventKind(kind);
122
123 eevent.data.u64 = 0; // placate valgrind (I love it so much)
124 eevent.data.fd = fd;
125
126 if (epoll_ctl(d_epollfd, EPOLL_CTL_ADD, fd, &eevent) < 0) {
127 throw FDMultiplexerException("Adding fd to epoll set: " + stringerror());
128 }
129 }
130
131 void EpollFDMultiplexer::removeFD(int fd, FDMultiplexer::EventKind)
132 {
133 struct epoll_event dummy;
134 dummy.events = 0;
135 dummy.data.u64 = 0;
136
137 if (epoll_ctl(d_epollfd, EPOLL_CTL_DEL, fd, &dummy) < 0) {
138 throw FDMultiplexerException("Removing fd from epoll set: " + stringerror());
139 }
140 }
141
142 void EpollFDMultiplexer::alterFD(int fd, FDMultiplexer::EventKind, FDMultiplexer::EventKind to)
143 {
144 struct epoll_event eevent;
145 eevent.events = convertEventKind(to);
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) {
150 throw FDMultiplexerException("Altering fd in epoll set: " + stringerror());
151 }
152 }
153
154 void EpollFDMultiplexer::getAvailableFDs(std::vector<int>& fds, int timeout)
155 {
156 int ret = epoll_wait(d_epollfd, d_eevents.data(), d_eevents.size(), timeout);
157
158 if (ret < 0 && errno != EINTR) {
159 throw FDMultiplexerException("epoll returned error: " + stringerror());
160 }
161
162 for (int n = 0; n < ret; ++n) {
163 fds.push_back(d_eevents[n].data.fd);
164 }
165 }
166
167 int EpollFDMultiplexer::run(struct timeval* now, int timeout)
168 {
169 if (d_inrun) {
170 throw FDMultiplexerException("FDMultiplexer::run() is not reentrant!\n");
171 }
172
173 int ret = epoll_wait(d_epollfd, d_eevents.data(), d_eevents.size(), timeout);
174 gettimeofday(now, nullptr); // MANDATORY
175
176 if (ret < 0 && errno != EINTR) {
177 throw FDMultiplexerException("epoll returned error: " + stringerror());
178 }
179
180 if (ret < 1) { // thanks AB!
181 return 0;
182 }
183
184 d_inrun = true;
185 int count = 0;
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);
191 count++;
192 }
193 }
194
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);
199 count++;
200 }
201 }
202 }
203
204 d_inrun = false;
205 return count;
206 }
207
208 #if 0
209 void acceptData(int fd, funcparam_t& parameter)
210 {
211 cout<<"Have data on fd "<<fd<<endl;
212 Socket* sock=funcparam_t_cast<Socket*>(parameter);
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 {
222 Socket s(AF_INET, SOCK_DGRAM);
223
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