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
44namespace Poco {
45namespace Net {
46
47
48#if defined(POCO_HAVE_FD_EPOLL)
49
50
51//
52// Linux implementation using epoll
53//
54class PollSetImpl
55{
56public:
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 SocketImpl* sockImpl = socket.impl();
79 poco_socket_t fd = sockImpl->sockfd();
80 struct epoll_event ev;
81 ev.events = 0;
82 if (mode & PollSet::POLL_READ)
83 ev.events |= EPOLLIN;
84 if (mode & PollSet::POLL_WRITE)
85 ev.events |= EPOLLOUT;
86 if (mode & PollSet::POLL_ERROR)
87 ev.events |= EPOLLERR;
88 ev.data.ptr = socket.impl();
89 int err = epoll_ctl(_epollfd, EPOLL_CTL_ADD, fd, &ev);
90
91 if (err)
92 {
93 if (errno == EEXIST) update(socket, mode);
94 else SocketImpl::error();
95 }
96
97 if (_socketMap.find(sockImpl) == _socketMap.end())
98 _socketMap[sockImpl] = socket;
99 }
100
101 void remove(const Socket& socket)
102 {
103 Poco::FastMutex::ScopedLock lock(_mutex);
104
105 poco_socket_t fd = socket.impl()->sockfd();
106 struct epoll_event ev;
107 ev.events = 0;
108 ev.data.ptr = 0;
109 int err = epoll_ctl(_epollfd, EPOLL_CTL_DEL, fd, &ev);
110 if (err) SocketImpl::error();
111
112 _socketMap.erase(socket.impl());
113 }
114
115 bool has(const Socket& socket) const
116 {
117 Poco::FastMutex::ScopedLock lock(_mutex);
118 SocketImpl* sockImpl = socket.impl();
119 return sockImpl &&
120 (_socketMap.find(sockImpl) != _socketMap.end());
121 }
122
123 bool empty() const
124 {
125 Poco::FastMutex::ScopedLock lock(_mutex);
126 return _socketMap.empty();
127 }
128
129 void update(const Socket& socket, int mode)
130 {
131 poco_socket_t fd = socket.impl()->sockfd();
132 struct epoll_event ev;
133 ev.events = 0;
134 if (mode & PollSet::POLL_READ)
135 ev.events |= EPOLLIN;
136 if (mode & PollSet::POLL_WRITE)
137 ev.events |= EPOLLOUT;
138 if (mode & PollSet::POLL_ERROR)
139 ev.events |= EPOLLERR;
140 ev.data.ptr = socket.impl();
141 int err = epoll_ctl(_epollfd, EPOLL_CTL_MOD, fd, &ev);
142 if (err)
143 {
144 SocketImpl::error();
145 }
146 }
147
148 void clear()
149 {
150 Poco::FastMutex::ScopedLock lock(_mutex);
151
152 ::close(_epollfd);
153 _socketMap.clear();
154 _epollfd = epoll_create(1);
155 if (_epollfd < 0)
156 {
157 SocketImpl::error();
158 }
159 }
160
161 PollSet::SocketModeMap poll(const Poco::Timespan& timeout)
162 {
163 PollSet::SocketModeMap result;
164
165 {
166 Poco::FastMutex::ScopedLock lock(_mutex);
167 if(_socketMap.empty()) return result;
168 }
169
170 Poco::Timespan remainingTime(timeout);
171 int rc;
172 do
173 {
174 Poco::Timestamp start;
175 rc = epoll_wait(_epollfd, &_events[0], _events.size(), remainingTime.totalMilliseconds());
176 if (rc < 0 && SocketImpl::lastError() == POCO_EINTR)
177 {
178 Poco::Timestamp end;
179 Poco::Timespan waited = end - start;
180 if (waited < remainingTime)
181 remainingTime -= waited;
182 else
183 remainingTime = 0;
184 }
185 }
186 while (rc < 0 && SocketImpl::lastError() == POCO_EINTR);
187 if (rc < 0) SocketImpl::error();
188
189 Poco::FastMutex::ScopedLock lock(_mutex);
190
191 for (int i = 0; i < rc; i++)
192 {
193 std::map<void*, Socket>::iterator it = _socketMap.find(_events[i].data.ptr);
194 if (it != _socketMap.end())
195 {
196 if (_events[i].events & EPOLLIN)
197 result[it->second] |= PollSet::POLL_READ;
198 if (_events[i].events & EPOLLOUT)
199 result[it->second] |= PollSet::POLL_WRITE;
200 if (_events[i].events & EPOLLERR)
201 result[it->second] |= PollSet::POLL_ERROR;
202 }
203 }
204
205 return result;
206 }
207
208private:
209 mutable Poco::FastMutex _mutex;
210 int _epollfd;
211 std::map<void*, Socket> _socketMap;
212 std::vector<struct epoll_event> _events;
213};
214
215
216#elif defined(POCO_HAVE_FD_POLL)
217
218
219//
220// BSD/Windows implementation using poll/WSAPoll
221//
222class PollSetImpl
223{
224public:
225 void add(const Socket& socket, int mode)
226 {
227 Poco::FastMutex::ScopedLock lock(_mutex);
228
229 poco_socket_t fd = socket.impl()->sockfd();
230 _addMap[fd] = mode;
231 _removeSet.erase(fd);
232 _socketMap[fd] = socket;
233 }
234
235 void remove(const Socket& socket)
236 {
237 Poco::FastMutex::ScopedLock lock(_mutex);
238
239 poco_socket_t fd = socket.impl()->sockfd();
240 _removeSet.insert(fd);
241 _addMap.erase(fd);
242 _socketMap.erase(fd);
243 }
244
245 bool has(const Socket& socket) const
246 {
247 Poco::FastMutex::ScopedLock lock(_mutex);
248 SocketImpl* sockImpl = socket.impl();
249 return sockImpl &&
250 (_socketMap.find(sockImpl->sockfd()) != _socketMap.end());
251 }
252
253 bool empty() const
254 {
255 Poco::FastMutex::ScopedLock lock(_mutex);
256 return _socketMap.empty();
257 }
258
259 void update(const Socket& socket, int mode)
260 {
261 Poco::FastMutex::ScopedLock lock(_mutex);
262
263 poco_socket_t fd = socket.impl()->sockfd();
264 for (std::vector<pollfd>::iterator it = _pollfds.begin(); it != _pollfds.end(); ++it)
265 {
266 if (it->fd == fd)
267 {
268 it->events = 0;
269 if (mode & PollSet::POLL_READ)
270 it->events |= POLLIN;
271 if (mode & PollSet::POLL_WRITE)
272 it->events |= POLLOUT;
273 }
274 }
275 }
276
277 void clear()
278 {
279 Poco::FastMutex::ScopedLock lock(_mutex);
280
281 _socketMap.clear();
282 _addMap.clear();
283 _removeSet.clear();
284 _pollfds.clear();
285 }
286
287 PollSet::SocketModeMap poll(const Poco::Timespan& timeout)
288 {
289 PollSet::SocketModeMap result;
290 {
291 Poco::FastMutex::ScopedLock lock(_mutex);
292
293 if (!_removeSet.empty())
294 {
295 for (std::vector<pollfd>::iterator it = _pollfds.begin(); it != _pollfds.end();)
296 {
297 if (_removeSet.find(it->fd) != _removeSet.end())
298 {
299 it = _pollfds.erase(it);
300 }
301 else ++it;
302 }
303 _removeSet.clear();
304 }
305
306 _pollfds.reserve(_pollfds.size() + _addMap.size());
307 for (std::map<poco_socket_t, int>::iterator it = _addMap.begin(); it != _addMap.end(); ++it)
308 {
309 pollfd pfd;
310 pfd.fd = it->first;
311 pfd.events = 0;
312 pfd.revents = 0;
313 if (it->second & PollSet::POLL_READ)
314 pfd.events |= POLLIN;
315 if (it->second & PollSet::POLL_WRITE)
316 pfd.events |= POLLOUT;
317
318 _pollfds.push_back(pfd);
319 }
320 _addMap.clear();
321 }
322
323 if (_pollfds.empty()) return result;
324
325 Poco::Timespan remainingTime(timeout);
326 int rc;
327 do
328 {
329 Poco::Timestamp start;
330#ifdef _WIN32
331 rc = WSAPoll(&_pollfds[0], static_cast<ULONG>(_pollfds.size()), static_cast<INT>(timeout.totalMilliseconds()));
332#else
333 rc = ::poll(&_pollfds[0], _pollfds.size(), timeout.totalMilliseconds());
334#endif
335 if (rc < 0 && SocketImpl::lastError() == POCO_EINTR)
336 {
337 Poco::Timestamp end;
338 Poco::Timespan waited = end - start;
339 if (waited < remainingTime)
340 remainingTime -= waited;
341 else
342 remainingTime = 0;
343 }
344 }
345 while (rc < 0 && SocketImpl::lastError() == POCO_EINTR);
346 if (rc < 0) SocketImpl::error();
347
348 {
349 Poco::FastMutex::ScopedLock lock(_mutex);
350
351 if (!_socketMap.empty())
352 {
353 for (std::vector<pollfd>::iterator it = _pollfds.begin(); it != _pollfds.end(); ++it)
354 {
355 std::map<poco_socket_t, Socket>::const_iterator its = _socketMap.find(it->fd);
356 if (its != _socketMap.end())
357 {
358 if (it->revents & POLLIN)
359 result[its->second] |= PollSet::POLL_READ;
360 if (it->revents & POLLOUT)
361 result[its->second] |= PollSet::POLL_WRITE;
362 if (it->revents & POLLERR)
363 result[its->second] |= PollSet::POLL_ERROR;
364#ifdef _WIN32
365 if (it->revents & POLLHUP)
366 result[its->second] |= PollSet::POLL_READ;
367#endif
368 }
369 it->revents = 0;
370 }
371 }
372 }
373
374 return result;
375 }
376
377private:
378 mutable Poco::FastMutex _mutex;
379 std::map<poco_socket_t, Socket> _socketMap;
380 std::map<poco_socket_t, int> _addMap;
381 std::set<poco_socket_t> _removeSet;
382 std::vector<pollfd> _pollfds;
383};
384
385
386#else
387
388
389//
390// Fallback implementation using select()
391//
392class PollSetImpl
393{
394public:
395 void add(const Socket& socket, int mode)
396 {
397 Poco::FastMutex::ScopedLock lock(_mutex);
398 _map[socket] = mode;
399 }
400
401 void remove(const Socket& socket)
402 {
403 Poco::FastMutex::ScopedLock lock(_mutex);
404 _map.erase(socket);
405 }
406
407 bool has(const Socket& socket) const
408 {
409 Poco::FastMutex::ScopedLock lock(_mutex);
410 return _map.find(socket) != _map.end();
411 }
412
413 bool empty() const
414 {
415 Poco::FastMutex::ScopedLock lock(_mutex);
416 return _map.empty();
417 }
418
419 void update(const Socket& socket, int mode)
420 {
421 Poco::FastMutex::ScopedLock lock(_mutex);
422 _map[socket] = mode;
423 }
424
425 void clear()
426 {
427 Poco::FastMutex::ScopedLock lock(_mutex);
428 _map.clear();
429 }
430
431 PollSet::SocketModeMap poll(const Poco::Timespan& timeout)
432 {
433 fd_set fdRead;
434 fd_set fdWrite;
435 fd_set fdExcept;
436 int nfd = 0;
437
438 FD_ZERO(&fdRead);
439 FD_ZERO(&fdWrite);
440 FD_ZERO(&fdExcept);
441
442 {
443 Poco::FastMutex::ScopedLock lock(_mutex);
444
445 for (PollSet::SocketModeMap::const_iterator it = _map.begin(); it != _map.end(); ++it)
446 {
447 poco_socket_t fd = it->first.impl()->sockfd();
448 if (fd != POCO_INVALID_SOCKET && it->second)
449 {
450 if (int(fd) > nfd) nfd = int(fd);
451
452 if (it->second & PollSet::POLL_READ)
453 {
454 FD_SET(fd, &fdRead);
455 }
456 if (it->second & PollSet::POLL_WRITE)
457 {
458 FD_SET(fd, &fdWrite);
459 }
460 if (it->second & PollSet::POLL_ERROR)
461 {
462 FD_SET(fd, &fdExcept);
463 }
464 }
465 }
466 }
467
468 PollSet::SocketModeMap result;
469 if (nfd == 0) return result;
470
471 Poco::Timespan remainingTime(timeout);
472 int rc;
473 do
474 {
475 struct timeval tv;
476 tv.tv_sec = (long) remainingTime.totalSeconds();
477 tv.tv_usec = (long) remainingTime.useconds();
478 Poco::Timestamp start;
479 rc = ::select(nfd + 1, &fdRead, &fdWrite, &fdExcept, &tv);
480 if (rc < 0 && SocketImpl::lastError() == POCO_EINTR)
481 {
482 Poco::Timestamp end;
483 Poco::Timespan waited = end - start;
484 if (waited < remainingTime)
485 remainingTime -= waited;
486 else
487 remainingTime = 0;
488 }
489 }
490 while (rc < 0 && SocketImpl::lastError() == POCO_EINTR);
491 if (rc < 0) SocketImpl::error();
492
493 {
494 Poco::FastMutex::ScopedLock lock(_mutex);
495
496 for (PollSet::SocketModeMap::const_iterator it = _map.begin(); it != _map.end(); ++it)
497 {
498 poco_socket_t fd = it->first.impl()->sockfd();
499 if (fd != POCO_INVALID_SOCKET)
500 {
501 if (FD_ISSET(fd, &fdRead))
502 {
503 result[it->first] |= PollSet::POLL_READ;
504 }
505 if (FD_ISSET(fd, &fdWrite))
506 {
507 result[it->first] |= PollSet::POLL_WRITE;
508 }
509 if (FD_ISSET(fd, &fdExcept))
510 {
511 result[it->first] |= PollSet::POLL_ERROR;
512 }
513 }
514 }
515 }
516
517 return result;
518 }
519
520private:
521 mutable Poco::FastMutex _mutex;
522 PollSet::SocketModeMap _map;
523};
524
525
526#endif
527
528
529PollSet::PollSet():
530 _pImpl(new PollSetImpl)
531{
532}
533
534
535PollSet::~PollSet()
536{
537 delete _pImpl;
538}
539
540
541void PollSet::add(const Socket& socket, int mode)
542{
543 _pImpl->add(socket, mode);
544}
545
546
547void PollSet::remove(const Socket& socket)
548{
549 _pImpl->remove(socket);
550}
551
552
553void PollSet::update(const Socket& socket, int mode)
554{
555 _pImpl->update(socket, mode);
556}
557
558
559bool PollSet::has(const Socket& socket) const
560{
561 return _pImpl->has(socket);
562}
563
564
565bool PollSet::empty() const
566{
567 return _pImpl->empty();
568}
569
570
571void PollSet::clear()
572{
573 _pImpl->clear();
574}
575
576
577PollSet::SocketModeMap PollSet::poll(const Poco::Timespan& timeout)
578{
579 return _pImpl->poll(timeout);
580}
581
582
583} } // namespace Poco::Net
584