File indexing completed on 2024-05-19 13:30:29
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 }