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_win32.h"
0025 
0026 #include "eventdispatcher_p.h"
0027 #include "iioeventlistener.h"
0028 
0029 #include <iostream>
0030 #include <thread>
0031 
0032 #include <cassert>
0033 #include <cstdio>
0034 
0035 static void socketpair(FileDescriptor fds[2])
0036 {
0037     struct sockaddr_in inaddr;
0038     struct sockaddr addr;
0039     FileDescriptor listener = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
0040     memset(&inaddr, 0, sizeof(inaddr));
0041     memset(&addr, 0, sizeof(addr));
0042 
0043     inaddr.sin_family = AF_INET;
0044     inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
0045     inaddr.sin_port = 0;
0046 
0047     int trueConstant = 1;
0048     setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
0049                reinterpret_cast<char* >(&trueConstant), sizeof(trueConstant));
0050     bind(listener, reinterpret_cast<struct sockaddr *>(&inaddr), sizeof(inaddr));
0051     listen(listener, 1);
0052 
0053     int len = sizeof(inaddr);
0054     getsockname(listener, &addr, &len);
0055     fds[0] = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
0056     connect(fds[0], &addr, len);
0057 
0058     fds[1] = accept(listener, nullptr, nullptr);
0059 
0060     closesocket(listener);
0061 }
0062 
0063 SelectEventPoller::SelectEventPoller(EventDispatcher *dispatcher)
0064    : IEventPoller(dispatcher)
0065 {
0066     WSAData wsadata;
0067     // IPv6 requires Winsock v2.0 or better (but we're not using IPv6 - yet!)
0068     if (WSAStartup(MAKEWORD(2, 0), &wsadata) != 0) {
0069         return;
0070     }
0071 
0072     socketpair(m_interruptSocket);
0073     unsigned long value = 1; // 0 blocking, != 0 non-blocking
0074     ioctlsocket(m_interruptSocket[0], FIONBIO, &value);
0075 }
0076 
0077 SelectEventPoller::~SelectEventPoller()
0078 {
0079     closesocket(m_interruptSocket[0]);
0080     closesocket(m_interruptSocket[1]);
0081 }
0082 
0083 IEventPoller::InterruptAction SelectEventPoller::poll(int timeout)
0084 {
0085     IEventPoller::InterruptAction ret = IEventPoller::NoInterrupt;
0086 
0087     resetFdSets();
0088 
0089     m_readSet.fd_array[m_readSet.fd_count++] = m_interruptSocket[0];
0090 
0091     // ### doing FD_SET "manually", avoiding a scan of the whole list for each set action - there is
0092     //     no danger of duplicates because our input is a set which already guarantees uniqueness.
0093     for (auto &fdRw : m_fds) {
0094         if (fdRw.second & uint32(IO::RW::Read)) {
0095             // FD_SET(fdRw.first, &m_readSet);
0096             if (m_readSet.fd_count < FD_SETSIZE) {
0097                 m_readSet.fd_array[m_readSet.fd_count++] = fdRw.first;
0098             }
0099         }
0100         if (fdRw.second & uint32(IO::RW::Write)) {
0101             // FD_SET(fdRw.first, &m_writeSet);
0102             if (m_writeSet.fd_count < FD_SETSIZE) {
0103                 m_writeSet.fd_array[m_writeSet.fd_count++] = fdRw.first;
0104             }
0105         }
0106     }
0107 
0108     struct timeval tv;
0109     struct timeval *tvPointer = nullptr;
0110     if (timeout >= 0) {
0111         tv.tv_sec = timeout / 1000;
0112         tv.tv_usec = (timeout % 1000) * 1000;
0113         tvPointer = &tv;
0114     }
0115 
0116     // select!
0117     int numEvents = select(0, &m_readSet, &m_writeSet, nullptr, tvPointer);
0118     if (numEvents == -1) {
0119         std::cerr << "select() failed with error code " << WSAGetLastError() << std::endl;
0120     }
0121 
0122     // check for interruption
0123     if (FD_ISSET(m_interruptSocket[0], &m_readSet)) {
0124         // interrupt; read bytes from pipe to clear buffers and get the interrupt type
0125         ret = IEventPoller::ProcessAuxEvents;
0126         char buf;
0127         while (recv(m_interruptSocket[0], &buf, 1, 0) > 0) {
0128             if (buf == 'S') {
0129                 ret = IEventPoller::Stop;
0130             }
0131         }
0132     }
0133 
0134     // dispatch reads and writes
0135     if (numEvents < 0) {
0136         // TODO error handling ?
0137     }
0138 
0139     // This being Windows-specfic code, and with Windows's famous binary compatibility, we may
0140     // as well exploit that the Windows fd_set struct allows for relatively efficient querying
0141     // if you just iterate over its internal list, instead of searching the list for each file
0142     // descriptor like with FD_ISSET.
0143     // numEvents -= m_readSet.fd_count + m_writeSet.fd_count;
0144     for (uint i = 0; i < m_readSet.fd_count; i++) {
0145         EventDispatcherPrivate::get(m_dispatcher)
0146             ->notifyListenerForIo(m_readSet.fd_array[i], IO::RW::Read);
0147     }
0148     for (uint i = 0; i < m_writeSet.fd_count; i++) {
0149         EventDispatcherPrivate::get(m_dispatcher)
0150             ->notifyListenerForIo(m_writeSet.fd_array[i], IO::RW::Write);
0151     }
0152 
0153     return ret;
0154 }
0155 
0156 void SelectEventPoller::resetFdSets()
0157 {
0158     FD_ZERO(&m_readSet);
0159     FD_ZERO(&m_writeSet);
0160 }
0161 
0162 void SelectEventPoller::interrupt(IEventPoller::InterruptAction action)
0163 {
0164     assert(action == IEventPoller::ProcessAuxEvents || action == IEventPoller::Stop);
0165     // write a byte to the write end so the poll waiting on the read end returns
0166     char buf = (action == IEventPoller::Stop) ? 'S' : 'N';
0167     send(m_interruptSocket[1], &buf, 1, 0);
0168 }
0169 
0170 void SelectEventPoller::addFileDescriptor(FileDescriptor fd, uint32 ioRw)
0171 {
0172     // The main select specific part of registration is in setReadWriteInterest().
0173     // Here we just check fd limits.
0174     if (m_fds.size() + 1 >= FD_SETSIZE) {
0175         std::cerr << "SelectEventPoller::addIoEventListener() failed: FD_SETSIZE too small.\n";
0176         // TODO indicate the error somehow?
0177         return;
0178     }
0179 
0180     m_fds.emplace(fd, ioRw);
0181 }
0182 
0183 void SelectEventPoller::removeFileDescriptor(FileDescriptor fd)
0184 {
0185     m_fds.erase(fd);
0186 }
0187 
0188 void SelectEventPoller::setReadWriteInterest(FileDescriptor fd, uint32 ioRw)
0189 {
0190     m_fds.at(fd) = ioRw;
0191 }