File indexing completed on 2024-05-05 17:50:03

0001 /*
0002    Copyright (C) 2013 Andreas Hartmetz <ahartmetz@gmail.com>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LGPL.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017    Boston, MA 02110-1301, USA.
0018 
0019    Alternatively, this file is available under the Mozilla Public License
0020    Version 1.1.  You may obtain a copy of the License at
0021    http://www.mozilla.org/MPL/
0022 */
0023 
0024 #include "epolleventpoller.h"
0025 
0026 #include "eventdispatcher_p.h"
0027 #include "iioeventlistener.h"
0028 
0029 #include <sys/epoll.h>
0030 #include <fcntl.h>
0031 #include <unistd.h>
0032 
0033 #include <cassert>
0034 #include <cstdio>
0035 
0036 EpollEventPoller::EpollEventPoller(EventDispatcher *dispatcher)
0037    : IEventPoller(dispatcher),
0038      m_epollFd(epoll_create(10))
0039 {
0040     // set up a pipe that can interrupt the polling from another thread
0041     // (we could also use the Linux-only eventfd() - pipes are at least portable to epoll-like mechanisms)
0042     pipe2(m_interruptPipe, O_NONBLOCK);
0043 
0044     struct epoll_event epevt;
0045     epevt.events = EPOLLIN;
0046     epevt.data.u64 = 0; // clear high bits in the union
0047     epevt.data.fd = m_interruptPipe[0];
0048     epoll_ctl(m_epollFd, EPOLL_CTL_ADD, m_interruptPipe[0], &epevt);
0049 }
0050 
0051 EpollEventPoller::~EpollEventPoller()
0052 {
0053     close(m_interruptPipe[0]);
0054     close(m_interruptPipe[1]);
0055     close(m_epollFd);
0056 }
0057 
0058 IEventPoller::InterruptAction EpollEventPoller::poll(int timeout)
0059 {
0060     IEventPoller::InterruptAction ret = IEventPoller::NoInterrupt;
0061 
0062     static const int maxEvPerPoll = 8;
0063     struct epoll_event results[maxEvPerPoll];
0064     int nresults = epoll_wait(m_epollFd, results, maxEvPerPoll, timeout);
0065     if (nresults < 0) {
0066         // error?
0067         return ret;
0068     }
0069 
0070     for (int i = 0; i < nresults; i++) {
0071         struct epoll_event *evt = results + i;
0072         // Check the same notification conditions as select: a client can call read() or write() without
0073         // blocking if the socket was closed in some way.
0074         if (evt->events & (EPOLLIN | EPOLLERR | EPOLLHUP)) {
0075             if (evt->data.fd != m_interruptPipe[0]) {
0076                 EventDispatcherPrivate::get(m_dispatcher)->notifyListenerForIo(evt->data.fd, IO::RW::Read);
0077             } else {
0078                 // interrupt; read bytes from pipe to clear buffers and get the interrupt type
0079                 ret = IEventPoller::ProcessAuxEvents;
0080                 char buf;
0081                 while (read(m_interruptPipe[0], &buf, 1) > 0) {
0082                     if (buf == 'S') {
0083                         ret = IEventPoller::Stop;
0084                     }
0085                 }
0086                 // ### discarding the rest of the events
0087                 // this works in our currently only use case, interrupting poll once to reap a thread
0088                 if (ret == IEventPoller::Stop) {
0089                     return ret;
0090                 }
0091             }
0092         }
0093         if (evt->events & (EPOLLOUT | EPOLLERR | EPOLLHUP)) {
0094             EventDispatcherPrivate::get(m_dispatcher)->notifyListenerForIo(evt->data.fd, IO::RW::Write);
0095         }
0096     }
0097     return ret;
0098 }
0099 
0100 void EpollEventPoller::interrupt(IEventPoller::InterruptAction action)
0101 {
0102     assert(action == IEventPoller::ProcessAuxEvents || action == IEventPoller::Stop);
0103 
0104     // write a byte to the write end so the poll waiting on the read end returns
0105     char buf = (action == IEventPoller::Stop) ? 'S' : 'N';
0106     write(m_interruptPipe[1], &buf, 1);
0107 }
0108 
0109 static uint32_t epeventsFromIoRw(uint32 ioRw)
0110 {
0111     return ((ioRw & uint32(IO::RW::Read)) ? uint32(EPOLLIN) : 0) |
0112            ((ioRw & uint32(IO::RW::Write)) ? uint32(EPOLLOUT) : 0);
0113 }
0114 
0115 void EpollEventPoller::addFileDescriptor(FileDescriptor fd, uint32 ioRw)
0116 {
0117     struct epoll_event epevt;
0118     epevt.events = epeventsFromIoRw(ioRw);
0119     epevt.data.u64 = 0; // clear high bits in the union
0120     epevt.data.fd = fd;
0121     epoll_ctl(m_epollFd, EPOLL_CTL_ADD, fd, &epevt);
0122 }
0123 
0124 void EpollEventPoller::removeFileDescriptor(FileDescriptor fd)
0125 {
0126     // Connection should call us *before* resetting its fd on failure
0127     assert(fd >= 0);
0128     struct epoll_event epevt; // required in Linux < 2.6.9 even though it's ignored
0129     epoll_ctl(m_epollFd, EPOLL_CTL_DEL, fd, &epevt);
0130 }
0131 
0132 void EpollEventPoller::setReadWriteInterest(FileDescriptor fd, uint32 ioRw)
0133 {
0134     if (!fd) {
0135         return;
0136     }
0137     struct epoll_event epevt;
0138     epevt.events = epeventsFromIoRw(ioRw);
0139     epevt.data.u64 = 0; // clear high bits in the union
0140     epevt.data.fd = fd;
0141     epoll_ctl(m_epollFd, EPOLL_CTL_MOD, fd, &epevt);
0142 }