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

0001 /*
0002    Copyright (C) 2015 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 "selecteventpoller_unix.h"
0025 
0026 #include "eventdispatcher_p.h"
0027 #include "iioeventlistener.h"
0028 
0029 #include <fcntl.h>
0030 #include <unistd.h>
0031 
0032 #include <cassert>
0033 #include <cstdio>
0034 
0035 
0036 SelectEventPoller::SelectEventPoller(EventDispatcher *dispatcher)
0037    : IEventPoller(dispatcher)
0038 {
0039     pipe2(m_interruptPipe, O_NONBLOCK);
0040     resetFdSets();
0041 }
0042 
0043 SelectEventPoller::~SelectEventPoller()
0044 {
0045     close(m_interruptPipe[0]);
0046     close(m_interruptPipe[1]);
0047 }
0048 
0049 IEventPoller::InterruptAction SelectEventPoller::poll(int timeout)
0050 {
0051     IEventPoller::InterruptAction ret = IEventPoller::NoInterrupt;
0052 
0053     resetFdSets();
0054 
0055     int nfds = m_interruptPipe[0];
0056 
0057     // set up the interruption listener
0058     FD_SET(m_interruptPipe[0], &m_readSet);
0059 
0060     for (const auto &fdRw : m_fds) {
0061         if (fdRw.second & uint32(IO::RW::Read)) {
0062             nfds = std::max(nfds, fdRw.first);
0063             FD_SET(fdRw.first, &m_readSet);
0064         }
0065         if (fdRw.second & uint32(IO::RW::Write)) {
0066             nfds = std::max(nfds, fdRw.first);
0067             FD_SET(fdRw.first, &m_writeSet);
0068         }
0069     }
0070 
0071     struct timeval tv;
0072     struct timeval *tvPointer = nullptr;
0073     if (timeout >= 0) {
0074         tv.tv_sec = timeout / 1000;
0075         tv.tv_usec = (timeout % 1000) * 1000;
0076         tvPointer = &tv;
0077     }
0078 
0079     // select!
0080     nfds += 1; // see documentation of select()...
0081     int numEvents = select(nfds, &m_readSet, &m_writeSet, nullptr, tvPointer);
0082 
0083     // check for interruption
0084     if (FD_ISSET(m_interruptPipe[0], &m_readSet)) {
0085         // interrupt; read bytes from pipe to clear buffers and get the interrupt type
0086         ret = IEventPoller::ProcessAuxEvents;
0087         char buf;
0088         while (read(m_interruptPipe[0], &buf, 1) > 0) {
0089             if (buf == 'S') {
0090                 ret = IEventPoller::Stop;
0091             }
0092         }
0093     }
0094 
0095     if (ret == IEventPoller::Stop) {
0096         // ### discarding the rest of the events, to avoid touching "dead" data while shutting down
0097         numEvents = 0;
0098     }
0099 
0100     // dispatch reads and writes
0101     if (numEvents < 0) {
0102         // TODO error handling
0103     } else {
0104         EventDispatcherPrivate* const edpriv = EventDispatcherPrivate::get(m_dispatcher);
0105         for (int i = 0; i < nfds && numEvents > 0; i++) {
0106             if (FD_ISSET(i, &m_readSet)) {
0107                 edpriv->notifyListenerForIo(i, IO::RW::Read);
0108                 numEvents--;
0109             }
0110             if (FD_ISSET(i, &m_writeSet)) {
0111                 edpriv->notifyListenerForIo(i, IO::RW::Write);
0112                 numEvents--;
0113             }
0114         }
0115     }
0116 
0117     return ret;
0118 }
0119 
0120 void SelectEventPoller::resetFdSets()
0121 {
0122     FD_ZERO(&m_readSet);
0123     FD_ZERO(&m_writeSet);
0124 }
0125 
0126 void SelectEventPoller::interrupt(IEventPoller::InterruptAction action)
0127 {
0128     assert(action == IEventPoller::ProcessAuxEvents || action == IEventPoller::Stop);
0129     // write a byte to the write end so the poll waiting on the read end returns
0130     char buf = (action == IEventPoller::Stop) ? 'S' : 'N';
0131     write(m_interruptPipe[1], &buf, 1);
0132 }
0133 
0134 void SelectEventPoller::addFileDescriptor(FileDescriptor fd, uint32 ioRw)
0135 {
0136     // The main select specific part of registration is in setReadWriteInterest().
0137     // Here we just check fd limits.
0138     if (fd >= FD_SETSIZE) {
0139         // TODO error...
0140         return;
0141     }
0142 
0143     m_fds.emplace(fd, ioRw);
0144 }
0145 
0146 void SelectEventPoller::removeFileDescriptor(FileDescriptor fd)
0147 {
0148     m_fds.erase(fd);
0149 }
0150 
0151 void SelectEventPoller::setReadWriteInterest(FileDescriptor fd, uint32 ioRw)
0152 {
0153     m_fds.at(fd) = ioRw;
0154 }