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 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
184private:
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//
198class PollSetImpl
199{
200public:
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
335private:
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//
350class PollSetImpl
351{
352public:
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
470private:
471 Poco::FastMutex _mutex;
472 PollSet::SocketModeMap _map;
473};
474
475
476#endif
477
478
479PollSet::PollSet():
480 _pImpl(new PollSetImpl)
481{
482}
483
484
485PollSet::~PollSet()
486{
487 delete _pImpl;
488}
489
490
491void PollSet::add(const Socket& socket, int mode)
492{
493 _pImpl->add(socket, mode);
494}
495
496
497void PollSet::remove(const Socket& socket)
498{
499 _pImpl->remove(socket);
500}
501
502
503void PollSet::update(const Socket& socket, int mode)
504{
505 _pImpl->update(socket, mode);
506}
507
508
509void PollSet::clear()
510{
511 _pImpl->clear();
512}
513
514
515PollSet::SocketModeMap PollSet::poll(const Poco::Timespan& timeout)
516{
517 return _pImpl->poll(timeout);
518}
519
520
521} } // namespace Poco::Net
522