1 | // |
2 | // PollSet.cpp |
3 | // |
4 | // Library: Net |
5 | // Package: Sockets |
6 | // Module: PollSet |
7 | // |
8 | // Copyright (c) 2016, Applied Informatics Software Engineering GmbH. |
9 | // All rights reserved. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | #if defined(_WIN32) |
16 | #include "Poco/Platform_WIN32_OSVER.h" |
17 | #endif |
18 | #include "Poco/Net/PollSet.h" |
19 | #include "Poco/Net/SocketImpl.h" |
20 | #include "Poco/Mutex.h" |
21 | #include <set> |
22 | |
23 | |
24 | #if defined(_WIN32) && _WIN32_WINNT >= 0x0600 |
25 | #ifndef POCO_HAVE_FD_POLL |
26 | #define POCO_HAVE_FD_POLL 1 |
27 | #endif |
28 | #elif defined(POCO_OS_FAMILY_BSD) |
29 | #ifndef POCO_HAVE_FD_POLL |
30 | #define POCO_HAVE_FD_POLL 1 |
31 | #endif |
32 | #endif |
33 | |
34 | |
35 | #if defined(POCO_HAVE_FD_EPOLL) |
36 | #include <sys/epoll.h> |
37 | #elif defined(POCO_HAVE_FD_POLL) |
38 | #ifndef _WIN32 |
39 | #include <poll.h> |
40 | #endif |
41 | #endif |
42 | |
43 | |
44 | namespace Poco { |
45 | namespace Net { |
46 | |
47 | |
48 | #if defined(POCO_HAVE_FD_EPOLL) |
49 | |
50 | |
51 | // |
52 | // Linux implementation using epoll |
53 | // |
54 | class PollSetImpl |
55 | { |
56 | public: |
57 | PollSetImpl(): |
58 | _epollfd(-1), |
59 | _events(1024) |
60 | { |
61 | _epollfd = epoll_create(1); |
62 | if (_epollfd < 0) |
63 | { |
64 | SocketImpl::error(); |
65 | } |
66 | } |
67 | |
68 | ~PollSetImpl() |
69 | { |
70 | if (_epollfd >= 0) |
71 | ::close(_epollfd); |
72 | } |
73 | |
74 | void add(const Socket& socket, int mode) |
75 | { |
76 | Poco::FastMutex::ScopedLock lock(_mutex); |
77 | |
78 | poco_socket_t fd = socket.impl()->sockfd(); |
79 | struct epoll_event ev; |
80 | ev.events = 0; |
81 | if (mode & PollSet::POLL_READ) |
82 | ev.events |= EPOLLIN; |
83 | if (mode & PollSet::POLL_WRITE) |
84 | ev.events |= EPOLLOUT; |
85 | if (mode & PollSet::POLL_ERROR) |
86 | ev.events |= EPOLLERR; |
87 | ev.data.ptr = socket.impl(); |
88 | int err = epoll_ctl(_epollfd, EPOLL_CTL_ADD, fd, &ev); |
89 | if (err) SocketImpl::error(); |
90 | |
91 | _socketMap[socket.impl()] = socket; |
92 | } |
93 | |
94 | void remove(const Socket& socket) |
95 | { |
96 | Poco::FastMutex::ScopedLock lock(_mutex); |
97 | |
98 | poco_socket_t fd = socket.impl()->sockfd(); |
99 | struct epoll_event ev; |
100 | ev.events = 0; |
101 | ev.data.ptr = 0; |
102 | int err = epoll_ctl(_epollfd, EPOLL_CTL_DEL, fd, &ev); |
103 | if (err) SocketImpl::error(); |
104 | |
105 | _socketMap.erase(socket.impl()); |
106 | } |
107 | |
108 | void update(const Socket& socket, int mode) |
109 | { |
110 | poco_socket_t fd = socket.impl()->sockfd(); |
111 | struct epoll_event ev; |
112 | ev.events = 0; |
113 | if (mode & PollSet::POLL_READ) |
114 | ev.events |= EPOLLIN; |
115 | if (mode & PollSet::POLL_WRITE) |
116 | ev.events |= EPOLLOUT; |
117 | if (mode & PollSet::POLL_ERROR) |
118 | ev.events |= EPOLLERR; |
119 | ev.data.ptr = socket.impl(); |
120 | int err = epoll_ctl(_epollfd, EPOLL_CTL_MOD, fd, &ev); |
121 | if (err) |
122 | { |
123 | SocketImpl::error(); |
124 | } |
125 | } |
126 | |
127 | void clear() |
128 | { |
129 | Poco::FastMutex::ScopedLock lock(_mutex); |
130 | |
131 | ::close(_epollfd); |
132 | _socketMap.clear(); |
133 | _epollfd = epoll_create(1); |
134 | if (_epollfd < 0) |
135 | { |
136 | SocketImpl::error(); |
137 | } |
138 | } |
139 | |
140 | PollSet::SocketModeMap poll(const Poco::Timespan& timeout) |
141 | { |
142 | PollSet::SocketModeMap result; |
143 | |
144 | if (_socketMap.empty()) return result; |
145 | |
146 | Poco::Timespan remainingTime(timeout); |
147 | int rc; |
148 | do |
149 | { |
150 | Poco::Timestamp start; |
151 | rc = epoll_wait(_epollfd, &_events[0], _events.size(), remainingTime.totalMilliseconds()); |
152 | if (rc < 0 && SocketImpl::lastError() == POCO_EINTR) |
153 | { |
154 | Poco::Timestamp end; |
155 | Poco::Timespan waited = end - start; |
156 | if (waited < remainingTime) |
157 | remainingTime -= waited; |
158 | else |
159 | remainingTime = 0; |
160 | } |
161 | } |
162 | while (rc < 0 && SocketImpl::lastError() == POCO_EINTR); |
163 | if (rc < 0) SocketImpl::error(); |
164 | |
165 | Poco::FastMutex::ScopedLock lock(_mutex); |
166 | |
167 | for (int i = 0; i < rc; i++) |
168 | { |
169 | std::map<void*, Socket>::iterator it = _socketMap.find(_events[i].data.ptr); |
170 | if (it != _socketMap.end()) |
171 | { |
172 | if (_events[i].events & EPOLLIN) |
173 | result[it->second] |= PollSet::POLL_READ; |
174 | if (_events[i].events & EPOLLOUT) |
175 | result[it->second] |= PollSet::POLL_WRITE; |
176 | if (_events[i].events & EPOLLERR) |
177 | result[it->second] |= PollSet::POLL_ERROR; |
178 | } |
179 | } |
180 | |
181 | return result; |
182 | } |
183 | |
184 | private: |
185 | Poco::FastMutex _mutex; |
186 | int _epollfd; |
187 | std::map<void*, Socket> _socketMap; |
188 | std::vector<struct epoll_event> _events; |
189 | }; |
190 | |
191 | |
192 | #elif defined(POCO_HAVE_FD_POLL) |
193 | |
194 | |
195 | // |
196 | // BSD implementation using poll |
197 | // |
198 | class PollSetImpl |
199 | { |
200 | public: |
201 | void add(const Socket& socket, int mode) |
202 | { |
203 | Poco::FastMutex::ScopedLock lock(_mutex); |
204 | |
205 | poco_socket_t fd = socket.impl()->sockfd(); |
206 | _addMap[fd] = mode; |
207 | _removeSet.erase(fd); |
208 | _socketMap[fd] = socket; |
209 | } |
210 | |
211 | void remove(const Socket& socket) |
212 | { |
213 | Poco::FastMutex::ScopedLock lock(_mutex); |
214 | |
215 | poco_socket_t fd = socket.impl()->sockfd(); |
216 | _removeSet.insert(fd); |
217 | _addMap.erase(fd); |
218 | _socketMap.erase(fd); |
219 | } |
220 | |
221 | void update(const Socket& socket, int mode) |
222 | { |
223 | Poco::FastMutex::ScopedLock lock(_mutex); |
224 | |
225 | poco_socket_t fd = socket.impl()->sockfd(); |
226 | for (std::vector<pollfd>::iterator it = _pollfds.begin(); it != _pollfds.end(); ++it) |
227 | { |
228 | if (it->fd == fd) |
229 | { |
230 | it->events = 0; |
231 | if (mode & PollSet::POLL_READ) |
232 | it->events |= POLLIN; |
233 | if (mode & PollSet::POLL_WRITE) |
234 | it->events |= POLLOUT; |
235 | } |
236 | } |
237 | } |
238 | |
239 | void clear() |
240 | { |
241 | Poco::FastMutex::ScopedLock lock(_mutex); |
242 | |
243 | _socketMap.clear(); |
244 | _addMap.clear(); |
245 | _removeSet.clear(); |
246 | _pollfds.clear(); |
247 | } |
248 | |
249 | PollSet::SocketModeMap poll(const Poco::Timespan& timeout) |
250 | { |
251 | PollSet::SocketModeMap result; |
252 | { |
253 | Poco::FastMutex::ScopedLock lock(_mutex); |
254 | |
255 | if (!_removeSet.empty()) |
256 | { |
257 | for (std::vector<pollfd>::iterator it = _pollfds.begin(); it != _pollfds.end();) |
258 | { |
259 | if (_removeSet.find(it->fd) != _removeSet.end()) |
260 | { |
261 | it = _pollfds.erase(it); |
262 | } |
263 | else ++it; |
264 | } |
265 | _removeSet.clear(); |
266 | } |
267 | |
268 | _pollfds.reserve(_pollfds.size() + _addMap.size()); |
269 | for (std::map<poco_socket_t, int>::iterator it = _addMap.begin(); it != _addMap.end(); ++it) |
270 | { |
271 | pollfd pfd; |
272 | pfd.fd = it->first; |
273 | pfd.events = 0; |
274 | pfd.revents = 0; |
275 | if (it->second & PollSet::POLL_READ) |
276 | pfd.events |= POLLIN; |
277 | if (it->second & PollSet::POLL_WRITE) |
278 | pfd.events |= POLLOUT; |
279 | |
280 | _pollfds.push_back(pfd); |
281 | } |
282 | _addMap.clear(); |
283 | } |
284 | |
285 | if (_pollfds.empty()) return result; |
286 | |
287 | Poco::Timespan remainingTime(timeout); |
288 | int rc; |
289 | do |
290 | { |
291 | Poco::Timestamp start; |
292 | #ifdef _WIN32 |
293 | rc = WSAPoll(&_pollfds[0], _pollfds.size(), static_cast<INT>(timeout.totalMilliseconds())); |
294 | #else |
295 | rc = ::poll(&_pollfds[0], _pollfds.size(), timeout.totalMilliseconds()); |
296 | #endif |
297 | if (rc < 0 && SocketImpl::lastError() == POCO_EINTR) |
298 | { |
299 | Poco::Timestamp end; |
300 | Poco::Timespan waited = end - start; |
301 | if (waited < remainingTime) |
302 | remainingTime -= waited; |
303 | else |
304 | remainingTime = 0; |
305 | } |
306 | } |
307 | while (rc < 0 && SocketImpl::lastError() == POCO_EINTR); |
308 | if (rc < 0) SocketImpl::error(); |
309 | |
310 | { |
311 | Poco::FastMutex::ScopedLock lock(_mutex); |
312 | |
313 | if (!_socketMap.empty()) |
314 | { |
315 | for (std::vector<pollfd>::iterator it = _pollfds.begin(); it != _pollfds.end(); ++it) |
316 | { |
317 | std::map<poco_socket_t, Socket>::const_iterator its = _socketMap.find(it->fd); |
318 | if (its != _socketMap.end()) |
319 | { |
320 | if (it->revents & POLLIN) |
321 | result[its->second] |= PollSet::POLL_READ; |
322 | if (it->revents & POLLOUT) |
323 | result[its->second] |= PollSet::POLL_WRITE; |
324 | if (it->revents & POLLERR) |
325 | result[its->second] |= PollSet::POLL_ERROR; |
326 | } |
327 | it->revents = 0; |
328 | } |
329 | } |
330 | } |
331 | |
332 | return result; |
333 | } |
334 | |
335 | private: |
336 | Poco::FastMutex _mutex; |
337 | std::map<poco_socket_t, Socket> _socketMap; |
338 | std::map<poco_socket_t, int> _addMap; |
339 | std::set<poco_socket_t> _removeSet; |
340 | std::vector<pollfd> _pollfds; |
341 | }; |
342 | |
343 | |
344 | #else |
345 | |
346 | |
347 | // |
348 | // Fallback implementation using select() |
349 | // |
350 | class PollSetImpl |
351 | { |
352 | public: |
353 | void add(const Socket& socket, int mode) |
354 | { |
355 | Poco::FastMutex::ScopedLock lock(_mutex); |
356 | |
357 | _map[socket] = mode; |
358 | } |
359 | |
360 | void remove(const Socket& socket) |
361 | { |
362 | Poco::FastMutex::ScopedLock lock(_mutex); |
363 | |
364 | _map.erase(socket); |
365 | } |
366 | |
367 | void update(const Socket& socket, int mode) |
368 | { |
369 | Poco::FastMutex::ScopedLock lock(_mutex); |
370 | |
371 | _map[socket] = mode; |
372 | } |
373 | |
374 | void clear() |
375 | { |
376 | Poco::FastMutex::ScopedLock lock(_mutex); |
377 | |
378 | _map.clear(); |
379 | } |
380 | |
381 | PollSet::SocketModeMap poll(const Poco::Timespan& timeout) |
382 | { |
383 | fd_set fdRead; |
384 | fd_set fdWrite; |
385 | fd_set fdExcept; |
386 | int nfd = 0; |
387 | |
388 | FD_ZERO(&fdRead); |
389 | FD_ZERO(&fdWrite); |
390 | FD_ZERO(&fdExcept); |
391 | |
392 | { |
393 | Poco::FastMutex::ScopedLock lock(_mutex); |
394 | |
395 | for (PollSet::SocketModeMap::const_iterator it = _map.begin(); it != _map.end(); ++it) |
396 | { |
397 | poco_socket_t fd = it->first.impl()->sockfd(); |
398 | if (fd != POCO_INVALID_SOCKET && it->second) |
399 | { |
400 | if (int(fd) > nfd) nfd = int(fd); |
401 | |
402 | if (it->second & PollSet::POLL_READ) |
403 | { |
404 | FD_SET(fd, &fdRead); |
405 | } |
406 | if (it->second & PollSet::POLL_WRITE) |
407 | { |
408 | FD_SET(fd, &fdWrite); |
409 | } |
410 | if (it->second & PollSet::POLL_ERROR) |
411 | { |
412 | FD_SET(fd, &fdExcept); |
413 | } |
414 | } |
415 | } |
416 | } |
417 | |
418 | PollSet::SocketModeMap result; |
419 | if (nfd == 0) return result; |
420 | |
421 | Poco::Timespan remainingTime(timeout); |
422 | int rc; |
423 | do |
424 | { |
425 | struct timeval tv; |
426 | tv.tv_sec = (long) remainingTime.totalSeconds(); |
427 | tv.tv_usec = (long) remainingTime.useconds(); |
428 | Poco::Timestamp start; |
429 | rc = ::select(nfd + 1, &fdRead, &fdWrite, &fdExcept, &tv); |
430 | if (rc < 0 && SocketImpl::lastError() == POCO_EINTR) |
431 | { |
432 | Poco::Timestamp end; |
433 | Poco::Timespan waited = end - start; |
434 | if (waited < remainingTime) |
435 | remainingTime -= waited; |
436 | else |
437 | remainingTime = 0; |
438 | } |
439 | } |
440 | while (rc < 0 && SocketImpl::lastError() == POCO_EINTR); |
441 | if (rc < 0) SocketImpl::error(); |
442 | |
443 | { |
444 | Poco::FastMutex::ScopedLock lock(_mutex); |
445 | |
446 | for (PollSet::SocketModeMap::const_iterator it = _map.begin(); it != _map.end(); ++it) |
447 | { |
448 | poco_socket_t fd = it->first.impl()->sockfd(); |
449 | if (fd != POCO_INVALID_SOCKET) |
450 | { |
451 | if (FD_ISSET(fd, &fdRead)) |
452 | { |
453 | result[it->first] |= PollSet::POLL_READ; |
454 | } |
455 | if (FD_ISSET(fd, &fdWrite)) |
456 | { |
457 | result[it->first] |= PollSet::POLL_WRITE; |
458 | } |
459 | if (FD_ISSET(fd, &fdExcept)) |
460 | { |
461 | result[it->first] |= PollSet::POLL_ERROR; |
462 | } |
463 | } |
464 | } |
465 | } |
466 | |
467 | return result; |
468 | } |
469 | |
470 | private: |
471 | Poco::FastMutex _mutex; |
472 | PollSet::SocketModeMap _map; |
473 | }; |
474 | |
475 | |
476 | #endif |
477 | |
478 | |
479 | PollSet::PollSet(): |
480 | _pImpl(new PollSetImpl) |
481 | { |
482 | } |
483 | |
484 | |
485 | PollSet::~PollSet() |
486 | { |
487 | delete _pImpl; |
488 | } |
489 | |
490 | |
491 | void PollSet::add(const Socket& socket, int mode) |
492 | { |
493 | _pImpl->add(socket, mode); |
494 | } |
495 | |
496 | |
497 | void PollSet::remove(const Socket& socket) |
498 | { |
499 | _pImpl->remove(socket); |
500 | } |
501 | |
502 | |
503 | void PollSet::update(const Socket& socket, int mode) |
504 | { |
505 | _pImpl->update(socket, mode); |
506 | } |
507 | |
508 | |
509 | void PollSet::clear() |
510 | { |
511 | _pImpl->clear(); |
512 | } |
513 | |
514 | |
515 | PollSet::SocketModeMap PollSet::poll(const Poco::Timespan& timeout) |
516 | { |
517 | return _pImpl->poll(timeout); |
518 | } |
519 | |
520 | |
521 | } } // namespace Poco::Net |
522 | |