]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/epollmplexer.cc
Meson: Separate test files from common files
[thirdparty/pdns.git] / pdns / epollmplexer.cc
CommitLineData
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
36class EpollFDMultiplexer : public FDMultiplexer
37{
38public:
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
59private:
60 int d_epollfd;
d651ad51 61 std::vector<epoll_event> d_eevents;
a1dfcec8
BH
62};
63
7c58a81f 64static FDMultiplexer* makeEpoll(unsigned int maxEventsHint)
a1dfcec8 65{
7c58a81f 66 return new EpollFDMultiplexer(maxEventsHint);
a1dfcec8
BH
67}
68
aa136564 69static 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
77EpollFDMultiplexer::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 103static 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
117void 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 131void 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 142void 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
154void 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 167int 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 209void 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
220int 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