reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
FdPoll.cxx
Go to the documentation of this file.
1#include "rutil/ResipAssert.h"
2#include <string.h>
3
4#include "rutil/FdPoll.hxx"
5#include "rutil/Logger.hxx"
7
8#include <vector>
9
10#ifdef RESIP_POLL_IMPL_EPOLL
11# include <sys/epoll.h>
12#endif
13#if defined(RESIP_POLL_IMPL_POLL) && !defined(WIN32)
14# include <poll.h>
15#endif
16
17using namespace resip;
18#define RESIPROCATE_SUBSYSTEM Subsystem::SIP
19
20/*****************************************************************
21 *
22 * FdPollItemIf and FdPollItemBase impl
23 *
24 *****************************************************************/
25
29
31 mPollGrp(grp), mPollSocket(fd), mPollHandle(0)
32{
33 if(mPollGrp)
34 {
35 mPollHandle = mPollGrp->addPollItem(fd, mask, this);
36 }
37}
38
46
47/*****************************************************************
48 *
49 * FdPollGrp
50 *
51 * Implementation for some of the base class methods.
52 * While some of these are epoll-specific, we can (and do) implement
53 * them at this level.
54 * For now we use delegation for the impl data structures
55 * rather than inhieritance. Long term, not sure which will
56 * be cleaner.
57 *
58 *****************************************************************/
59
63
67
68void
70{
71 try
72 {
73 item->processPollEvent( mask );
74 }
75 catch(BaseException& e)
76 {
77 // kill it or something?
78 ErrLog(<<"Exception thrown for FdPollItem: " << e);
79 }
80 item = NULL; // WATCHOUT: item may have been deleted
81 /*
82 * If FPEM_Error was reported, should really make sure it was deleted
83 * or disabled from polling. Otherwise were in stuck in an infinite loop.
84 * But difficult to do that checking robustly until we serials the items.
85 */
86}
87
88int
90{
91 return -1;
92}
93
94/*****************************************************************
95 *
96 * FdPollImplFdSet
97 *
98 *****************************************************************/
99
106namespace resip
107{
108
110{
111 public:
116
117 Socket mSocketFd; // socket
118 FdPollItemIf* mItemObj; // callback object
119 FdPollEventMask mEvMask; // events the application wants
120 size_t mNextIdx; // next link for live or free list
121};
122
124{
125 public:
128
129 virtual const char* getImplName() const { return "fdset"; }
130 virtual ImplType getImplType() const { return FdSetImpl; }
131
133 virtual void modPollItem(FdPollItemHandle handle, FdPollEventMask newMask);
134 virtual void delPollItem(FdPollItemHandle handle);
135
136 virtual bool waitAndProcess(int ms=0);
137 virtual void buildFdSet(FdSet& fdSet);
138 virtual bool processFdSet(FdSet& fdset);
139
140 protected:
141 void killCache(Socket fd);
142
143 std::vector<FdPollItemFdSetInfo> mItems;
144
145 /*
146 * The ItemInfos are stored in a vector (above) that grows as needed.
147 * Every Info is in one single-linked list, either the "Live" list
148 * or the "Free" list. This is somewhat like using
149 * boost::intrusive::slist, except we use indices not pointers
150 * since the vector may reallocate and move around.
151 */
154
155 /*
156 * This is temporary cache of poll events. It is a member (and
157 * not on stack) for two reasons: (1) simpler memory management,
158 * and (2) so delPollItem() can traverse it and clean up.
159 */
161};
162
163}; // namespace
164
165// NOTE: shift by one so that idx=0 doesn't have NULL handle
166#define IMPL_FDSET_IdxToHandle(idx) ((FdPollItemHandle)( ((char*)0) + ((idx)+1) ))
167#define IMPL_FDSET_HandleToIdx(handle) ( ((char*)(handle)) - ((char*)0) - 1)
168
170 : mLiveHeadIdx(-1), mFreeHeadIdx(-1)
171{
172}
173
175{
176 unsigned itemIdx;
177 for (itemIdx=0; itemIdx < mItems.size(); itemIdx++)
178 {
179 FdPollItemFdSetInfo& info = mItems[itemIdx];
180 if (info.mItemObj)
181 {
182 CritLog(<<"FdPollItem idx="<<itemIdx
183 <<" not deleted prior to destruction");
184 }
185 }
186}
187
190{
191 // if this isn't true then the linked lists will get messed up
192 resip_assert(item);
194
195 size_t useIdx;
196 if (mFreeHeadIdx != (size_t)-1)
197 {
198 useIdx = mFreeHeadIdx;
199 mFreeHeadIdx = mItems[useIdx].mNextIdx;
200 }
201 else
202 {
203 useIdx = mItems.size();
204 size_t newsz = 10+useIdx + useIdx/3; // plus 30% margin
205 // WATCHOUT: below may trigger re-allocation, invalidating any iters
206 // We don't use iters (only indices), but need to watchout for
207 // cached pointers
208 mItems.resize(newsz);
209 // push new items onto the free list
210 size_t itemIdx;
211 for (itemIdx=useIdx+1; itemIdx < newsz; itemIdx++)
212 {
213 mItems[itemIdx].mNextIdx = mFreeHeadIdx;
214 mFreeHeadIdx = itemIdx;
215 }
216 }
217 FdPollItemFdSetInfo& info = mItems[useIdx];
218 info.mItemObj = item;
219 info.mSocketFd = fd;
220 info.mEvMask = newMask;
221 info.mNextIdx = mLiveHeadIdx;
222 mLiveHeadIdx = useIdx;
223
227
228 return IMPL_FDSET_IdxToHandle(useIdx);
229}
230
231void
233{
234 size_t useIdx = IMPL_FDSET_HandleToIdx(handle);
235 resip_assert(useIdx != (size_t)-1 && useIdx < mItems.size());
236 FdPollItemFdSetInfo& info = mItems[useIdx];
239 info.mEvMask = newMask;
240
241 if(info.mSocketFd != INVALID_SOCKET)
242 {
243 killCache(info.mSocketFd);
247 }
248}
249
250void
252{
253 if(!handle) return;
254
255 size_t useIdx = IMPL_FDSET_HandleToIdx(handle);
256 //DebugLog(<<"deleting epoll item fd="<<fd);
257 resip_assert(useIdx != (size_t)-1 && useIdx < mItems.size());
258 FdPollItemFdSetInfo& info = mItems[useIdx];
261 killCache(info.mSocketFd);
262 // we don't change the lists here since the select loop might
263 // be iterating. Just mark it as dead and gc it later.
265 info.mItemObj = NULL;
266 info.mEvMask = 0;
267}
268
269
290void
295
296bool
298{
299 if(ms<0)
300 {
301 // On Linux, passing a NULL timeout ptr to select() will wait
302 // forever, but I don't want to trust that on all platforms.
303 // So use 60sec as approximation of "forever".
304 // Use 60sec b/c fits in short.
305 ms = 60*1000;
306 }
307
308 // Create copy; is cheaper than rebuilding from scratch every time.
309 FdSet fdset(mSelectSet);
310
311 // Step 2: Select on our built FdSet
312 int numReady = fdset.selectMilliSeconds(ms);
313 if ( numReady < 0 )
314 {
315 int err = getErrno();
316 if ( err!=EINTR )
317 {
318 CritLog(<<"select() failed: " <<strError(err));
319 resip_assert(0); // .kw. not sure correct behavior...
320 }
321 return false;
322 }
323
324 if ( numReady==0 )
325 {
326 return false; // timer expired
327 }
328
329 return processFdSet(fdset);
330}
331
332void
334{
335 size_t* prevIdxRef=&mLiveHeadIdx;
336 int loopCnt = 0;
337 size_t itemIdx;
338
339 // Step 1: build a new FdSet from the Items vector
340 while ( (itemIdx = *prevIdxRef) != (size_t)-1 )
341 {
342 resip_assert( ++loopCnt < 99123123 );
343 FdPollItemFdSetInfo& info = mItems[itemIdx];
344 if ( info.mItemObj==0 )
345 {
346 // item was deleted, need to garbage collect
347 resip_assert( info.mEvMask==0 );
348 // unlink from live list
349 *prevIdxRef = info.mNextIdx;
350 // link into free list
351 info.mNextIdx = mFreeHeadIdx;
352 mFreeHeadIdx = itemIdx;
353 continue;
354 }
355 if ( info.mEvMask!=0 )
356 {
358 if(info.mEvMask & FPEM_Read) fdset.setRead(info.mSocketFd);
359 if(info.mEvMask & FPEM_Write) fdset.setWrite(info.mSocketFd);
360 if(info.mEvMask & FPEM_Error) fdset.setExcept(info.mSocketFd);
361 }
362 prevIdxRef = &info.mNextIdx;
363 }
364}
365
366bool
368{
369 bool didsomething = false;
370 size_t itemIdx;
371 size_t* prevIdxRef = &mLiveHeadIdx;
372 int loopCnt = 0;
373
374 // Step 3: Invoke callbacks
375 // Could take advantage of early via numReady, but book keeping
376 // seems tedious especially if items are deleted during walk
377 while ( (itemIdx = *prevIdxRef) != (size_t)-1 )
378 {
379 FdPollItemFdSetInfo& info = mItems[itemIdx];
380 resip_assert( ++loopCnt < 99123123 );
381 if ( info.mEvMask!=0 && info.mItemObj!=0 )
382 {
383 FdPollEventMask usrMask = 0;
385 if(fdset.readyToRead(info.mSocketFd)) usrMask |= FPEM_Read;
386 if(fdset.readyToWrite(info.mSocketFd)) usrMask |= FPEM_Write;
387 if(fdset.hasException(info.mSocketFd)) usrMask |= FPEM_Error;
388
389 // items's mask may have changed since select occured, so mask it again
390 usrMask &= info.mEvMask;
391 if ( usrMask )
392 {
393 processItem(info.mItemObj, usrMask);
394 didsomething = true;
395 }
396 }
397 // WATCHOUT: {info} may have moved due to add during processItem()
398 // set pointer using index, not {info}
399 prevIdxRef = &mItems[itemIdx].mNextIdx;
400 }
401
402 return didsomething;
403}
404
405// end of ImplFdSet
406
407
408/*****************************************************************
409 *
410 * FdPollImplPoll
411 *
412 *****************************************************************/
413#ifdef RESIP_POLL_IMPL_POLL
414
415namespace resip
416{
417
418class FdPollItemPollInfo
419{
420 public:
421 FdPollItemPollInfo()
422 : mSocketFd(INVALID_SOCKET), mItemObj(0), mFdPollCacheIndex(-1)
423 {
424 }
425
426 Socket mSocketFd; // socket
427 FdPollItemIf* mItemObj; // callback object
428 unsigned int mFdPollCacheIndex;
429};
430
431class FdPollImplPoll : public FdPollGrp
432{
433 public:
434 FdPollImplPoll();
435 ~FdPollImplPoll();
436
437 virtual const char* getImplName() const { return "poll"; }
438 virtual ImplType getImplType() const { return PollImpl; }
439
440 virtual FdPollItemHandle addPollItem(Socket fd, FdPollEventMask newMask, FdPollItemIf *item);
441 virtual void modPollItem(FdPollItemHandle handle, FdPollEventMask newMask);
442 virtual void delPollItem(FdPollItemHandle handle);
443
444 virtual bool waitAndProcess(int ms=0);
445
447 virtual int getEPollFd() const { return -1; }
448 virtual void buildFdSet(FdSet& fdSet);
449 virtual bool processFdSet(FdSet& fdset);
450
451 protected:
452 typedef std::map<Socket, FdPollItemPollInfo> FdPollItemPollInfoMap;
453 FdPollItemPollInfoMap mItems; // indexed by fd/handle
454
455 /*
456 * This is temporary cache of pollfds. It is a member (and
457 * not on stack) for two reasons: (1) simpler memory management,
458 * and (2) so delPollItem() can traverse it and clean up.
459 */
460 std::vector<pollfd> mPollFdCache; // This list is adjustable while we are waiting/processing
461 std::vector<pollfd> mPollFds;
462 RecursiveMutex mMutex;
463};
464
465// NOTE: shift by one so that fd=0 doesn't have NULL handle
466#define IMPL_POLL_FdToHandle(fd) ((FdPollItemHandle)( ((char*)0) + ((fd)+1) ))
467#define IMPL_POLL_HandleToFd(handle) ( ((char*)(handle)) - ((char*)0) - 1)
468
469}; // namespace
470
471FdPollImplPoll::FdPollImplPoll()
472{
473 int sz = 200;
474 mPollFdCache.reserve(sz);
475 mPollFds.reserve(sz);
476}
477
478FdPollImplPoll::~FdPollImplPoll()
479{
480 FdPollItemPollInfoMap::iterator it;
481 for(it = mItems.begin(); it != mItems.end(); it++)
482 {
483 CritLog(<<"FdPollItem fd=" << it->first <<" not deleted prior to destruction");
484 }
485}
486
487static inline unsigned short
488CvtPollToUsrMask(unsigned long sysMask)
489{
490 unsigned usrMask = 0;
491 if(sysMask & POLLIN) usrMask |= FPEM_Read;
492 if(sysMask & POLLOUT) usrMask |= FPEM_Write;
493 // POLLNVAL: the socket was closed while still in the set
494 if(sysMask & (POLLERR|POLLHUP|POLLNVAL)) usrMask |= FPEM_Error|FPEM_Read|FPEM_Write;
495 // NOTE: above, fake read and write if error to encourage
496 // apps to actually do something about it
497 return usrMask;
498}
499
500static inline unsigned long
501CvtUsrToPollMask(unsigned short usrMask)
502{
503 unsigned long sysMask = 0;
504 if(usrMask & FPEM_Read) sysMask |= POLLIN;
505 if(usrMask & FPEM_Write) sysMask |= POLLOUT;
506 //if(usrMask & FPEM_Error) sysMask |= POLLERR; // Note: We don't need to ask for error signalling, POLLERR is an output mask only for revents member
507 return sysMask;
508}
509
511FdPollImplPoll::addPollItem(Socket fd, FdPollEventMask newMask, FdPollItemIf *item)
512{
513 resip_assert(fd>=0);
514 //InfoLog(<<"adding poll item fd="<<fd);
515
516 RecursiveLock lock(mMutex);
517 FdPollItemPollInfo& info = mItems[fd];
518 info.mSocketFd = fd;
519 info.mItemObj = item;
520 info.mFdPollCacheIndex = (unsigned int)mPollFdCache.size();
521
522 pollfd pollFD;
523 pollFD.fd = fd;
524 pollFD.events = (short)CvtUsrToPollMask(newMask);
525 mPollFdCache.push_back(pollFD);
526
527 return IMPL_POLL_FdToHandle(fd);
528}
529
530void
531FdPollImplPoll::modPollItem(const FdPollItemHandle handle, FdPollEventMask newMask)
532{
533 int fd = (int)IMPL_POLL_HandleToFd(handle);
534
535 RecursiveLock lock(mMutex);
536 FdPollItemPollInfoMap::iterator it = mItems.find(fd);
537 if(it != mItems.end())
538 {
539 FdPollItemPollInfo& info = it->second;
540 resip_assert(info.mSocketFd!=INVALID_SOCKET);
541 resip_assert(info.mItemObj);
542
543 if(info.mSocketFd != INVALID_SOCKET)
544 {
545 mPollFdCache[info.mFdPollCacheIndex].events = (short)CvtUsrToPollMask(newMask);
546 }
547 }
548}
549
550void
551FdPollImplPoll::delPollItem(FdPollItemHandle handle)
552{
553 int fd = (int)IMPL_POLL_HandleToFd(handle);
554 //InfoLog(<<"deleting poll item fd="<<fd);
555
556 RecursiveLock lock(mMutex);
557 FdPollItemPollInfoMap::iterator it = mItems.find(fd);
558 if(it != mItems.end())
559 {
560 FdPollItemPollInfo& info = it->second;
561 resip_assert(info.mSocketFd!=INVALID_SOCKET);
562 resip_assert(info.mItemObj);
563 resip_assert(info.mFdPollCacheIndex != -1);
564 resip_assert(mPollFdCache.size() >= 1);
565
566 if(mPollFdCache.size() > 1)
567 {
568 // About to reassign this cache slot to be the current last item in the cache, then
569 // we will remove the last item
570 size_t lastCacheIndex = mPollFdCache.size() - 1;
571
572 // Adjust index of last item in cache - to be index of deleted item
573 mItems[mPollFdCache[lastCacheIndex].fd].mFdPollCacheIndex = info.mFdPollCacheIndex;
574
575 // Adjust Cache - reassign index being deleted to last index
576 mPollFdCache[info.mFdPollCacheIndex] = mPollFdCache[mPollFdCache.size() - 1];
577 }
578 // Remove last cache item - no longer used - was last item, or re-assigned above
579 mPollFdCache.pop_back();
580
581 // Remove from Map
582 mItems.erase(it);
583 }
584}
585
586bool
587FdPollImplPoll::waitAndProcess(int ms)
588{
589 // Copy vector - cheaper than rebuilding from scratch each time
590 // Need to copy, since vector cannot be changed while Poll is running.
591 { // Scope for Lock
592 RecursiveLock lock(mMutex);
593 mPollFds = mPollFdCache;
594 //InfoLog(<<"FdPollImplPoll::waitAndProcess() ms=" << ms << ", numFds " << mPollFds.size());
595 }
596
597 if(mPollFds.size() == 0)
598 {
599 // no handles to poll
600 return false;
601 }
602
603 bool didsomething=false;
604
605 pollfd *pollFDArray = &(mPollFds.front());
606#ifdef WIN32
607 int numReadyFDs = WSAPoll(pollFDArray, (ULONG)mPollFds.size(), ms);
608#else
609 int numReadyFDs = poll(pollFDArray, mPollFds.size(), ms);
610#endif
611 if ( numReadyFDs < 0 )
612 {
613 int err = getErrno();
614 if ( err != EINTR )
615 {
616 CritLog(<<"poll() failed: " << err << " " << strError(err));
617 resip_assert(0); // .kw. not sure correct behavior...
618 }
619 return false;
620 }
621
622 if ( numReadyFDs==0 )
623 {
624 return false; // timer expired
625 }
626
627 // Process poll result now
628 { // Scope for Lock
629 RecursiveLock lock(mMutex);
630 for (size_t index = 0; index < mPollFds.size() && numReadyFDs > 0; index++)
631 {
632 int revents = pollFDArray[index].revents;
633 if (revents)
634 {
635 //InfoLog(<<"FdPollImplPoll::waitAndProcess() fd=" << pollFDArray[index].fd << " signalled, revent=" << revents);
636
637 numReadyFDs--;
638 FdPollItemPollInfoMap::iterator it = mItems.find(pollFDArray[index].fd);
639 if(it != mItems.end())
640 {
641 FdPollItemIf* pdPollItem = it->second.mItemObj;
642 processItem(pdPollItem, CvtPollToUsrMask(revents));
643 didsomething = true;
644 }
645 }
646 }//for
647 }
648
649 return didsomething;
650}
651
652void
653FdPollImplPoll::buildFdSet(FdSet& fdset)
654{
655 CritLog(<<"buildFdSet failed - API not supported for FdPollImplPoll.");
656 resip_assert(false);
657}
658
659bool
660FdPollImplPoll::processFdSet(FdSet& fdset)
661{
662 CritLog(<<"processFdSet failed - API not supported for FdPollImplPoll.");
663 resip_assert(false);
664 return false;
665}
666
667#endif // RESIP_POLL_IMPL_POLL
668
669
670
671/*****************************************************************
672 *
673 * FdPollImplEpoll
674 *
675 *****************************************************************/
676
677#ifdef RESIP_POLL_IMPL_EPOLL
678
679namespace resip
680{
681
682class FdPollImplEpoll : public FdPollGrp
683{
684 public:
685 FdPollImplEpoll();
686 ~FdPollImplEpoll();
687
688 virtual const char* getImplName() const { return "epoll"; }
689 virtual ImplType getImplType() const { return EPollImpl; }
690
691 virtual FdPollItemHandle addPollItem(Socket fd,
692 FdPollEventMask newMask, FdPollItemIf *item);
693 virtual void modPollItem(FdPollItemHandle handle,
694 FdPollEventMask newMask);
695 virtual void delPollItem(FdPollItemHandle handle);
696
697 virtual bool waitAndProcess(int ms=0);
698
700 virtual int getEPollFd() const { return mEPollFd; }
701 virtual void buildFdSet(FdSet& fdSet);
702 virtual bool processFdSet(FdSet& fdset);
703
704 protected:
705 void killCache(Socket fd);
706 bool epollWait(int ms);
707
708 std::vector<FdPollItemIf*> mItems; // indexed by fd
709 int mEPollFd; // from epoll_create()
710
711 /*
712 * This is temporary cache of poll events. It is a member (and
713 * not on stack) for two reasons: (1) simpler memory management,
714 * and (2) so delPollItem() can traverse it and clean up.
715 */
716 std::vector<struct epoll_event> mEvCache;
717 int mEvCacheCur;
718 int mEvCacheLen;
719};
720
721}; // namespace
722
723// NOTE: shift by one so that fd=0 doesn't have NULL handle
724#define IMPL_EPOLL_FdToHandle(fd) ((FdPollItemHandle)( ((char*)0) + ((fd)+1) ))
725#define IMPL_EPOLL_HandleToFd(handle) ( ((char*)(handle)) - ((char*)0) - 1)
726
727FdPollImplEpoll::FdPollImplEpoll() :
728 mEPollFd(-1)
729{
730 int sz = 200; // ignored
731 if ( (mEPollFd = epoll_create(sz)) < 0 )
732 {
733 CritLog(<<"epoll_create() failed: "<<strError(errno));
734 abort();
735 }
736 mEvCache.resize(sz);
737 mEvCacheCur = mEvCacheLen = 0;
738}
739
740FdPollImplEpoll::~FdPollImplEpoll()
741{
742 resip_assert( mEvCacheLen == 0 ); // poll not active
743 unsigned itemIdx;
744 for (itemIdx=0; itemIdx < mItems.size(); itemIdx++)
745 {
746 FdPollItemIf *item = mItems[itemIdx];
747 if (item)
748 {
749 CritLog(<<"FdPollItem idx="<<itemIdx
750 <<" not deleted prior to destruction");
751 }
752 }
753 if (mEPollFd != -1)
754 {
755 close(mEPollFd);
756 }
757}
758
759static inline unsigned short
760CvtSysToUsrMask(unsigned long sysMask)
761{
762 unsigned usrMask = 0;
763 if(sysMask & EPOLLIN) usrMask |= FPEM_Read;
764 if(sysMask & EPOLLOUT) usrMask |= FPEM_Write;
765 if(sysMask & EPOLLERR) usrMask |= FPEM_Error|FPEM_Read|FPEM_Write;
766 // NOTE: above, fake read and write if error to encourage
767 // apps to actually do something about it
768 return usrMask;
769}
770
771static inline unsigned long
772CvtUsrToSysMask(unsigned short usrMask)
773{
774 unsigned long sysMask = 0;
775 if(usrMask & FPEM_Read) sysMask |= EPOLLIN;
776 if(usrMask & FPEM_Write) sysMask |= EPOLLOUT;
777 if(usrMask & FPEM_Edge) sysMask |= EPOLLET;
778 return sysMask;
779}
780
782FdPollImplEpoll::addPollItem(Socket fd, FdPollEventMask newMask, FdPollItemIf *item)
783{
784 resip_assert(fd>=0);
785 //DebugLog(<<"adding epoll item fd="<<fd);
786 if (mItems.size() <= (unsigned)fd)
787 {
788 unsigned newsz = fd+1;
789 newsz += newsz/3; // plus 30% margin
790 // WATCHOUT: below may trigger re-allocation, invalidating any iters
791 // Currently only iterator is destructor, so should be safe
792 mItems.resize(newsz);
793 }
794 FdPollItemIf *olditem = mItems[fd];
795 resip_assert(olditem == NULL); // what is right thing to do?
796 mItems[fd] = item;
797 struct epoll_event ev;
798 memset(&ev, 0, sizeof(ev)); // make valgrind happy
799 ev.events = CvtUsrToSysMask(newMask);
800 ev.data.fd = fd;
801 if (epoll_ctl(mEPollFd, EPOLL_CTL_ADD, fd, &ev) < 0)
802 {
803 CritLog(<<"epoll_ctl(ADD) failed: " << strError(errno));
804 abort();
805 }
806 return IMPL_EPOLL_FdToHandle(fd);
807}
808
809void
810FdPollImplEpoll::modPollItem(const FdPollItemHandle handle, FdPollEventMask newMask)
811{
812 int fd = IMPL_EPOLL_HandleToFd(handle);
813 resip_assert(fd>=0 && ((unsigned)fd) < mItems.size());
814 resip_assert(mItems[fd] != NULL);
815
816 struct epoll_event ev;
817 memset(&ev, 0, sizeof(ev)); // make valgrind happy
818 ev.events = CvtUsrToSysMask(newMask);
819 ev.data.fd = fd;
820 if (epoll_ctl(mEPollFd, EPOLL_CTL_MOD, fd, &ev) < 0)
821 {
822 CritLog(<<"epoll_ctl(MOD) failed: "<<strError(errno));
823 abort();
824 }
825}
826
827void
828FdPollImplEpoll::delPollItem(FdPollItemHandle handle)
829{
830 int fd = IMPL_EPOLL_HandleToFd(handle);
831 //DebugLog(<<"deleting epoll item fd="<<fd);
832 resip_assert(fd>=0 && ((unsigned)fd) < mItems.size());
833 resip_assert( mItems[fd] != NULL );
834 mItems[fd] = NULL;
835 if (epoll_ctl(mEPollFd, EPOLL_CTL_DEL, fd, NULL) < 0)
836 {
837 CritLog(<<"epoll_ctl(DEL) fd="<<fd<<" failed: " << strError(errno));
838 abort();
839 }
840 killCache(fd);
841}
842
843
867void
868FdPollImplEpoll::killCache(int fd)
869{
870 int ne;
871 for (ne=mEvCacheCur; ne < mEvCacheLen; ne++)
872 {
873 if ( mEvCache[ne].data.fd == fd )
874 {
875 mEvCache[ne].data.fd = INVALID_SOCKET;
876 }
877 }
878}
879
880
881bool
882FdPollImplEpoll::waitAndProcess(int ms)
883{
884 resip_assert( mEvCache.size() > 0 );
885
886 return epollWait(ms);
887}
888
889void
890FdPollImplEpoll::buildFdSet(FdSet& fdset)
891{
892 int fd = getEPollFd();
893 if (fd != -1)
894 {
895 fdset.setRead(fd);
896 }
897}
898
899bool
900FdPollImplEpoll::processFdSet(FdSet& fdset)
901{
902 int fd = getEPollFd();
903 if (fd !=- 1 && fdset.readyToRead(fd))
904 {
905 return epollWait(0);
906 }
907 return false;
908}
909
910bool
911FdPollImplEpoll::epollWait(int waitMs)
912{
913 bool maybeMore;
914 bool didsomething=false;
915 do
916 {
917 int nfds = epoll_wait(mEPollFd, &mEvCache.front(), mEvCache.size(), waitMs);
918 if (nfds < 0)
919 {
920 if (errno==EINTR)
921 {
922 // signal handler (like alarm) broke loop. generally ok
923 DebugLog(<<"epoll_wait() broken by EINTR");
924 nfds = 0; // clean-up and return. could add return code
925 // to indicate this, but not needed by us
926 }
927 else
928 {
929 CritLog(<<"epoll_wait() failed: " << strError(errno));
930 abort(); // TBD: just throw instead?
931 }
932 }
933 waitMs = 0; // don't wait anymore
934 mEvCacheLen = nfds; // for killCache()
935 maybeMore = ( ((unsigned)nfds)==mEvCache.size()) ? 1 : 0;
936 int ne;
937 for (ne=0; ne < nfds; ne++)
938 {
939 int fd = mEvCache[ne].data.fd;
940 if (fd == INVALID_SOCKET)
941 {
942 continue; // was killed by killCache()
943 }
944 int sysEvtMask = mEvCache[ne].events;
945 resip_assert(fd>=0 && fd < (int)mItems.size());
946 FdPollItemIf *item = mItems[fd];
947 if (item == NULL)
948 {
949 /* this can happen if item was deleted after
950 * event was generated in kernel, etc. */
951 continue;
952 }
953 mEvCacheCur = ne; // for killCache()
954 processItem(item, CvtSysToUsrMask(sysEvtMask));
955 item = NULL; // WATCHOUT: item may not exist anymore
956 didsomething = true;
957 }
958 mEvCacheLen = 0;
959 } while (maybeMore);
960 return didsomething;
961}
962
963#endif // RESIP_POLL_IMPL_EPOLL
964
965/*****************************************************************
966 *
967 * Factory
968 *
969 *****************************************************************/
970
971/*static*/FdPollGrp*
972FdPollGrp::create(const char *implName)
973{
974 if ( implName==0 || implName[0]==0 || strcmp(implName,"event")==0 )
975 implName = 0; // pick the first (best) one supported
976#ifdef RESIP_POLL_IMPL_EPOLL
977 if ( implName==0 || strcmp(implName,"epoll")==0 )
978 {
979 return new FdPollImplEpoll();
980 }
981#endif
982#ifdef RESIP_POLL_IMPL_POLL
983 if ( implName==0 || strcmp(implName,"poll")==0 )
984 {
985 return new FdPollImplPoll();
986 }
987#endif
988 if ( implName==0 || strcmp(implName,"fdset")==0 )
989 {
990 return new FdPollImplFdSet();
991 }
992 resip_assert(0);
993 return NULL;
994}
995
996/*static*/const char*
998{
999 // .kw. this isn't really scalable approach if we get a lot of impls
1000 // but it works for now
1001#ifdef RESIP_POLL_IMPL_EPOLL
1002 #ifdef RESIP_POLL_IMPL_POLL
1003 return "event|epoll|fdset|poll";
1004 #else
1005 return "event|epoll|fdset";
1006 #endif
1007#else
1008 #ifdef RESIP_POLL_IMPL_POLL
1009 return "event|fdset|poll";
1010 #else
1011 return "event|fdset";
1012 #endif
1013#endif
1014}
1015
1016/* ====================================================================
1017 * The Vovida Software License, Version 1.0
1018 *
1019 * Copyright (c) 2026 SIP Spectrum, Inc. https://www.sipspectrum.com
1020 * Copyright (c) 2000-2005 Jacob Butcher
1021 *
1022 * Redistribution and use in source and binary forms, with or without
1023 * modification, are permitted provided that the following conditions
1024 * are met:
1025 *
1026 * 1. Redistributions of source code must retain the above copyright
1027 * notice, this list of conditions and the following disclaimer.
1028 *
1029 * 2. Redistributions in binary form must reproduce the above copyright
1030 * notice, this list of conditions and the following disclaimer in
1031 * the documentation and/or other materials provided with the
1032 * distribution.
1033 *
1034 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
1035 * and "Vovida Open Communication Application Library (VOCAL)" must
1036 * not be used to endorse or promote products derived from this
1037 * software without prior written permission. For written
1038 * permission, please contact vocal@vovida.org.
1039 *
1040 * 4. Products derived from this software may not be called "VOCAL", nor
1041 * may "VOCAL" appear in their name, without prior written
1042 * permission of Vovida Networks, Inc.
1043 *
1044 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
1045 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1046 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
1047 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
1048 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
1049 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
1050 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1051 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1052 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
1053 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1054 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
1055 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
1056 * DAMAGE.
1057 *
1058 * vi: set shiftwidth=3 expandtab:
1059 */
#define IMPL_FDSET_HandleToIdx(handle)
Definition FdPoll.cxx:167
#define IMPL_FDSET_IdxToHandle(idx)
Definition FdPoll.cxx:166
#define FPEM_Edge
Definition FdPoll.hxx:32
#define FPEM_Write
Definition FdPoll.hxx:30
#define FPEM_Read
Definition FdPoll.hxx:29
#define FPEM_Error
Definition FdPoll.hxx:31
#define DebugLog(args_)
Definition Logger.hxx:128
#define CritLog(args_)
Definition Logger.hxx:140
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
#define INVALID_SOCKET
Definition Socket.hxx:95
int Socket
The abstract base-class for all exceptions thrown by resip code.
void processItem(FdPollItemIf *item, FdPollEventMask mask)
Definition FdPoll.cxx:69
static const char * getImplList()
Return candidate impl names with vertical bar (|) between them Intended for help messages.
Definition FdPoll.cxx:997
static FdPollGrp * create(const char *implName=NULL)
factory
Definition FdPoll.cxx:972
virtual FdPollItemHandle addPollItem(Socket sock, FdPollEventMask newMask, FdPollItemIf *item)=0
virtual ~FdPollGrp()
Definition FdPoll.cxx:64
virtual int getEPollFd() const
get the epoll-fd (epoll_create()) This is fd (type int), not Socket.
Definition FdPoll.cxx:89
virtual void delPollItem(FdPollItemHandle handle)=0
virtual ImplType getImplType() const
Definition FdPoll.cxx:130
void killCache(Socket fd)
There is a boundary case:
Definition FdPoll.cxx:291
virtual bool waitAndProcess(int ms=0)
Wait at most {ms} milliseconds.
Definition FdPoll.cxx:297
virtual FdPollItemHandle addPollItem(Socket fd, FdPollEventMask newMask, FdPollItemIf *item)
Definition FdPoll.cxx:189
virtual void delPollItem(FdPollItemHandle handle)
Definition FdPoll.cxx:251
std::vector< FdPollItemFdSetInfo > mItems
Definition FdPoll.cxx:143
virtual void buildFdSet(FdSet &fdSet)
Definition FdPoll.cxx:333
virtual bool processFdSet(FdSet &fdset)
Definition FdPoll.cxx:367
virtual const char * getImplName() const
Definition FdPoll.cxx:129
virtual void modPollItem(FdPollItemHandle handle, FdPollEventMask newMask)
Definition FdPoll.cxx:232
FdPollGrp * mPollGrp
Definition FdPoll.hxx:66
FdPollItemHandle mPollHandle
Definition FdPoll.hxx:68
virtual ~FdPollItemBase()
Definition FdPoll.cxx:39
FdPollItemBase(FdPollGrp *grp, Socket fd, FdPollEventMask mask)
Definition FdPoll.cxx:30
FdPollEventMask mEvMask
Definition FdPoll.cxx:119
FdPollItemIf * mItemObj
Definition FdPoll.cxx:118
virtual void processPollEvent(FdPollEventMask mask)=0
Called by PollGrp when activity is possible.
virtual ~FdPollItemIf()
Definition FdPoll.cxx:26
Object-oriented wrapper for your platform's file-descriptor set.
Definition Socket.hxx:162
void clear(Socket fd)
Definition Socket.hxx:239
bool readyToWrite(Socket fd)
Definition Socket.hxx:194
bool readyToRead(Socket fd)
Definition Socket.hxx:189
void setRead(Socket fd)
Definition Socket.hxx:204
void setExcept(Socket fd)
Definition Socket.hxx:227
int selectMilliSeconds(unsigned long ms)
Definition Socket.hxx:181
bool hasException(Socket fd)
Definition Socket.hxx:199
void setWrite(Socket fd)
Definition Socket.hxx:216
dcm! – add UnusedChecking(_enum) below;
unsigned short FdPollEventMask
Definition FdPoll.hxx:28
std::unique_lock< RecursiveMutex > RecursiveLock
Definition Lock.hxx:29
int Socket
Definition Socket.hxx:94
struct FdPollItemFake * FdPollItemHandle
This is opaque type used to identify a particular Item.
Definition FdPoll.hxx:42
int getErrno()
Definition Socket.hxx:97
std::string strError(int err)
Definition compat.hxx:274
std::recursive_mutex RecursiveMutex
Definition Mutex.hxx:38