Stun Server  Compliant with the latest RFCs including 5389, 5769, and 5780
discover the local host's own external IP address
polling.cpp
Go to the documentation of this file.
1 /*
2  Copyright 2011 John Selbie
3 
4  Licensed under the Apache License, Version 2.0 (the "License");
5  you may not use this file except in compliance with the License.
6  You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10  Unless required by applicable law or agreed to in writing, software
11  distributed under the License is distributed on an "AS IS" BASIS,
12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  See the License for the specific language governing permissions and
14  limitations under the License.
15 */
16 
17 #include "commonincludes.hpp"
18 #include "polling.h"
19 #include "fasthash.h"
20 
21 
22 // --------------------------------------------------------------------------
23 
24 #ifdef HAS_EPOLL
25 
26 class CEpoll :
27  public CBasicRefCount,
28  public CObjectFactory<CEpoll>,
29  public IPolling
30 {
31 private:
32  int _epollfd;
33 
34  epoll_event* _events;
35  size_t _sizeEvents; // total allocated size for events
36  size_t _pendingCount; // number of valid events in _events
37  size_t _currentEventIndex; // which one to process next
38 
39 
40 
41  uint32_t ToNativeFlags(uint32_t eventflags);
42  uint32_t FromNativeFlags(uint32_t eventflags);
43 
44 public:
45  virtual HRESULT Initialize(size_t maxSockets);
46  virtual HRESULT Close();
47  virtual HRESULT Add(int fd, uint32_t eventflags);
48  virtual HRESULT Remove(int fd);
49  virtual HRESULT ChangeEventSet(int fd, uint32_t eventflags);
50  virtual HRESULT WaitForNextEvent(PollEvent* pPollEvent, int timeoutMilliseconds);
51 
52  CEpoll();
53  ~CEpoll();
54 
56 };
57 
58 
59 
60 CEpoll::CEpoll() :
61 _epollfd(-1),
62 _events(NULL),
63 _sizeEvents(0),
64 _pendingCount(0),
65 _currentEventIndex(0)
66 {
67 
68 }
69 
70 CEpoll::~CEpoll()
71 {
72  Close();
73 }
74 
75 uint32_t CEpoll::ToNativeFlags(uint32_t eventflags)
76 {
77  uint32_t result = 0;
78 
79  if (eventflags & IPOLLING_READ) result |= EPOLLIN;
80  if (eventflags & IPOLLING_WRITE) result |= EPOLLOUT;
81  if (eventflags & IPOLLING_EDGETRIGGER) result |= EPOLLET;
82 #ifdef EPOLLRDHUP
83  if (eventflags & IPOLLING_RDHUP) result |= EPOLLRDHUP;
84 #endif
85  if (eventflags & IPOLLING_HUP) result |= EPOLLHUP;
86  if (eventflags & IPOLLING_PRI) result |= EPOLLPRI;
87  if (eventflags & IPOLLING_ERROR) result |= EPOLLERR;
88 
89  return result;
90 }
91 
92 
93 uint32_t CEpoll::FromNativeFlags(uint32_t eventflags)
94 {
95  uint32_t result = 0;
96 
97  if (eventflags & EPOLLIN) result |= IPOLLING_READ;
98  if (eventflags & EPOLLOUT) result |= IPOLLING_WRITE;
99  if (eventflags & EPOLLET) result |= IPOLLING_EDGETRIGGER;
100 #ifdef EPOLLRDHUP
101  if (eventflags & EPOLLRDHUP) result |= IPOLLING_RDHUP;
102 #endif
103  if (eventflags & EPOLLHUP) result |= IPOLLING_HUP;
104  if (eventflags & EPOLLPRI) result |= IPOLLING_PRI;
105  if (eventflags & EPOLLERR) result |= IPOLLING_ERROR;
106 
107  return result;
108 }
109 
110 
111 HRESULT CEpoll::Initialize(size_t maxSockets)
112 {
113  HRESULT hr = S_OK;
114 
115  ASSERT(_epollfd == -1);
116 
117  Close();
118 
119  _epollfd = epoll_create(maxSockets); // maxSockets is likely ignored by epoll_create
120  ChkIf(_epollfd == -1, ERRNOHR);
121 
122 
123  _sizeEvents = maxSockets;
124  _events = new epoll_event[maxSockets];
125  ChkIf(_events == NULL, E_OUTOFMEMORY);
126 
127 
128  _pendingCount = 0;
129  _currentEventIndex = 0;
130 Cleanup:
131  if (FAILED(hr))
132  {
133  Close();
134  }
135  return S_OK;
136 }
137 
138 HRESULT CEpoll::Close()
139 {
140  if (_epollfd != -1)
141  {
142  close(_epollfd);
143  _epollfd = -1;
144  }
145 
146  delete [] _events;
147  _events = NULL;
148  _sizeEvents = 0;
149  _pendingCount = 0;
150  _currentEventIndex = 0;
151 
152  return S_OK;
153 }
154 
155 
156 HRESULT CEpoll::Add(int fd, uint32_t eventflags)
157 {
158  epoll_event ev = {};
159  HRESULT hr = S_OK;
160 
161  ChkIfA(fd == -1, E_INVALIDARG);
162  ChkIfA(_epollfd==-1, E_UNEXPECTED);
163 
164 
165  ev.data.fd = fd;
166  ev.events = ToNativeFlags(eventflags);
167 
168  ChkIfA(epoll_ctl(_epollfd, EPOLL_CTL_ADD, fd, &ev) == -1, ERRNOHR);
169 Cleanup:
170  return hr;
171 }
172 
173 HRESULT CEpoll::Remove(int fd)
174 {
175  // Remove doesn't bother to check to see if the socket is within any
176  // unprocessed event within _events. A more robust polling and eventing
177  // library might want to check this. For the stun server, "Remove" gets
178  // called immediately after WaitForNextEvent in most cases. That is, the socket
179  // we just got notified for isn't going to be within the _events array
180 
181  HRESULT hr = S_OK;
182  epoll_event ev={}; // pass empty ev, because some implementations of epoll_ctl can't handle a NULL event struct
183 
184  ChkIfA(fd == -1, E_INVALIDARG);
185  ChkIfA(_epollfd==-1, E_UNEXPECTED);
186 
187  ChkIfA(epoll_ctl(_epollfd, EPOLL_CTL_DEL, fd, &ev) == -1, ERRNOHR);
188 Cleanup:
189  return hr;
190 }
191 
192 HRESULT CEpoll::ChangeEventSet(int fd, uint32_t eventflags)
193 {
194  HRESULT hr = S_OK;
195  epoll_event ev = {};
196 
197  ChkIfA(fd == -1, E_INVALIDARG);
198  ChkIfA(_epollfd==-1, E_UNEXPECTED);
199 
200  ev.data.fd = fd;
201  ev.events = ToNativeFlags(eventflags);
202 
203  ChkIfA(epoll_ctl(_epollfd, EPOLL_CTL_MOD, fd, &ev) == -1, ERRNOHR);
204 
205 Cleanup:
206  return hr;
207 }
208 
209 HRESULT CEpoll::WaitForNextEvent(PollEvent* pPollEvent, int timeoutMilliseconds)
210 {
211  HRESULT hr = S_OK;
212  epoll_event *pEvent = NULL;
213  int ret = 0;
214 
215  ChkIfA(_epollfd==-1, E_UNEXPECTED);
216 
217  if (_currentEventIndex >= _pendingCount)
218  {
219  _currentEventIndex = 0;
220  _pendingCount = 0;
221 
222  ret = ::epoll_wait(_epollfd, _events, _sizeEvents, timeoutMilliseconds);
223  ChkIf(ret <= -1, ERRNOHR);
224  ChkIf(ret == 0, S_FALSE);
225 
226  _pendingCount = (size_t)ret;
227  }
228 
229 
230  pEvent = &_events[_currentEventIndex];
231  _currentEventIndex++;
232 
233 
234  pPollEvent->fd = pEvent->data.fd;
235  pPollEvent->eventflags = FromNativeFlags(pEvent->events);
236 
237 Cleanup:
238  return hr;
239 }
240 
241 #endif // HAS_EPOLL
242 
243 // ------------------------------------------------------------------------------
244 
245 class CPoll :
246  public CBasicRefCount,
247  public CObjectFactory<CPoll>,
248  public IPolling
249 {
250 private:
251  std::vector<pollfd> _fds;
252  uint32_t _rotation;
253  uint32_t _unreadcount;
255 
256  FastHashDynamic<int, size_t> _hashtable; // maps socket to position in fds
257 
258  void Reindex();
259 
260  uint32_t ToNativeFlags(uint32_t eventflags);
261  uint32_t FromNativeFlags(uint32_t eventflags);
262 
263  bool FindNextEvent(PollEvent* pEvent);
264 
265 public:
266  virtual HRESULT Initialize(size_t maxSockets);
267  virtual HRESULT Close();
268  virtual HRESULT Add(int fd, uint32_t eventflags);
269  virtual HRESULT Remove(int fd);
270  virtual HRESULT ChangeEventSet(int fd, uint32_t eventflags);
271  virtual HRESULT WaitForNextEvent(PollEvent* pPollEvent, int timeoutMilliseconds);
272 
273  CPoll();
274  ~CPoll();
275 
277 };
278 
280 _rotation (0),
281 _unreadcount(0),
282 _fInitialized(false)
283 {
284 
285 }
286 
288 {
289  Close();
290 }
291 
292 uint32_t CPoll::ToNativeFlags(uint32_t eventflags)
293 {
294  uint32_t result = 0;
295 
296  if (eventflags & IPOLLING_READ) result |= POLLIN;
297  if (eventflags & IPOLLING_WRITE) result |= POLLOUT;
298 #ifdef POLLRDHUP
299  if (eventflags & IPOLLING_RDHUP) result |= POLLRDHUP;
300 #endif
301  if (eventflags & IPOLLING_HUP) result |= POLLHUP;
302  if (eventflags & IPOLLING_PRI) result |= POLLPRI;
303  if (eventflags & IPOLLING_ERROR) result |= POLLERR;
304 
305  return result;
306 }
307 
308 
309 uint32_t CPoll::FromNativeFlags(uint32_t eventflags)
310 {
311  uint32_t result = 0;
312 
313  if (eventflags & POLLIN) result |= IPOLLING_READ;
314  if (eventflags & POLLOUT) result |= IPOLLING_WRITE;
315 #ifdef POLLRDHUP
316  if (eventflags & POLLRDHUP) result |= IPOLLING_RDHUP;
317 #endif
318  if (eventflags & POLLHUP) result |= IPOLLING_HUP;
319  if (eventflags & POLLPRI) result |= IPOLLING_PRI;
320  if (eventflags & POLLERR) result |= IPOLLING_ERROR;
321 
322  return result;
323 }
324 
325 
326 HRESULT CPoll::Initialize(size_t maxSockets)
327 {
328  _fds.reserve(maxSockets);
329  _rotation = 0;
330  _unreadcount = 0;
331 
332  _hashtable.InitTable(maxSockets, 0);
333 
334  _fInitialized = true;
335 
336  return S_OK;
337 }
338 
340 {
341  _fds.clear();
342  _fInitialized = false;
343 
344  return S_OK;
345 }
346 
347 HRESULT CPoll::Add(int fd, uint32_t eventflags)
348 {
349  HRESULT hr = S_OK;
350  size_t pos = _fds.size();
351  pollfd pfd;
352 
353  ChkIfA(_fInitialized == false, E_FAIL);
354 
355  ChkIfA(_hashtable.Lookup(fd)!=NULL, E_UNEXPECTED);
356 
357  pfd.events = ToNativeFlags(eventflags);
358  pfd.fd = fd;
359  pfd.revents = 0;
360  _fds.push_back(pfd);
361  _hashtable.Insert(fd, pos);
362 Cleanup:
363  return hr;
364 }
365 
367 {
368 
369  // See notes below why pPos is declared volatile. Gets around a compiler bug
370  volatile size_t* pPos = NULL;
371 
372  size_t size = _fds.size();
373  size_t pos;
374  HRESULT hr = S_OK;
375 
376  ChkIfA(_fInitialized == false, E_FAIL);
377 
378  ASSERT(_hashtable.Size() == size);
379 
380  ChkIf(size == 0, E_FAIL);
381 
382  pPos = _hashtable.Lookup(fd);
383 
384  ChkIfA(pPos == NULL, E_FAIL);
385 
386  pos = *pPos;
387 
388  ChkIfA(pos >= size, E_FAIL);
389 
390  ChkIfA(_fds[pos].fd != fd, E_FAIL);
391 
392  if (pos != (size-1))
393  {
394  _fds[pos] = _fds[size-1];
395  pPos = _hashtable.Lookup(_fds[pos].fd);
396 
397  ASSERT(pPos != NULL);
398 
399  // If the volatile declaration above was not made, this block of code
400  // gets over-optimized on older GCC compilers (g++ 4.2.1 on BSD) with with -O2
401  // The following line would essentially not get executed.
402  // There are multiple workarounds, but "volatile" seems to work.
403  *pPos = pos;
404  }
405 
406  _hashtable.Remove(fd);
407  _fds.pop_back();
408 
409 Cleanup:
410  return hr;
411 }
412 
413 HRESULT CPoll::ChangeEventSet(int fd, uint32_t eventflags)
414 {
415  size_t* pPos = NULL;
416  size_t pos;
417  HRESULT hr = S_OK;
418  size_t size = _fds.size();
419 
420  ChkIfA(_fInitialized == false, E_FAIL);
421 
422  ASSERT(_hashtable.Size() == size);
423  ChkIf(size == 0, E_FAIL);
424 
425  pPos = _hashtable.Lookup(fd);
426  ChkIfA(pPos == NULL, E_FAIL);
427  pos = *pPos;
428  ChkIfA(pos >= size, E_FAIL);
429  ChkIfA(_fds[pos].fd != fd, E_FAIL);
430  _fds[pos].events = ToNativeFlags(eventflags);
431 
432 Cleanup:
433  return hr;
434 }
435 
436 HRESULT CPoll::WaitForNextEvent(PollEvent* pPollEvent, int timeoutMilliseconds)
437 {
438  HRESULT hr = S_OK;
439  int ret;
440  size_t size = _fds.size();
441  pollfd* list = NULL;
442  bool fFound = false;
443 
444  ChkIfA(_fInitialized == false, E_FAIL);
445 
446  ChkIfA(pPollEvent == NULL, E_INVALIDARG);
447  pPollEvent->eventflags = 0;
448 
449  ChkIf(size == 0, S_FALSE);
450 
451  // check first to see if there is a pending event from the last poll() call
452  fFound = FindNextEvent(pPollEvent);
453 
454  if (fFound == false)
455  {
456  ASSERT(_unreadcount == 0);
457 
458  _unreadcount = 0;
459 
460  list = &_fds.front();
461 
462  ret = poll(list, size, timeoutMilliseconds);
463 
464  ChkIfA(ret < 0, ERRNOHR); // error
465  ChkIf(ret == 0, S_FALSE); // no data, we timed out
466 
467  _unreadcount = (uint32_t)ret;
468 
469  fFound = FindNextEvent(pPollEvent);
470  ASSERT(fFound); // poll returned a positive value, but we didn't find anything?
471  }
472 
473  hr = fFound ? S_OK : S_FALSE;
474 
475 Cleanup:
476  return hr;
477 }
478 
480 {
481  size_t size = _fds.size();
482  ASSERT(size > 0);
483  pollfd* list = &_fds.front();
484  bool fFound = false;
485 
486  if (_unreadcount == 0)
487  {
488  return false;
489  }
490 
491  if (_rotation >= size)
492  {
493  _rotation = 0;
494  }
495 
496  for (size_t index = 0; index < size; index++)
497  {
498  size_t slotindex = (index + _rotation) % size;
499 
500  if (list[slotindex].revents)
501  {
502  pEvent->fd = list[slotindex].fd;
503  pEvent->eventflags = FromNativeFlags(list[slotindex].revents);
504  list[slotindex].revents = 0;
505  fFound = true;
506  _rotation++;
507  _unreadcount--;
508  break;
509  }
510  }
511 
512  // don't increment _rotation if we didn't find anything
513 
514  return fFound;
515 }
516 
517 
518 
519 HRESULT CreatePollingInstance(uint32_t type, size_t maxSockets, IPolling** ppPolling)
520 {
521  HRESULT hr = S_OK;
522 
523  ChkIfA(ppPolling == NULL, E_INVALIDARG);
524 
525 #ifdef HAS_EPOLL
526  if (type == IPOLLING_TYPE_BEST)
527  {
528  type = IPOLLING_TYPE_EPOLL;
529  }
530 #else
531  if (type == IPOLLING_TYPE_BEST)
532  {
533  type = IPOLLING_TYPE_POLL;
534  }
535 #endif
536 
537 
538  if (type == IPOLLING_TYPE_EPOLL)
539  {
540 #ifndef HAS_EPOLL
541  ChkA(E_FAIL);
542 #else
543  ChkA(CEpoll::CreateInstance(maxSockets, ppPolling));
544 #endif
545  }
546  else if (type == IPOLLING_TYPE_POLL)
547  {
548  ChkA(CPoll::CreateInstance(maxSockets, ppPolling));
549  }
550  else
551  {
552  ChkA(E_FAIL); // unknown type
553  }
554 
555 Cleanup:
556  return hr;
557 }
558 
#define S_OK
Definition: hresult.h:46
#define ASSERT(expr)
virtual HRESULT ChangeEventSet(int fd, uint32_t eventflags)
Definition: polling.cpp:413
const uint32_t IPOLLING_TYPE_EPOLL
Definition: polling.h:51
uint32_t ToNativeFlags(uint32_t eventflags)
Definition: polling.cpp:292
virtual HRESULT WaitForNextEvent(PollEvent *pPollEvent, int timeoutMilliseconds)=0
const uint32_t IPOLLING_PRI
Definition: polling.h:34
~CPoll()
Definition: polling.cpp:287
const uint32_t IPOLLING_RDHUP
Definition: polling.h:32
virtual HRESULT ChangeEventSet(int fd, uint32_t eventflags)=0
uint32_t _rotation
Definition: polling.cpp:252
virtual HRESULT Initialize(size_t maxSockets)
Definition: polling.cpp:326
HRESULT CreatePollingInstance(uint32_t type, size_t maxSockets, IPolling **ppPolling)
Definition: polling.cpp:519
const uint32_t IPOLLING_TYPE_POLL
Definition: polling.h:52
const uint32_t IPOLLING_TYPE_BEST
Definition: polling.h:50
virtual HRESULT Close()=0
const uint32_t IPOLLING_ERROR
Definition: polling.h:35
#define S_FALSE
Definition: hresult.h:47
uint32_t eventflags
Definition: polling.h:24
const uint32_t IPOLLING_WRITE
Definition: polling.h:30
#define ADDREF_AND_RELEASE_IMPL()
bool _fInitialized
Definition: polling.cpp:254
uint32_t _unreadcount
Definition: polling.cpp:253
int Insert(const K &key, V &value)
Definition: fasthash.h:284
#define ChkIf(expr, hrerror)
Definition: chkmacros.h:63
#define E_UNEXPECTED
Definition: hresult.h:48
virtual HRESULT Add(int fd, uint32_t eventflags)=0
const uint32_t IPOLLING_READ
Definition: polling.h:29
virtual HRESULT Initialize(size_t maxSockets)=0
CPoll()
Definition: polling.cpp:279
virtual HRESULT Remove(int fd)=0
const uint32_t IPOLLING_EDGETRIGGER
Definition: polling.h:31
virtual HRESULT Close()
Definition: polling.cpp:339
V * Lookup(const K &key)
Definition: fasthash.h:351
virtual HRESULT Add(int fd, uint32_t eventflags)
Definition: polling.cpp:347
int32_t HRESULT
Definition: hresult.h:22
virtual HRESULT Remove(int fd)
Definition: polling.cpp:366
FastHashDynamic< int, size_t > _hashtable
Definition: polling.cpp:256
uint32_t FromNativeFlags(uint32_t eventflags)
Definition: polling.cpp:309
std::vector< pollfd > _fds
Definition: polling.cpp:251
#define E_INVALIDARG
Definition: hresult.h:51
#define E_FAIL
Definition: hresult.h:56
int InitTable(size_t fsize, size_t tsize)
Definition: fasthash.h:471
bool FindNextEvent(PollEvent *pEvent)
Definition: polling.cpp:479
int Remove(const K &key)
Definition: fasthash.h:316
#define FAILED(hr)
Definition: hresult.h:29
#define ChkA(expr)
Definition: chkmacros.h:73
size_t Size()
Definition: fasthash.h:269
const uint32_t IPOLLING_HUP
Definition: polling.h:33
#define E_OUTOFMEMORY
Definition: hresult.h:50
static HRESULT CreateInstance(I **ppI)
Definition: objectfactory.h:52
int fd
Definition: polling.h:23
virtual HRESULT WaitForNextEvent(PollEvent *pPollEvent, int timeoutMilliseconds)
Definition: polling.cpp:436
#define ChkIfA(expr, hrerror)
Definition: chkmacros.h:84
#define ERRNOHR
Definition: hresult.h:42