]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/epollmplexer.cc
Merge pull request #7870 from omoerbeek/stubquery-fix-arg
[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();
40 virtual ~EpollFDMultiplexer()
41 {
42 close(d_epollfd);
43 }
44
45 virtual int run(struct timeval* tv, int timeout=500) override;
46 virtual void getAvailableFDs(std::vector<int>& fds, int timeout) override;
47
48 virtual void addFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const funcparam_t& parameter, const struct timeval* ttd=nullptr) override;
49 virtual void removeFD(callbackmap_t& cbmap, int fd) override;
50 string getName() const override
51 {
52 return "epoll";
53 }
54 private:
55 int d_epollfd;
56 boost::shared_array<epoll_event> d_eevents;
57 static int s_maxevents; // not a hard maximum
58 };
59
60
61 static FDMultiplexer* makeEpoll()
62 {
63 return new EpollFDMultiplexer();
64 }
65
66 static struct EpollRegisterOurselves
67 {
68 EpollRegisterOurselves() {
69 FDMultiplexer::getMultiplexerMap().insert(make_pair(0, &makeEpoll)); // priority 0!
70 }
71 } doItEpoll;
72
73 int EpollFDMultiplexer::s_maxevents=1024;
74
75 EpollFDMultiplexer::EpollFDMultiplexer() : d_eevents(new epoll_event[s_maxevents])
76 {
77 d_epollfd=epoll_create(s_maxevents); // not hard max
78 if(d_epollfd < 0)
79 throw FDMultiplexerException("Setting up epoll: "+stringerror());
80 int fd=socket(AF_INET, SOCK_DGRAM, 0); // for self-test
81 if(fd < 0)
82 return;
83 try {
84 addReadFD(fd, 0);
85 removeReadFD(fd);
86 close(fd);
87 return;
88 }
89 catch(FDMultiplexerException &fe) {
90 close(fd);
91 close(d_epollfd);
92 throw FDMultiplexerException("epoll multiplexer failed self-test: "+string(fe.what()));
93 }
94
95 }
96
97 void EpollFDMultiplexer::addFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const funcparam_t& parameter, const struct timeval* ttd)
98 {
99 accountingAddFD(cbmap, fd, toDo, parameter, ttd);
100
101 struct epoll_event eevent;
102
103 eevent.events = (&cbmap == &d_readCallbacks) ? EPOLLIN : EPOLLOUT;
104
105 eevent.data.u64=0; // placate valgrind (I love it so much)
106 eevent.data.fd=fd;
107
108 if(epoll_ctl(d_epollfd, EPOLL_CTL_ADD, fd, &eevent) < 0) {
109 cbmap.erase(fd);
110 throw FDMultiplexerException("Adding fd to epoll set: "+stringerror());
111 }
112 }
113
114 void EpollFDMultiplexer::removeFD(callbackmap_t& cbmap, int fd)
115 {
116 if(!cbmap.erase(fd))
117 throw FDMultiplexerException("Tried to remove unlisted fd "+std::to_string(fd)+ " from multiplexer");
118
119 struct epoll_event dummy;
120 dummy.events = 0;
121 dummy.data.u64 = 0;
122
123 if(epoll_ctl(d_epollfd, EPOLL_CTL_DEL, fd, &dummy) < 0)
124 throw FDMultiplexerException("Removing fd from epoll set: "+stringerror());
125 }
126
127 void EpollFDMultiplexer::getAvailableFDs(std::vector<int>& fds, int timeout)
128 {
129 int ret=epoll_wait(d_epollfd, d_eevents.get(), s_maxevents, timeout);
130
131 if(ret < 0 && errno!=EINTR)
132 throw FDMultiplexerException("epoll returned error: "+stringerror());
133
134 for(int n=0; n < ret; ++n) {
135 fds.push_back(d_eevents[n].data.fd);
136 }
137 }
138
139 int EpollFDMultiplexer::run(struct timeval* now, int timeout)
140 {
141 if(d_inrun) {
142 throw FDMultiplexerException("FDMultiplexer::run() is not reentrant!\n");
143 }
144
145 int ret=epoll_wait(d_epollfd, d_eevents.get(), s_maxevents, timeout);
146 gettimeofday(now,0); // MANDATORY
147
148 if(ret < 0 && errno!=EINTR)
149 throw FDMultiplexerException("epoll returned error: "+stringerror());
150
151 if(ret < 1) // thanks AB!
152 return 0;
153
154 d_inrun=true;
155 for(int n=0; n < ret; ++n) {
156 d_iter=d_readCallbacks.find(d_eevents[n].data.fd);
157
158 if(d_iter != d_readCallbacks.end()) {
159 d_iter->d_callback(d_iter->d_fd, d_iter->d_parameter);
160 continue; // so we don't refind ourselves as writable!
161 }
162 d_iter=d_writeCallbacks.find(d_eevents[n].data.fd);
163
164 if(d_iter != d_writeCallbacks.end()) {
165 d_iter->d_callback(d_iter->d_fd, d_iter->d_parameter);
166 }
167 }
168 d_inrun=false;
169 return ret;
170 }
171
172 #if 0
173 void acceptData(int fd, funcparam_t& parameter)
174 {
175 cout<<"Have data on fd "<<fd<<endl;
176 Socket* sock=funcparam_t_cast<Socket*>(parameter);
177 string packet;
178 IPEndpoint rem;
179 sock->recvFrom(packet, rem);
180 cout<<"Received "<<packet.size()<<" bytes!\n";
181 }
182
183
184 int main()
185 {
186 Socket s(AF_INET, SOCK_DGRAM);
187
188 IPEndpoint loc("0.0.0.0", 2000);
189 s.bind(loc);
190
191 EpollFDMultiplexer sfm;
192
193 sfm.addReadFD(s.getHandle(), &acceptData, &s);
194
195 for(int n=0; n < 100 ; ++n) {
196 sfm.run();
197 }
198 sfm.removeReadFD(s.getHandle());
199 sfm.removeReadFD(s.getHandle());
200 }
201 #endif
202
203