File indexing completed on 2024-05-19 09:36:34
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 }