]>
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: | |
39 | EpollFDMultiplexer(); | |
40 | virtual ~EpollFDMultiplexer() | |
41 | { | |
42 | close(d_epollfd); | |
43 | } | |
44 | ||
5bdbb83d RG |
45 | virtual int run(struct timeval* tv, int timeout=500) override; |
46 | virtual void getAvailableFDs(std::vector<int>& fds, int timeout) override; | |
a1dfcec8 | 47 | |
27ae2e3c | 48 | virtual void addFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const funcparam_t& parameter, const struct timeval* ttd=nullptr) override; |
5bdbb83d RG |
49 | virtual void removeFD(callbackmap_t& cbmap, int fd) override; |
50 | string getName() const override | |
1f4abb20 BH |
51 | { |
52 | return "epoll"; | |
53 | } | |
a1dfcec8 BH |
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 | ||
1f4abb20 | 60 | |
aa136564 | 61 | static FDMultiplexer* makeEpoll() |
a1dfcec8 BH |
62 | { |
63 | return new EpollFDMultiplexer(); | |
64 | } | |
65 | ||
aa136564 | 66 | static struct EpollRegisterOurselves |
1f4abb20 | 67 | { |
aa136564 BH |
68 | EpollRegisterOurselves() { |
69 | FDMultiplexer::getMultiplexerMap().insert(make_pair(0, &makeEpoll)); // priority 0! | |
1f4abb20 | 70 | } |
aa136564 | 71 | } doItEpoll; |
1f4abb20 | 72 | |
a1dfcec8 | 73 | int EpollFDMultiplexer::s_maxevents=1024; |
5bdbb83d | 74 | |
a1dfcec8 BH |
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()); | |
98d0ee4a BH |
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); | |
0a7f24cb | 91 | close(d_epollfd); |
98d0ee4a BH |
92 | throw FDMultiplexerException("epoll multiplexer failed self-test: "+string(fe.what())); |
93 | } | |
94 | ||
a1dfcec8 BH |
95 | } |
96 | ||
27ae2e3c | 97 | void EpollFDMultiplexer::addFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const funcparam_t& parameter, const struct timeval* ttd) |
a1dfcec8 | 98 | { |
27ae2e3c | 99 | accountingAddFD(cbmap, fd, toDo, parameter, ttd); |
a1dfcec8 | 100 | |
a1dfcec8 BH |
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 | ||
c454d11b BH |
108 | if(epoll_ctl(d_epollfd, EPOLL_CTL_ADD, fd, &eevent) < 0) { |
109 | cbmap.erase(fd); | |
a1dfcec8 | 110 | throw FDMultiplexerException("Adding fd to epoll set: "+stringerror()); |
c454d11b | 111 | } |
a1dfcec8 BH |
112 | } |
113 | ||
114 | void EpollFDMultiplexer::removeFD(callbackmap_t& cbmap, int fd) | |
115 | { | |
a1dfcec8 | 116 | if(!cbmap.erase(fd)) |
335da0ba | 117 | throw FDMultiplexerException("Tried to remove unlisted fd "+std::to_string(fd)+ " from multiplexer"); |
a1dfcec8 | 118 | |
3368984f BH |
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) | |
a1dfcec8 BH |
124 | throw FDMultiplexerException("Removing fd from epoll set: "+stringerror()); |
125 | } | |
126 | ||
5bdbb83d RG |
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 | ||
0e663c3b | 139 | int EpollFDMultiplexer::run(struct timeval* now, int timeout) |
a1dfcec8 BH |
140 | { |
141 | if(d_inrun) { | |
142 | throw FDMultiplexerException("FDMultiplexer::run() is not reentrant!\n"); | |
143 | } | |
144 | ||
0e663c3b | 145 | int ret=epoll_wait(d_epollfd, d_eevents.get(), s_maxevents, timeout); |
55df795b | 146 | gettimeofday(now,0); // MANDATORY |
5bdbb83d | 147 | |
a1dfcec8 | 148 | if(ret < 0 && errno!=EINTR) |
abbd6830 | 149 | throw FDMultiplexerException("epoll returned error: "+stringerror()); |
a1dfcec8 | 150 | |
f3c5773b | 151 | if(ret < 1) // thanks AB! |
a1dfcec8 BH |
152 | return 0; |
153 | ||
154 | d_inrun=true; | |
a1dfcec8 BH |
155 | for(int n=0; n < ret; ++n) { |
156 | d_iter=d_readCallbacks.find(d_eevents[n].data.fd); | |
157 | ||
0bff046b | 158 | if(d_iter != d_readCallbacks.end()) { |
ac3da0c2 | 159 | d_iter->d_callback(d_iter->d_fd, d_iter->d_parameter); |
abbd6830 | 160 | continue; // so we don't refind ourselves as writable! |
0bff046b | 161 | } |
a1dfcec8 BH |
162 | d_iter=d_writeCallbacks.find(d_eevents[n].data.fd); |
163 | ||
0bff046b | 164 | if(d_iter != d_writeCallbacks.end()) { |
ac3da0c2 | 165 | d_iter->d_callback(d_iter->d_fd, d_iter->d_parameter); |
0bff046b | 166 | } |
a1dfcec8 | 167 | } |
a1dfcec8 | 168 | d_inrun=false; |
0e663c3b | 169 | return ret; |
a1dfcec8 BH |
170 | } |
171 | ||
172 | #if 0 | |
d8f6d49f | 173 | void acceptData(int fd, funcparam_t& parameter) |
a1dfcec8 BH |
174 | { |
175 | cout<<"Have data on fd "<<fd<<endl; | |
d8f6d49f | 176 | Socket* sock=funcparam_t_cast<Socket*>(parameter); |
a1dfcec8 BH |
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 | { | |
a5794017 | 186 | Socket s(AF_INET, SOCK_DGRAM); |
a1dfcec8 BH |
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 |