|
@@ -1,896 +1,897 @@
|
|
|
-#include "sys/epoll.h"
|
|
|
-#include "msafd.h"
|
|
|
-
|
|
|
-#include <unordered_map>
|
|
|
-#include <unordered_set>
|
|
|
-#include <cassert>
|
|
|
-#include <mutex>
|
|
|
-#include <iostream>
|
|
|
-
|
|
|
-using namespace std;
|
|
|
-
|
|
|
-#define ARRAY_COUNT(a) (sizeof(a) / (sizeof((a)[0])))
|
|
|
-
|
|
|
-#define EPOLL__EVENT_MASK 0xffff
|
|
|
-
|
|
|
-class epoll_port_data_t;
|
|
|
-struct epoll_sock_data_t;
|
|
|
-struct epoll_op_t;
|
|
|
-
|
|
|
-/* State associated with a AFD_POLL request. */
|
|
|
-struct epoll_op_t
|
|
|
-{
|
|
|
- OVERLAPPED _overlapped;
|
|
|
- AFD_POLL_INFO _poll_info;
|
|
|
- uint32_t _generation;
|
|
|
- epoll_sock_data_t *_sock_data;
|
|
|
-
|
|
|
- epoll_op_t(epoll_sock_data_t *sock_data, uint32_t afd_events);
|
|
|
- ~epoll_op_t();
|
|
|
-};
|
|
|
-
|
|
|
-/* State associated with a socket that is registered to the epoll port. */
|
|
|
-struct epoll_sock_data_t
|
|
|
-{
|
|
|
- SOCKET _sock;
|
|
|
- SOCKET _base_sock;
|
|
|
- SOCKET _peer_sock;
|
|
|
- uint32_t _registered_events;
|
|
|
- uint32_t _op_generation;
|
|
|
- uint64_t _user_data;
|
|
|
- int32_t _op_count;
|
|
|
-
|
|
|
- epoll_sock_data_t(SOCKET sock, SOCKET base_sock, SOCKET peer_sock);
|
|
|
-
|
|
|
- int submit();
|
|
|
-};
|
|
|
-
|
|
|
-/* State associated with a epoll handle. */
|
|
|
-class epoll_port_data_t
|
|
|
-{
|
|
|
-protected:
|
|
|
- HANDLE _iocp;
|
|
|
- SOCKET _peer_sockets[ARRAY_COUNT(AFD_PROVIDER_IDS)];
|
|
|
- unordered_map<int, epoll_sock_data_t *> _sock_data_tree;
|
|
|
- unordered_set<epoll_sock_data_t *> _attn_list;
|
|
|
- std::mutex _mutex;
|
|
|
-
|
|
|
-public:
|
|
|
- epoll_port_data_t();
|
|
|
- HANDLE getHandle() { return _iocp; }
|
|
|
- int epoll_add(SOCKET sock, struct epoll_event *ev);
|
|
|
- int epoll_mod(SOCKET sock, struct epoll_event *ev);
|
|
|
- int epoll_del(SOCKET sock, struct epoll_event *ev);
|
|
|
- int epoll_close();
|
|
|
- int epoll_wait(OVERLAPPED_ENTRY *entries, ULONG count, struct epoll_event *events);
|
|
|
- void submit()
|
|
|
- {
|
|
|
- std::lock_guard<std::mutex> lck (_mutex);
|
|
|
-
|
|
|
- for(auto entry : _attn_list)
|
|
|
- {
|
|
|
- if(entry->_op_count == 0) {
|
|
|
- // printf("before epoll_wait submit: %d\n", entry->_op_count);
|
|
|
- //no focus any event, then submit keep attn, otherwise cause op leak
|
|
|
- entry->submit();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- _attn_list.clear();
|
|
|
- }
|
|
|
-protected:
|
|
|
- bool find(epoll_sock_data_t *sock_data)
|
|
|
- {
|
|
|
- auto it = _sock_data_tree.find(sock_data->_sock);
|
|
|
- if (it != _sock_data_tree.end()) {
|
|
|
- return it->second == sock_data;
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- void remove(epoll_sock_data_t *sock_data)
|
|
|
- {
|
|
|
- _sock_data_tree.erase(sock_data->_sock);
|
|
|
- _attn_list.erase(sock_data);
|
|
|
- }
|
|
|
- void add(epoll_sock_data_t *sock_data)
|
|
|
- {
|
|
|
- _sock_data_tree[sock_data->_sock] = sock_data;
|
|
|
- }
|
|
|
- void remove_attn(epoll_sock_data_t *sock_data)
|
|
|
- {
|
|
|
- _attn_list.erase(sock_data);
|
|
|
- }
|
|
|
- void add_attn(epoll_sock_data_t *sock_data)
|
|
|
- {
|
|
|
- _attn_list.insert(sock_data);
|
|
|
- }
|
|
|
-
|
|
|
- epoll_sock_data_t *get(SOCKET sock)
|
|
|
- {
|
|
|
- auto it = _sock_data_tree.find(sock);
|
|
|
- if (it == _sock_data_tree.end())
|
|
|
- {
|
|
|
- /* Socket has not been registered with epoll instance. */
|
|
|
- SetLastError(ERROR_NOT_FOUND);
|
|
|
- return NULL;
|
|
|
- }
|
|
|
- return it->second;
|
|
|
- }
|
|
|
-
|
|
|
- SOCKET create_peer_socket(WSAPROTOCOL_INFOW *protocol_info);
|
|
|
- SOCKET get_peer_socket(WSAPROTOCOL_INFOW *protocol_info);
|
|
|
-
|
|
|
-};
|
|
|
-
|
|
|
-static int epoll__initialize();
|
|
|
-static int epoll__afd_poll(SOCKET socket, AFD_POLL_INFO *info, OVERLAPPED *overlapped);
|
|
|
-static int epoll__ntstatus_to_winsock_error(NTSTATUS status);
|
|
|
-
|
|
|
-static int epoll__initialized = 0;
|
|
|
-static PNTDEVICEIOCONTROLFILE pNtDeviceIoControlFile;
|
|
|
-
|
|
|
-epoll_op_t::epoll_op_t(epoll_sock_data_t *sock_data, uint32_t afd_events)
|
|
|
-{
|
|
|
- _generation = sock_data->_op_generation + 1;
|
|
|
-
|
|
|
- memset(&_overlapped, 0, sizeof _overlapped);
|
|
|
- _poll_info.Exclusive = FALSE;
|
|
|
- _poll_info.NumberOfHandles = 1;
|
|
|
- _poll_info.Timeout.QuadPart = INT64_MAX;
|
|
|
- _poll_info.Handles[0].Handle = (HANDLE)sock_data->_base_sock;
|
|
|
- _poll_info.Handles[0].Status = 0;
|
|
|
- _poll_info.Handles[0].Events = afd_events;
|
|
|
-
|
|
|
- _sock_data = sock_data;
|
|
|
- _sock_data->_op_count++;
|
|
|
-
|
|
|
- // printf("submit new: %p, %d\n", this, _sock_data->_op_count);
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
-epoll_op_t::~epoll_op_t()
|
|
|
-{
|
|
|
- // printf("~epoll_op_t: %p, %d\n", this, _sock_data->_op_count );
|
|
|
-
|
|
|
- _sock_data->_op_count--;
|
|
|
- assert(_sock_data->_op_count >= 0);
|
|
|
-}
|
|
|
-
|
|
|
-epoll_sock_data_t::epoll_sock_data_t(SOCKET sock, SOCKET base_sock, SOCKET peer_sock)
|
|
|
-{
|
|
|
- _sock = sock;
|
|
|
- _base_sock = base_sock;
|
|
|
- _op_generation = 0;
|
|
|
- _registered_events = 0;
|
|
|
- _user_data = 0;
|
|
|
- _peer_sock = peer_sock;
|
|
|
- _op_count = 0;
|
|
|
-}
|
|
|
-
|
|
|
-int epoll_sock_data_t::submit()
|
|
|
-{
|
|
|
- DWORD afd_events = AFD_POLL_ABORT | AFD_POLL_DISCONNECT | AFD_POLL_CONNECT_FAIL | AFD_POLL_LOCAL_CLOSE;
|
|
|
-
|
|
|
- if (_registered_events & (EPOLLIN | EPOLLRDNORM))
|
|
|
- {
|
|
|
- afd_events |= AFD_POLL_RECEIVE | AFD_POLL_ACCEPT ;
|
|
|
- }
|
|
|
- if (_registered_events & (EPOLLIN | EPOLLRDBAND))
|
|
|
- {
|
|
|
- afd_events |= AFD_POLL_RECEIVE_EXPEDITED;
|
|
|
- }
|
|
|
- if (_registered_events & (EPOLLOUT | EPOLLWRNORM | EPOLLRDBAND))
|
|
|
- {
|
|
|
- afd_events |= AFD_POLL_SEND | AFD_POLL_CONNECT;
|
|
|
- }
|
|
|
-
|
|
|
- epoll_op_t* op = new epoll_op_t(this, afd_events);
|
|
|
-
|
|
|
- DWORD result = epoll__afd_poll(_peer_sock, &op->_poll_info, &op->_overlapped);
|
|
|
- if (result != 0)
|
|
|
- {
|
|
|
- DWORD error = WSAGetLastError();
|
|
|
- if (error != WSA_IO_PENDING)
|
|
|
- {
|
|
|
- /* If this happens an error happened and no overlapped operation was */
|
|
|
- /* started. */
|
|
|
- delete op;
|
|
|
- op = NULL;
|
|
|
- return -1;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- _op_generation = op->_generation;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-//////////////////////////////////////////////////////////////////////
|
|
|
-epoll_port_data_t::epoll_port_data_t()
|
|
|
-{
|
|
|
- _iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
|
|
- if (_iocp == INVALID_HANDLE_VALUE)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- memset(&_peer_sockets, 0, sizeof _peer_sockets);
|
|
|
-}
|
|
|
-
|
|
|
-SOCKET epoll_port_data_t::get_peer_socket(WSAPROTOCOL_INFOW *protocol_info)
|
|
|
-{
|
|
|
- int index = -1;
|
|
|
-
|
|
|
- for (int i = 0; i < ARRAY_COUNT(AFD_PROVIDER_IDS); i++)
|
|
|
- {
|
|
|
- if (memcmp((void *)&protocol_info->ProviderId, (void *)&AFD_PROVIDER_IDS[i], sizeof protocol_info->ProviderId) == 0)
|
|
|
- {
|
|
|
- index = i;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* Check if the protocol uses an msafd socket. */
|
|
|
- if (index < 0)
|
|
|
- {
|
|
|
- SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
- return INVALID_SOCKET;
|
|
|
- }
|
|
|
-
|
|
|
- /* If we didn't (try) to create a peer socket yet, try to make one. Don't */
|
|
|
- /* try again if the peer socket creation failed earlier for the same */
|
|
|
- /* protocol. */
|
|
|
- SOCKET peer_socket = _peer_sockets[index];
|
|
|
- if (peer_socket == 0)
|
|
|
- {
|
|
|
- peer_socket = create_peer_socket(protocol_info);
|
|
|
- _peer_sockets[index] = peer_socket;
|
|
|
- }
|
|
|
-
|
|
|
- return peer_socket;
|
|
|
-}
|
|
|
-
|
|
|
-SOCKET epoll_port_data_t::create_peer_socket(WSAPROTOCOL_INFOW *protocol_info)
|
|
|
-{
|
|
|
- SOCKET sock = WSASocketW(protocol_info->iAddressFamily, protocol_info->iSocketType, protocol_info->iProtocol, protocol_info, 0, WSA_FLAG_OVERLAPPED);
|
|
|
- if (sock == INVALID_SOCKET)
|
|
|
- {
|
|
|
- return INVALID_SOCKET;
|
|
|
- }
|
|
|
-
|
|
|
- if (!SetHandleInformation((HANDLE)sock, HANDLE_FLAG_INHERIT, 0))
|
|
|
- {
|
|
|
- goto error;
|
|
|
- };
|
|
|
-
|
|
|
- if (CreateIoCompletionPort((HANDLE)sock, _iocp, 0, 0) == NULL)
|
|
|
- {
|
|
|
- goto error;
|
|
|
- }
|
|
|
-
|
|
|
- return sock;
|
|
|
-
|
|
|
-error:
|
|
|
- closesocket(sock);
|
|
|
- return INVALID_SOCKET;
|
|
|
-}
|
|
|
-
|
|
|
-int epoll_port_data_t::epoll_add(SOCKET sock, struct epoll_event *ev)
|
|
|
-{
|
|
|
- std::lock_guard<std::mutex> lck (_mutex);
|
|
|
-
|
|
|
- epoll_sock_data_t *sock_data = get(sock);
|
|
|
- if(sock_data != NULL)
|
|
|
- {
|
|
|
- SetLastError(ERROR_ALREADY_EXISTS);
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- WSAPROTOCOL_INFOW protocol_info;
|
|
|
- DWORD bytes;
|
|
|
-
|
|
|
- /* Try to obtain a base handle for the socket, so we can bypass LSPs */
|
|
|
- /* that get in the way if we want to talk to the kernel directly. If */
|
|
|
- /* it fails we try if we work with the original socket. Note that on */
|
|
|
- /* windows XP/2k3 this will always fail since they don't support the */
|
|
|
- /* SIO_BASE_HANDLE ioctl. */
|
|
|
- SOCKET base_sock = sock;
|
|
|
- WSAIoctl(sock, SIO_BASE_HANDLE, NULL, 0, &base_sock, sizeof base_sock, &bytes, NULL, NULL);
|
|
|
-
|
|
|
- /* Obtain protocol information about the socket. */
|
|
|
- int len = sizeof protocol_info;
|
|
|
- if (getsockopt(base_sock, SOL_SOCKET, SO_PROTOCOL_INFOW, (char *)&protocol_info, &len) != 0)
|
|
|
- {
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- SOCKET peer_sock = get_peer_socket(&protocol_info);
|
|
|
- if (peer_sock == INVALID_SOCKET)
|
|
|
- {
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- sock_data = new epoll_sock_data_t(sock, base_sock, peer_sock);
|
|
|
- if (sock_data == NULL)
|
|
|
- {
|
|
|
- SetLastError(ERROR_OUTOFMEMORY);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- // printf("add new, %d\n", sock_data->_op_count);
|
|
|
-
|
|
|
- add(sock_data);
|
|
|
-
|
|
|
- sock_data->_registered_events = ev->events | EPOLLERR | EPOLLHUP;
|
|
|
- sock_data->_user_data = ev->data.u64;
|
|
|
-
|
|
|
- return sock_data->submit();
|
|
|
-}
|
|
|
-
|
|
|
-int epoll_port_data_t::epoll_mod(SOCKET sock, struct epoll_event *ev)
|
|
|
-{
|
|
|
- {
|
|
|
- std::lock_guard<std::mutex> lck (_mutex);
|
|
|
-
|
|
|
- epoll_sock_data_t *sock_data = get(sock);
|
|
|
-
|
|
|
- if(sock_data != NULL)
|
|
|
- {
|
|
|
- sock_data->_registered_events = ev->events | EPOLLERR | EPOLLHUP;
|
|
|
- sock_data->_user_data = ev->data.u64;
|
|
|
- return sock_data->submit();
|
|
|
- }
|
|
|
- }
|
|
|
- // LOG_CONSOLE_DEBUG << endl;
|
|
|
- return epoll_add(sock, ev);
|
|
|
-}
|
|
|
-
|
|
|
-int epoll_port_data_t::epoll_del(SOCKET sock, struct epoll_event *ev)
|
|
|
-{
|
|
|
- std::lock_guard<std::mutex> lck (_mutex);
|
|
|
-
|
|
|
- // printf("del new\n");
|
|
|
-
|
|
|
- epoll_sock_data_t *sock_data = get(sock);
|
|
|
- if(sock_data == NULL)
|
|
|
- {
|
|
|
- SetLastError(ERROR_NOT_FOUND);
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- /* Remove from attention list. */
|
|
|
- remove(sock_data);
|
|
|
-
|
|
|
- return 0;
|
|
|
- // 以下代码不能有, 否则短链接模式下句柄复用会有问题!!!!
|
|
|
- // sock_data->_registered_events = ev->events | EPOLLERR | EPOLLHUP;
|
|
|
- // sock_data->_user_data = ev->data.u64;
|
|
|
-
|
|
|
- // return sock_data->submit();
|
|
|
-}
|
|
|
-
|
|
|
-int epoll_port_data_t::epoll_close()
|
|
|
-{
|
|
|
- std::lock_guard<std::mutex> lck (_mutex);
|
|
|
-
|
|
|
- /* Close all peer sockets. This will make all pending ops return. */
|
|
|
- for (int i = 0; i < ARRAY_COUNT(_peer_sockets); i++)
|
|
|
- {
|
|
|
- SOCKET peer_sock = _peer_sockets[i];
|
|
|
- if (peer_sock != 0 && peer_sock != INVALID_SOCKET)
|
|
|
- {
|
|
|
- closesocket(peer_sock);
|
|
|
-
|
|
|
- _peer_sockets[i] = 0;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* Remove all entries from the socket_state tree. */
|
|
|
- for(auto entry : _sock_data_tree)
|
|
|
- {
|
|
|
- entry.second->submit();
|
|
|
- }
|
|
|
-
|
|
|
- while(!_sock_data_tree.empty())
|
|
|
- {
|
|
|
- OVERLAPPED_ENTRY entries[64];
|
|
|
- ULONG count;
|
|
|
-
|
|
|
- DWORD result = GetQueuedCompletionStatusEx(_iocp, entries, ARRAY_COUNT(entries), &count, 0, FALSE);
|
|
|
- if (!result)
|
|
|
- {
|
|
|
- DWORD error = GetLastError();
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- for(ULONG i = 0; i < count; i++)
|
|
|
- {
|
|
|
- epoll_op_t *op = CONTAINING_RECORD(entries[i].lpOverlapped, epoll_op_t, _overlapped);
|
|
|
- epoll_sock_data_t *sock_data = op->_sock_data;
|
|
|
-
|
|
|
- if(!find(sock_data))
|
|
|
- {
|
|
|
- remove(sock_data);
|
|
|
- }
|
|
|
-
|
|
|
- delete op;
|
|
|
- if(sock_data->_op_count == 0)
|
|
|
- delete sock_data;
|
|
|
- }
|
|
|
-
|
|
|
- if(count == 0)
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- /* Close the I/O completion port. */
|
|
|
- CloseHandle(_iocp);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-int epoll_port_data_t::epoll_wait(OVERLAPPED_ENTRY *entries, ULONG count, struct epoll_event *events)
|
|
|
-{
|
|
|
- std::lock_guard<std::mutex> lck (_mutex);
|
|
|
-
|
|
|
- int num_events = 0;
|
|
|
-
|
|
|
- /* Successfully dequeued overlappeds. */
|
|
|
- for (ULONG i = 0; i < count; i++)
|
|
|
- {
|
|
|
- epoll_op_t *op = CONTAINING_RECORD(entries[i].lpOverlapped, epoll_op_t, _overlapped);
|
|
|
- epoll_sock_data_t *sock_data = op->_sock_data;
|
|
|
-
|
|
|
- if (op->_generation < sock_data->_op_generation)
|
|
|
- {
|
|
|
- // cout << "op superseded" << endl;
|
|
|
- delete op;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /* Dequeued the most recent op. Reset generation and submitted_events. */
|
|
|
- sock_data->_op_generation = 0;
|
|
|
-
|
|
|
- /* Check if this op was associated with a socket that was removed */
|
|
|
- /* with EPOLL_CTL_DEL. */
|
|
|
- if(!find(sock_data))
|
|
|
- {
|
|
|
- delete op;
|
|
|
- if(sock_data->_op_count == 0)
|
|
|
- delete sock_data;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- /* Check for error. */
|
|
|
- if (!NT_SUCCESS(entries[i].lpOverlapped->Internal))
|
|
|
- {
|
|
|
- struct epoll_event *ev = events + num_events;
|
|
|
- ev->data.u64 = sock_data->_user_data;
|
|
|
- ev->events = EPOLLERR;
|
|
|
- num_events++;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- DWORD afd_events;
|
|
|
-
|
|
|
- if (op->_poll_info.NumberOfHandles == 0)
|
|
|
- {
|
|
|
- /* NumberOfHandles can be zero if this poll operation was canceled */
|
|
|
- /* due to a more recent exclusive poll operation. */
|
|
|
- afd_events = 0;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- afd_events = op->_poll_info.Handles[0].Events;
|
|
|
- }
|
|
|
-
|
|
|
- // cout << "wait ok, handles:" << op->_poll_info.NumberOfHandles << ", events:" << afd_events << endl;
|
|
|
-
|
|
|
- /* Check for a closed socket. */
|
|
|
- if (afd_events & AFD_POLL_LOCAL_CLOSE)
|
|
|
- {
|
|
|
- remove(sock_data);
|
|
|
- delete op;
|
|
|
- if(sock_data->_op_count == 0)
|
|
|
- delete sock_data;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- delete op;
|
|
|
-
|
|
|
- // int registered_events = sock_data->_registered_events;
|
|
|
- int reported_events = 0;
|
|
|
- /* Convert afd events to epoll events. */
|
|
|
- if (afd_events & (AFD_POLL_RECEIVE | AFD_POLL_ACCEPT))
|
|
|
- {
|
|
|
- reported_events |= EPOLLIN;
|
|
|
- }
|
|
|
- if (afd_events & AFD_POLL_RECEIVE_EXPEDITED)
|
|
|
- {
|
|
|
- reported_events |= (EPOLLIN | EPOLLRDBAND);
|
|
|
- }
|
|
|
- if (afd_events & AFD_POLL_SEND)
|
|
|
- {
|
|
|
- reported_events |= (EPOLLOUT);
|
|
|
- }
|
|
|
- if ((afd_events & AFD_POLL_DISCONNECT) && !(afd_events & AFD_POLL_ABORT))
|
|
|
- {
|
|
|
- reported_events |= (EPOLLRDHUP | EPOLLIN | EPOLLRDNORM | EPOLLRDBAND);
|
|
|
- }
|
|
|
- if (afd_events & AFD_POLL_ABORT)
|
|
|
- {
|
|
|
- reported_events |= EPOLLHUP | EPOLLERR;
|
|
|
- }
|
|
|
- if (afd_events & AFD_POLL_CONNECT)
|
|
|
- {
|
|
|
- reported_events |= (EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND);
|
|
|
- }
|
|
|
- if (afd_events & AFD_POLL_CONNECT_FAIL)
|
|
|
- {
|
|
|
- reported_events |= EPOLLERR;
|
|
|
- }
|
|
|
-
|
|
|
- //keep attn
|
|
|
- sock_data->_registered_events &= ~EPOLLOUT;
|
|
|
- add_attn(sock_data);
|
|
|
-
|
|
|
- /* Don't report events that the user didn't specify. */
|
|
|
- // reported_events &= registered_events;
|
|
|
- // if(reported_events & EPOLLOUT)
|
|
|
- // cout << "reported_events EPOLLOUT:" << reported_events << endl;
|
|
|
-
|
|
|
- if (reported_events)
|
|
|
- {
|
|
|
- struct epoll_event *ev = events + num_events;
|
|
|
- ev->data.u64 = sock_data->_user_data;
|
|
|
- ev->events = reported_events;
|
|
|
- num_events++;
|
|
|
- }
|
|
|
- }
|
|
|
- return num_events;
|
|
|
-}
|
|
|
-
|
|
|
-///////////////////////////////////////////////////////////////////////////////////
|
|
|
-epoll_t epoll_create(int size)
|
|
|
-{
|
|
|
- /* If necessary, do global initialization first. This is totally not */
|
|
|
- /* thread-safe at the moment. */
|
|
|
- if (!epoll__initialized)
|
|
|
- {
|
|
|
- if (epoll__initialize() < 0)
|
|
|
- return NULL;
|
|
|
- epoll__initialized = 1;
|
|
|
- }
|
|
|
-
|
|
|
- epoll_port_data_t *port_data = new epoll_port_data_t();
|
|
|
- if (port_data == NULL)
|
|
|
- {
|
|
|
- SetLastError(ERROR_OUTOFMEMORY);
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- return (epoll_t)port_data;
|
|
|
-}
|
|
|
-
|
|
|
-int epoll_ctl(epoll_t port_handle, int op, SOCKET sock, struct epoll_event *ev)
|
|
|
-{
|
|
|
- epoll_port_data_t *port_data = (epoll_port_data_t *)port_handle;
|
|
|
-
|
|
|
- switch (op)
|
|
|
- {
|
|
|
- case EPOLL_CTL_ADD:
|
|
|
- {
|
|
|
- return port_data->epoll_add(sock, ev);
|
|
|
- }
|
|
|
- case EPOLL_CTL_MOD:
|
|
|
- {
|
|
|
- return port_data->epoll_mod(sock, ev);
|
|
|
- }
|
|
|
- case EPOLL_CTL_DEL:
|
|
|
- {
|
|
|
- return port_data->epoll_del(sock, ev);
|
|
|
- }
|
|
|
-
|
|
|
- default:
|
|
|
- WSASetLastError(WSAEINVAL);
|
|
|
- return -1;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-int epoll_wait(epoll_t port_handle, struct epoll_event *events, int maxevents, int timeout)
|
|
|
-{
|
|
|
- DWORD gqcs_timeout;
|
|
|
-
|
|
|
- /* Compute the timeout for GetQueuedCompletionStatus, and the wait end */
|
|
|
- /* time, if the user specified a timeout other than zero or infinite. */
|
|
|
- if (timeout >= 0)
|
|
|
- {
|
|
|
- gqcs_timeout = (DWORD)timeout;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- gqcs_timeout = INFINITE;
|
|
|
- }
|
|
|
-
|
|
|
- epoll_port_data_t *port_data = (epoll_port_data_t *)port_handle;
|
|
|
-
|
|
|
- port_data->submit();
|
|
|
-
|
|
|
- OVERLAPPED_ENTRY entries[64];
|
|
|
-
|
|
|
- /* Compute how much overlapped entries can be dequeued at most. */
|
|
|
- DWORD max_entries = min(ARRAY_COUNT(entries), maxevents);
|
|
|
- ULONG count = 0;
|
|
|
-
|
|
|
- DWORD result = GetQueuedCompletionStatusEx(port_data->getHandle(), entries, max_entries, &count, gqcs_timeout, TRUE);
|
|
|
-
|
|
|
- if (!result)
|
|
|
- {
|
|
|
- DWORD error = GetLastError();
|
|
|
- if (error == WAIT_TIMEOUT)
|
|
|
- {
|
|
|
- // printf("%d, GetQueuedCompletionStatusEx:%d\n", std::this_thread::get_id() , count);
|
|
|
-
|
|
|
- return 0;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- return -1;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return port_data->epoll_wait(entries, count, events);
|
|
|
-}
|
|
|
-
|
|
|
-int epoll_close(epoll_t port_handle)
|
|
|
-{
|
|
|
- epoll_port_data_t *port_data = (epoll_port_data_t *)port_handle;
|
|
|
-
|
|
|
- port_data->epoll_close();
|
|
|
-
|
|
|
- /* Finally, remove the port data. */
|
|
|
- delete port_data;
|
|
|
- port_data = NULL;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-int epoll__initialize()
|
|
|
-{
|
|
|
- HMODULE ntdll;
|
|
|
- int r;
|
|
|
- WSADATA wsa_data;
|
|
|
-
|
|
|
- r = WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
|
|
- if (r != 0)
|
|
|
- return -1;
|
|
|
-
|
|
|
- ntdll = LoadLibraryW(L"ntdll.dll");
|
|
|
- if (ntdll == NULL)
|
|
|
- return -1;
|
|
|
-
|
|
|
- pNtDeviceIoControlFile = (PNTDEVICEIOCONTROLFILE)GetProcAddress(ntdll, "NtDeviceIoControlFile");
|
|
|
- if (pNtDeviceIoControlFile == NULL)
|
|
|
- return -1;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-int epoll__afd_poll(SOCKET socket, AFD_POLL_INFO *info, OVERLAPPED *overlapped)
|
|
|
-{
|
|
|
- IO_STATUS_BLOCK iosb;
|
|
|
- IO_STATUS_BLOCK *iosb_ptr;
|
|
|
- HANDLE event = NULL;
|
|
|
- void *apc_context;
|
|
|
- NTSTATUS status;
|
|
|
- DWORD error;
|
|
|
-
|
|
|
- if (overlapped != NULL)
|
|
|
- {
|
|
|
- /* Overlapped operation. */
|
|
|
- iosb_ptr = (IO_STATUS_BLOCK *)&overlapped->Internal;
|
|
|
- event = overlapped->hEvent;
|
|
|
-
|
|
|
- /* Do not report iocp completion if hEvent is tagged. */
|
|
|
- if ((uintptr_t)event & 1)
|
|
|
- {
|
|
|
- event = (HANDLE)((uintptr_t)event & ~(uintptr_t)1);
|
|
|
- apc_context = NULL;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- apc_context = overlapped;
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- /* Blocking operation. */
|
|
|
- iosb_ptr = &iosb;
|
|
|
- event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
- if (event == NULL)
|
|
|
- {
|
|
|
- return SOCKET_ERROR;
|
|
|
- }
|
|
|
- apc_context = NULL;
|
|
|
- }
|
|
|
-
|
|
|
- iosb_ptr->Status = STATUS_PENDING;
|
|
|
- status = pNtDeviceIoControlFile((HANDLE)socket,
|
|
|
- event,
|
|
|
- NULL,
|
|
|
- apc_context,
|
|
|
- iosb_ptr,
|
|
|
- IOCTL_AFD_POLL,
|
|
|
- info,
|
|
|
- sizeof *info,
|
|
|
- info,
|
|
|
- sizeof *info);
|
|
|
-
|
|
|
- if (overlapped == NULL)
|
|
|
- {
|
|
|
- /* If this is a blocking operation, wait for the event to become */
|
|
|
- /* signaled, and then grab the real status from the io status block. */
|
|
|
- if (status == STATUS_PENDING)
|
|
|
- {
|
|
|
- DWORD r = WaitForSingleObject(event, INFINITE);
|
|
|
-
|
|
|
- if (r == WAIT_FAILED)
|
|
|
- {
|
|
|
- DWORD saved_error = GetLastError();
|
|
|
- CloseHandle(event);
|
|
|
- WSASetLastError(saved_error);
|
|
|
- return SOCKET_ERROR;
|
|
|
- }
|
|
|
-
|
|
|
- status = iosb.Status;
|
|
|
- }
|
|
|
-
|
|
|
- CloseHandle(event);
|
|
|
- }
|
|
|
-
|
|
|
- switch (status)
|
|
|
- {
|
|
|
- case STATUS_SUCCESS:
|
|
|
- error = ERROR_SUCCESS;
|
|
|
- break;
|
|
|
-
|
|
|
- case STATUS_PENDING:
|
|
|
- error = WSA_IO_PENDING;
|
|
|
- break;
|
|
|
-
|
|
|
- default:
|
|
|
- error = epoll__ntstatus_to_winsock_error(status);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- WSASetLastError(error);
|
|
|
-
|
|
|
- if (error == ERROR_SUCCESS)
|
|
|
- {
|
|
|
- return 0;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- return SOCKET_ERROR;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-int epoll__ntstatus_to_winsock_error(NTSTATUS status)
|
|
|
-{
|
|
|
- switch (status)
|
|
|
- {
|
|
|
- case STATUS_SUCCESS:
|
|
|
- return ERROR_SUCCESS;
|
|
|
-
|
|
|
- case STATUS_PENDING:
|
|
|
- return ERROR_IO_PENDING;
|
|
|
-
|
|
|
- case STATUS_INVALID_HANDLE:
|
|
|
- case STATUS_OBJECT_TYPE_MISMATCH:
|
|
|
- return WSAENOTSOCK;
|
|
|
-
|
|
|
- case STATUS_INSUFFICIENT_RESOURCES:
|
|
|
- case STATUS_PAGEFILE_QUOTA:
|
|
|
- case STATUS_COMMITMENT_LIMIT:
|
|
|
- case STATUS_WORKING_SET_QUOTA:
|
|
|
- case STATUS_NO_MEMORY:
|
|
|
- case STATUS_CONFLICTING_ADDRESSES:
|
|
|
- case STATUS_QUOTA_EXCEEDED:
|
|
|
- case STATUS_TOO_MANY_PAGING_FILES:
|
|
|
- case STATUS_REMOTE_RESOURCES:
|
|
|
- case STATUS_TOO_MANY_ADDRESSES:
|
|
|
- return WSAENOBUFS;
|
|
|
-
|
|
|
- case STATUS_SHARING_VIOLATION:
|
|
|
- case STATUS_ADDRESS_ALREADY_EXISTS:
|
|
|
- return WSAEADDRINUSE;
|
|
|
-
|
|
|
- case STATUS_LINK_TIMEOUT:
|
|
|
- case STATUS_IO_TIMEOUT:
|
|
|
- case STATUS_TIMEOUT:
|
|
|
- return WSAETIMEDOUT;
|
|
|
-
|
|
|
- case STATUS_GRACEFUL_DISCONNECT:
|
|
|
- return WSAEDISCON;
|
|
|
-
|
|
|
- case STATUS_REMOTE_DISCONNECT:
|
|
|
- case STATUS_CONNECTION_RESET:
|
|
|
- case STATUS_LINK_FAILED:
|
|
|
- case STATUS_CONNECTION_DISCONNECTED:
|
|
|
- case STATUS_PORT_UNREACHABLE:
|
|
|
- case STATUS_HOPLIMIT_EXCEEDED:
|
|
|
- return WSAECONNRESET;
|
|
|
-
|
|
|
- case STATUS_LOCAL_DISCONNECT:
|
|
|
- case STATUS_TRANSACTION_ABORTED:
|
|
|
- case STATUS_CONNECTION_ABORTED:
|
|
|
- return WSAECONNABORTED;
|
|
|
-
|
|
|
- case STATUS_BAD_NETWORK_PATH:
|
|
|
- case STATUS_NETWORK_UNREACHABLE:
|
|
|
- case STATUS_PROTOCOL_UNREACHABLE:
|
|
|
- return WSAENETUNREACH;
|
|
|
-
|
|
|
- case STATUS_HOST_UNREACHABLE:
|
|
|
- return WSAEHOSTUNREACH;
|
|
|
-
|
|
|
- case STATUS_CANCELLED:
|
|
|
- case STATUS_REQUEST_ABORTED:
|
|
|
- return WSAEINTR;
|
|
|
-
|
|
|
- case STATUS_BUFFER_OVERFLOW:
|
|
|
- case STATUS_INVALID_BUFFER_SIZE:
|
|
|
- return WSAEMSGSIZE;
|
|
|
-
|
|
|
- case STATUS_BUFFER_TOO_SMALL:
|
|
|
- case STATUS_ACCESS_VIOLATION:
|
|
|
- return WSAEFAULT;
|
|
|
-
|
|
|
- case STATUS_DEVICE_NOT_READY:
|
|
|
- case STATUS_REQUEST_NOT_ACCEPTED:
|
|
|
- return WSAEWOULDBLOCK;
|
|
|
-
|
|
|
- case STATUS_INVALID_NETWORK_RESPONSE:
|
|
|
- case STATUS_NETWORK_BUSY:
|
|
|
- case STATUS_NO_SUCH_DEVICE:
|
|
|
- case STATUS_NO_SUCH_FILE:
|
|
|
- case STATUS_OBJECT_PATH_NOT_FOUND:
|
|
|
- case STATUS_OBJECT_NAME_NOT_FOUND:
|
|
|
- case STATUS_UNEXPECTED_NETWORK_ERROR:
|
|
|
- return WSAENETDOWN;
|
|
|
-
|
|
|
- case STATUS_INVALID_CONNECTION:
|
|
|
- return WSAENOTCONN;
|
|
|
-
|
|
|
- case STATUS_REMOTE_NOT_LISTENING:
|
|
|
- case STATUS_CONNECTION_REFUSED:
|
|
|
- return WSAECONNREFUSED;
|
|
|
-
|
|
|
- case STATUS_PIPE_DISCONNECTED:
|
|
|
- return WSAESHUTDOWN;
|
|
|
-
|
|
|
- case STATUS_INVALID_ADDRESS:
|
|
|
- case STATUS_INVALID_ADDRESS_COMPONENT:
|
|
|
- return WSAEADDRNOTAVAIL;
|
|
|
-
|
|
|
- case STATUS_NOT_SUPPORTED:
|
|
|
- case STATUS_NOT_IMPLEMENTED:
|
|
|
- return WSAEOPNOTSUPP;
|
|
|
-
|
|
|
- case STATUS_ACCESS_DENIED:
|
|
|
- return WSAEACCES;
|
|
|
-
|
|
|
- default:
|
|
|
- if ((status & (FACILITY_NTWIN32 << 16)) == (FACILITY_NTWIN32 << 16) &&
|
|
|
- (status & (ERROR_SEVERITY_ERROR | ERROR_SEVERITY_WARNING)))
|
|
|
- {
|
|
|
- /* It's a windows error that has been previously mapped to an */
|
|
|
- /* ntstatus code. */
|
|
|
- return (DWORD)(status & 0xffff);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- /* The default fallback for unmappable ntstatus codes. */
|
|
|
- return WSAEINVAL;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
+#include "sys/epoll.h"
|
|
|
+#include "msafd.h"
|
|
|
+
|
|
|
+#include <unordered_map>
|
|
|
+#include <unordered_set>
|
|
|
+#include <cassert>
|
|
|
+#include <mutex>
|
|
|
+#include <iostream>
|
|
|
+
|
|
|
+using namespace std;
|
|
|
+
|
|
|
+#define ARRAY_COUNT(a) (sizeof(a) / (sizeof((a)[0])))
|
|
|
+
|
|
|
+#define EPOLL__EVENT_MASK 0xffff
|
|
|
+
|
|
|
+class epoll_port_data_t;
|
|
|
+struct epoll_sock_data_t;
|
|
|
+struct epoll_op_t;
|
|
|
+
|
|
|
+/* State associated with a AFD_POLL request. */
|
|
|
+struct epoll_op_t
|
|
|
+{
|
|
|
+ OVERLAPPED _overlapped;
|
|
|
+ AFD_POLL_INFO _poll_info;
|
|
|
+ uint32_t _generation;
|
|
|
+ epoll_sock_data_t *_sock_data;
|
|
|
+
|
|
|
+ epoll_op_t(epoll_sock_data_t *sock_data, uint32_t afd_events);
|
|
|
+ ~epoll_op_t();
|
|
|
+};
|
|
|
+
|
|
|
+/* State associated with a socket that is registered to the epoll port. */
|
|
|
+struct epoll_sock_data_t
|
|
|
+{
|
|
|
+ SOCKET _sock;
|
|
|
+ SOCKET _base_sock;
|
|
|
+ SOCKET _peer_sock;
|
|
|
+ uint32_t _registered_events;
|
|
|
+ uint32_t _op_generation;
|
|
|
+ uint64_t _user_data;
|
|
|
+ int32_t _op_count;
|
|
|
+
|
|
|
+ epoll_sock_data_t(SOCKET sock, SOCKET base_sock, SOCKET peer_sock);
|
|
|
+
|
|
|
+ int submit();
|
|
|
+};
|
|
|
+
|
|
|
+/* State associated with a epoll handle. */
|
|
|
+class epoll_port_data_t
|
|
|
+{
|
|
|
+protected:
|
|
|
+ HANDLE _iocp;
|
|
|
+ SOCKET _peer_sockets[ARRAY_COUNT(AFD_PROVIDER_IDS)];
|
|
|
+ unordered_map<int, epoll_sock_data_t *> _sock_data_tree;
|
|
|
+ unordered_set<epoll_sock_data_t *> _attn_list;
|
|
|
+ std::mutex _mutex;
|
|
|
+
|
|
|
+public:
|
|
|
+ epoll_port_data_t();
|
|
|
+ HANDLE getHandle() { return _iocp; }
|
|
|
+ int epoll_add(SOCKET sock, struct epoll_event *ev);
|
|
|
+ int epoll_mod(SOCKET sock, struct epoll_event *ev);
|
|
|
+ int epoll_del(SOCKET sock, struct epoll_event *ev);
|
|
|
+ int epoll_close();
|
|
|
+ int epoll_wait(OVERLAPPED_ENTRY *entries, ULONG count, struct epoll_event *events);
|
|
|
+ void submit()
|
|
|
+ {
|
|
|
+ std::lock_guard<std::mutex> lck (_mutex);
|
|
|
+
|
|
|
+ for(auto entry : _attn_list)
|
|
|
+ {
|
|
|
+ if(entry->_op_count == 0) {
|
|
|
+ // printf("before epoll_wait submit: %d\n", entry->_op_count);
|
|
|
+ //no focus any event, then submit keep attn, otherwise cause op leak
|
|
|
+ entry->submit();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _attn_list.clear();
|
|
|
+ }
|
|
|
+protected:
|
|
|
+ bool find(epoll_sock_data_t *sock_data)
|
|
|
+ {
|
|
|
+ auto it = _sock_data_tree.find(sock_data->_sock);
|
|
|
+ if (it != _sock_data_tree.end()) {
|
|
|
+ return it->second == sock_data;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ void remove(epoll_sock_data_t *sock_data)
|
|
|
+ {
|
|
|
+ _sock_data_tree.erase(sock_data->_sock);
|
|
|
+ _attn_list.erase(sock_data);
|
|
|
+ }
|
|
|
+ void add(epoll_sock_data_t *sock_data)
|
|
|
+ {
|
|
|
+ _sock_data_tree[sock_data->_sock] = sock_data;
|
|
|
+ }
|
|
|
+ void remove_attn(epoll_sock_data_t *sock_data)
|
|
|
+ {
|
|
|
+ _attn_list.erase(sock_data);
|
|
|
+ }
|
|
|
+ void add_attn(epoll_sock_data_t *sock_data)
|
|
|
+ {
|
|
|
+ _attn_list.insert(sock_data);
|
|
|
+ }
|
|
|
+
|
|
|
+ epoll_sock_data_t *get(SOCKET sock)
|
|
|
+ {
|
|
|
+ auto it = _sock_data_tree.find(sock);
|
|
|
+ if (it == _sock_data_tree.end())
|
|
|
+ {
|
|
|
+ /* Socket has not been registered with epoll instance. */
|
|
|
+ SetLastError(ERROR_NOT_FOUND);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ return it->second;
|
|
|
+ }
|
|
|
+
|
|
|
+ SOCKET create_peer_socket(WSAPROTOCOL_INFOW *protocol_info);
|
|
|
+ SOCKET get_peer_socket(WSAPROTOCOL_INFOW *protocol_info);
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+static int epoll__initialize();
|
|
|
+static int epoll__afd_poll(SOCKET socket, AFD_POLL_INFO *info, OVERLAPPED *overlapped);
|
|
|
+static int epoll__ntstatus_to_winsock_error(NTSTATUS status);
|
|
|
+
|
|
|
+static int epoll__initialized = 0;
|
|
|
+static PNTDEVICEIOCONTROLFILE pNtDeviceIoControlFile;
|
|
|
+
|
|
|
+epoll_op_t::epoll_op_t(epoll_sock_data_t *sock_data, uint32_t afd_events)
|
|
|
+{
|
|
|
+ _generation = sock_data->_op_generation + 1;
|
|
|
+
|
|
|
+ memset(&_overlapped, 0, sizeof _overlapped);
|
|
|
+ _poll_info.Exclusive = FALSE;
|
|
|
+ _poll_info.NumberOfHandles = 1;
|
|
|
+ _poll_info.Timeout.QuadPart = INT64_MAX;
|
|
|
+ _poll_info.Handles[0].Handle = (HANDLE)sock_data->_base_sock;
|
|
|
+ _poll_info.Handles[0].Status = 0;
|
|
|
+ _poll_info.Handles[0].Events = afd_events;
|
|
|
+
|
|
|
+ _sock_data = sock_data;
|
|
|
+ _sock_data->_op_count++;
|
|
|
+
|
|
|
+ // printf("submit new: %p, %d\n", this, _sock_data->_op_count);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+epoll_op_t::~epoll_op_t()
|
|
|
+{
|
|
|
+ // printf("~epoll_op_t: %p, %d\n", this, _sock_data->_op_count );
|
|
|
+
|
|
|
+ _sock_data->_op_count--;
|
|
|
+ assert(_sock_data->_op_count >= 0);
|
|
|
+}
|
|
|
+
|
|
|
+epoll_sock_data_t::epoll_sock_data_t(SOCKET sock, SOCKET base_sock, SOCKET peer_sock)
|
|
|
+{
|
|
|
+ _sock = sock;
|
|
|
+ _base_sock = base_sock;
|
|
|
+ _op_generation = 0;
|
|
|
+ _registered_events = 0;
|
|
|
+ _user_data = 0;
|
|
|
+ _peer_sock = peer_sock;
|
|
|
+ _op_count = 0;
|
|
|
+}
|
|
|
+
|
|
|
+int epoll_sock_data_t::submit()
|
|
|
+{
|
|
|
+ DWORD afd_events = AFD_POLL_ABORT | AFD_POLL_DISCONNECT | AFD_POLL_CONNECT_FAIL | AFD_POLL_LOCAL_CLOSE;
|
|
|
+
|
|
|
+ if (_registered_events & (EPOLLIN | EPOLLRDNORM))
|
|
|
+ {
|
|
|
+ afd_events |= AFD_POLL_RECEIVE | AFD_POLL_ACCEPT ;
|
|
|
+ }
|
|
|
+ if (_registered_events & (EPOLLIN | EPOLLRDBAND))
|
|
|
+ {
|
|
|
+ afd_events |= AFD_POLL_RECEIVE_EXPEDITED;
|
|
|
+ }
|
|
|
+ if (_registered_events & (EPOLLOUT | EPOLLWRNORM | EPOLLRDBAND))
|
|
|
+ {
|
|
|
+ afd_events |= AFD_POLL_SEND | AFD_POLL_CONNECT;
|
|
|
+ }
|
|
|
+
|
|
|
+ epoll_op_t* op = new epoll_op_t(this, afd_events);
|
|
|
+
|
|
|
+ DWORD result = epoll__afd_poll(_peer_sock, &op->_poll_info, &op->_overlapped);
|
|
|
+ if (result != 0)
|
|
|
+ {
|
|
|
+ DWORD error = WSAGetLastError();
|
|
|
+ if (error != WSA_IO_PENDING)
|
|
|
+ {
|
|
|
+ /* If this happens an error happened and no overlapped operation was */
|
|
|
+ /* started. */
|
|
|
+ delete op;
|
|
|
+ op = NULL;
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ _op_generation = op->_generation;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+//////////////////////////////////////////////////////////////////////
|
|
|
+epoll_port_data_t::epoll_port_data_t()
|
|
|
+{
|
|
|
+ _iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
|
|
+ if (_iocp == INVALID_HANDLE_VALUE)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ memset(&_peer_sockets, 0, sizeof _peer_sockets);
|
|
|
+}
|
|
|
+
|
|
|
+SOCKET epoll_port_data_t::get_peer_socket(WSAPROTOCOL_INFOW *protocol_info)
|
|
|
+{
|
|
|
+ int index = -1;
|
|
|
+
|
|
|
+ for (int i = 0; i < ARRAY_COUNT(AFD_PROVIDER_IDS); i++)
|
|
|
+ {
|
|
|
+ if (memcmp((void *)&protocol_info->ProviderId, (void *)&AFD_PROVIDER_IDS[i], sizeof protocol_info->ProviderId) == 0)
|
|
|
+ {
|
|
|
+ index = i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check if the protocol uses an msafd socket. */
|
|
|
+ if (index < 0)
|
|
|
+ {
|
|
|
+ SetLastError(ERROR_NOT_SUPPORTED);
|
|
|
+ return INVALID_SOCKET;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If we didn't (try) to create a peer socket yet, try to make one. Don't */
|
|
|
+ /* try again if the peer socket creation failed earlier for the same */
|
|
|
+ /* protocol. */
|
|
|
+ SOCKET peer_socket = _peer_sockets[index];
|
|
|
+ if (peer_socket == 0)
|
|
|
+ {
|
|
|
+ peer_socket = create_peer_socket(protocol_info);
|
|
|
+ _peer_sockets[index] = peer_socket;
|
|
|
+ }
|
|
|
+
|
|
|
+ return peer_socket;
|
|
|
+}
|
|
|
+
|
|
|
+SOCKET epoll_port_data_t::create_peer_socket(WSAPROTOCOL_INFOW *protocol_info)
|
|
|
+{
|
|
|
+ SOCKET sock = WSASocketW(protocol_info->iAddressFamily, protocol_info->iSocketType, protocol_info->iProtocol, protocol_info, 0, WSA_FLAG_OVERLAPPED);
|
|
|
+ if (sock == INVALID_SOCKET)
|
|
|
+ {
|
|
|
+ return INVALID_SOCKET;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!SetHandleInformation((HANDLE)sock, HANDLE_FLAG_INHERIT, 0))
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ };
|
|
|
+
|
|
|
+ if (CreateIoCompletionPort((HANDLE)sock, _iocp, 0, 0) == NULL)
|
|
|
+ {
|
|
|
+ goto error;
|
|
|
+ }
|
|
|
+
|
|
|
+ return sock;
|
|
|
+
|
|
|
+error:
|
|
|
+ closesocket(sock);
|
|
|
+ return INVALID_SOCKET;
|
|
|
+}
|
|
|
+
|
|
|
+int epoll_port_data_t::epoll_add(SOCKET sock, struct epoll_event *ev)
|
|
|
+{
|
|
|
+ std::lock_guard<std::mutex> lck (_mutex);
|
|
|
+
|
|
|
+ epoll_sock_data_t *sock_data = get(sock);
|
|
|
+ if(sock_data != NULL)
|
|
|
+ {
|
|
|
+ SetLastError(ERROR_ALREADY_EXISTS);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ WSAPROTOCOL_INFOW protocol_info;
|
|
|
+ DWORD bytes;
|
|
|
+
|
|
|
+ /* Try to obtain a base handle for the socket, so we can bypass LSPs */
|
|
|
+ /* that get in the way if we want to talk to the kernel directly. If */
|
|
|
+ /* it fails we try if we work with the original socket. Note that on */
|
|
|
+ /* windows XP/2k3 this will always fail since they don't support the */
|
|
|
+ /* SIO_BASE_HANDLE ioctl. */
|
|
|
+ SOCKET base_sock = sock;
|
|
|
+ WSAIoctl(sock, SIO_BASE_HANDLE, NULL, 0, &base_sock, sizeof base_sock, &bytes, NULL, NULL);
|
|
|
+
|
|
|
+ /* Obtain protocol information about the socket. */
|
|
|
+ int len = sizeof protocol_info;
|
|
|
+ if (getsockopt(base_sock, SOL_SOCKET, SO_PROTOCOL_INFOW, (char *)&protocol_info, &len) != 0)
|
|
|
+ {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ SOCKET peer_sock = get_peer_socket(&protocol_info);
|
|
|
+ if (peer_sock == INVALID_SOCKET)
|
|
|
+ {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ sock_data = new epoll_sock_data_t(sock, base_sock, peer_sock);
|
|
|
+ if (sock_data == NULL)
|
|
|
+ {
|
|
|
+ SetLastError(ERROR_OUTOFMEMORY);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ // printf("add new, %d\n", sock_data->_op_count);
|
|
|
+
|
|
|
+ add(sock_data);
|
|
|
+
|
|
|
+ sock_data->_registered_events = ev->events | EPOLLERR | EPOLLHUP;
|
|
|
+ sock_data->_user_data = ev->data.u64;
|
|
|
+
|
|
|
+ return sock_data->submit();
|
|
|
+}
|
|
|
+
|
|
|
+int epoll_port_data_t::epoll_mod(SOCKET sock, struct epoll_event *ev)
|
|
|
+{
|
|
|
+ {
|
|
|
+ std::lock_guard<std::mutex> lck (_mutex);
|
|
|
+
|
|
|
+ epoll_sock_data_t *sock_data = get(sock);
|
|
|
+
|
|
|
+ if(sock_data != NULL)
|
|
|
+ {
|
|
|
+ sock_data->_registered_events = ev->events | EPOLLERR | EPOLLHUP;
|
|
|
+ sock_data->_user_data = ev->data.u64;
|
|
|
+ return sock_data->submit();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // LOG_CONSOLE_DEBUG << endl;
|
|
|
+ return epoll_add(sock, ev);
|
|
|
+}
|
|
|
+
|
|
|
+int epoll_port_data_t::epoll_del(SOCKET sock, struct epoll_event *ev)
|
|
|
+{
|
|
|
+ std::lock_guard<std::mutex> lck (_mutex);
|
|
|
+
|
|
|
+ // printf("del new\n");
|
|
|
+
|
|
|
+ epoll_sock_data_t *sock_data = get(sock);
|
|
|
+ if(sock_data == NULL)
|
|
|
+ {
|
|
|
+ SetLastError(ERROR_NOT_FOUND);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Remove from attention list. */
|
|
|
+ remove(sock_data);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ // 以下代码不能有, 否则短链接模式下句柄复用会有问题!!!!
|
|
|
+ // sock_data->_registered_events = ev->events | EPOLLERR | EPOLLHUP;
|
|
|
+ // sock_data->_user_data = ev->data.u64;
|
|
|
+
|
|
|
+ // return sock_data->submit();
|
|
|
+}
|
|
|
+
|
|
|
+int epoll_port_data_t::epoll_close()
|
|
|
+{
|
|
|
+ std::lock_guard<std::mutex> lck (_mutex);
|
|
|
+
|
|
|
+ /* Close all peer sockets. This will make all pending ops return. */
|
|
|
+ for (int i = 0; i < ARRAY_COUNT(_peer_sockets); i++)
|
|
|
+ {
|
|
|
+ SOCKET peer_sock = _peer_sockets[i];
|
|
|
+ if (peer_sock != 0 && peer_sock != INVALID_SOCKET)
|
|
|
+ {
|
|
|
+ closesocket(peer_sock);
|
|
|
+
|
|
|
+ _peer_sockets[i] = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Remove all entries from the socket_state tree. */
|
|
|
+ for(auto entry : _sock_data_tree)
|
|
|
+ {
|
|
|
+ entry.second->submit();
|
|
|
+ }
|
|
|
+
|
|
|
+ while(!_sock_data_tree.empty())
|
|
|
+ {
|
|
|
+ OVERLAPPED_ENTRY entries[64];
|
|
|
+ ULONG count;
|
|
|
+
|
|
|
+ DWORD result = GetQueuedCompletionStatusEx(_iocp, entries, ARRAY_COUNT(entries), &count, 0, FALSE);
|
|
|
+ if (!result)
|
|
|
+ {
|
|
|
+ DWORD error = GetLastError();
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ for(ULONG i = 0; i < count; i++)
|
|
|
+ {
|
|
|
+ epoll_op_t *op = CONTAINING_RECORD(entries[i].lpOverlapped, epoll_op_t, _overlapped);
|
|
|
+ epoll_sock_data_t *sock_data = op->_sock_data;
|
|
|
+
|
|
|
+ if(!find(sock_data))
|
|
|
+ {
|
|
|
+ remove(sock_data);
|
|
|
+ }
|
|
|
+
|
|
|
+ delete op;
|
|
|
+ if(sock_data->_op_count == 0)
|
|
|
+ delete sock_data;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(count == 0)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Close the I/O completion port. */
|
|
|
+ CloseHandle(_iocp);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int epoll_port_data_t::epoll_wait(OVERLAPPED_ENTRY *entries, ULONG count, struct epoll_event *events)
|
|
|
+{
|
|
|
+ std::lock_guard<std::mutex> lck (_mutex);
|
|
|
+
|
|
|
+ int num_events = 0;
|
|
|
+
|
|
|
+ /* Successfully dequeued overlappeds. */
|
|
|
+ for (ULONG i = 0; i < count; i++)
|
|
|
+ {
|
|
|
+ epoll_op_t *op = CONTAINING_RECORD(entries[i].lpOverlapped, epoll_op_t, _overlapped);
|
|
|
+ epoll_sock_data_t *sock_data = op->_sock_data;
|
|
|
+
|
|
|
+ if (op->_generation < sock_data->_op_generation)
|
|
|
+ {
|
|
|
+ // cout << "op superseded" << endl;
|
|
|
+ delete op;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /* Dequeued the most recent op. Reset generation and submitted_events. */
|
|
|
+ sock_data->_op_generation = 0;
|
|
|
+
|
|
|
+ /* Check if this op was associated with a socket that was removed */
|
|
|
+ /* with EPOLL_CTL_DEL. */
|
|
|
+ if(!find(sock_data))
|
|
|
+ {
|
|
|
+ delete op;
|
|
|
+ if(sock_data->_op_count == 0)
|
|
|
+ delete sock_data;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check for error. */
|
|
|
+ if (!NT_SUCCESS(entries[i].lpOverlapped->Internal))
|
|
|
+ {
|
|
|
+ struct epoll_event *ev = events + num_events;
|
|
|
+ ev->data.u64 = sock_data->_user_data;
|
|
|
+ ev->events = EPOLLERR;
|
|
|
+ num_events++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ DWORD afd_events;
|
|
|
+
|
|
|
+ if (op->_poll_info.NumberOfHandles == 0)
|
|
|
+ {
|
|
|
+ /* NumberOfHandles can be zero if this poll operation was canceled */
|
|
|
+ /* due to a more recent exclusive poll operation. */
|
|
|
+ afd_events = 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ afd_events = op->_poll_info.Handles[0].Events;
|
|
|
+ }
|
|
|
+
|
|
|
+ // cout << "wait ok, handles:" << op->_poll_info.NumberOfHandles << ", events:" << afd_events << endl;
|
|
|
+
|
|
|
+ /* Check for a closed socket. */
|
|
|
+ if (afd_events & AFD_POLL_LOCAL_CLOSE)
|
|
|
+ {
|
|
|
+ remove(sock_data);
|
|
|
+ delete op;
|
|
|
+ if(sock_data->_op_count == 0)
|
|
|
+ delete sock_data;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ delete op;
|
|
|
+
|
|
|
+ // int registered_events = sock_data->_registered_events;
|
|
|
+ int reported_events = 0;
|
|
|
+ /* Convert afd events to epoll events. */
|
|
|
+ if (afd_events & (AFD_POLL_RECEIVE | AFD_POLL_ACCEPT))
|
|
|
+ {
|
|
|
+ reported_events |= EPOLLIN;
|
|
|
+ }
|
|
|
+ if (afd_events & AFD_POLL_RECEIVE_EXPEDITED)
|
|
|
+ {
|
|
|
+ reported_events |= (EPOLLIN | EPOLLRDBAND);
|
|
|
+ }
|
|
|
+ if (afd_events & AFD_POLL_SEND)
|
|
|
+ {
|
|
|
+ reported_events |= (EPOLLOUT);
|
|
|
+ }
|
|
|
+ if ((afd_events & AFD_POLL_DISCONNECT) && !(afd_events & AFD_POLL_ABORT))
|
|
|
+ {
|
|
|
+ reported_events |= (EPOLLRDHUP | EPOLLIN | EPOLLRDNORM | EPOLLRDBAND);
|
|
|
+ }
|
|
|
+ if (afd_events & AFD_POLL_ABORT)
|
|
|
+ {
|
|
|
+ reported_events |= EPOLLHUP | EPOLLERR;
|
|
|
+ }
|
|
|
+ if (afd_events & AFD_POLL_CONNECT)
|
|
|
+ {
|
|
|
+ reported_events |= (EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND);
|
|
|
+ }
|
|
|
+ if (afd_events & AFD_POLL_CONNECT_FAIL)
|
|
|
+ {
|
|
|
+ reported_events |= EPOLLERR;
|
|
|
+ }
|
|
|
+
|
|
|
+ //keep attn
|
|
|
+ sock_data->_registered_events &= ~EPOLLOUT;
|
|
|
+ add_attn(sock_data);
|
|
|
+
|
|
|
+ /* Don't report events that the user didn't specify. */
|
|
|
+ // reported_events &= registered_events;
|
|
|
+ // if(reported_events & EPOLLOUT)
|
|
|
+ // cout << "reported_events EPOLLOUT:" << reported_events << endl;
|
|
|
+
|
|
|
+ if (reported_events)
|
|
|
+ {
|
|
|
+ struct epoll_event *ev = events + num_events;
|
|
|
+ ev->data.u64 = sock_data->_user_data;
|
|
|
+ ev->events = reported_events;
|
|
|
+ num_events++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return num_events;
|
|
|
+}
|
|
|
+
|
|
|
+///////////////////////////////////////////////////////////////////////////////////
|
|
|
+epoll_t epoll_create(int size)
|
|
|
+{
|
|
|
+ /* If necessary, do global initialization first. This is totally not */
|
|
|
+ /* thread-safe at the moment. */
|
|
|
+ if (!epoll__initialized)
|
|
|
+ {
|
|
|
+ if (epoll__initialize() < 0)
|
|
|
+ return NULL;
|
|
|
+ epoll__initialized = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ epoll_port_data_t *port_data = new epoll_port_data_t();
|
|
|
+ if (port_data == NULL)
|
|
|
+ {
|
|
|
+ SetLastError(ERROR_OUTOFMEMORY);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return (epoll_t)port_data;
|
|
|
+}
|
|
|
+
|
|
|
+int epoll_ctl(epoll_t port_handle, int op, SOCKET sock, struct epoll_event *ev)
|
|
|
+{
|
|
|
+ epoll_port_data_t *port_data = (epoll_port_data_t *)port_handle;
|
|
|
+
|
|
|
+ switch (op)
|
|
|
+ {
|
|
|
+ case EPOLL_CTL_ADD:
|
|
|
+ {
|
|
|
+ return port_data->epoll_add(sock, ev);
|
|
|
+ }
|
|
|
+ case EPOLL_CTL_MOD:
|
|
|
+ {
|
|
|
+ return port_data->epoll_mod(sock, ev);
|
|
|
+ }
|
|
|
+ case EPOLL_CTL_DEL:
|
|
|
+ {
|
|
|
+ return port_data->epoll_del(sock, ev);
|
|
|
+ }
|
|
|
+
|
|
|
+ default:
|
|
|
+ WSASetLastError(WSAEINVAL);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int epoll_wait(epoll_t port_handle, struct epoll_event *events, int maxevents, int timeout)
|
|
|
+{
|
|
|
+ DWORD gqcs_timeout;
|
|
|
+
|
|
|
+ /* Compute the timeout for GetQueuedCompletionStatus, and the wait end */
|
|
|
+ /* time, if the user specified a timeout other than zero or infinite. */
|
|
|
+ if (timeout >= 0)
|
|
|
+ {
|
|
|
+ gqcs_timeout = (DWORD)timeout;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ gqcs_timeout = INFINITE;
|
|
|
+ }
|
|
|
+
|
|
|
+ epoll_port_data_t *port_data = (epoll_port_data_t *)port_handle;
|
|
|
+
|
|
|
+ port_data->submit();
|
|
|
+
|
|
|
+ OVERLAPPED_ENTRY entries[64];
|
|
|
+
|
|
|
+ /* Compute how much overlapped entries can be dequeued at most. */
|
|
|
+ int tmp_count = ARRAY_COUNT(entries);
|
|
|
+ DWORD max_entries = min(tmp_count, maxevents);
|
|
|
+ ULONG count = 0;
|
|
|
+
|
|
|
+ DWORD result = GetQueuedCompletionStatusEx(port_data->getHandle(), entries, max_entries, &count, gqcs_timeout, TRUE);
|
|
|
+
|
|
|
+ if (!result)
|
|
|
+ {
|
|
|
+ DWORD error = GetLastError();
|
|
|
+ if (error == WAIT_TIMEOUT)
|
|
|
+ {
|
|
|
+ // printf("%d, GetQueuedCompletionStatusEx:%d\n", std::this_thread::get_id() , count);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return port_data->epoll_wait(entries, count, events);
|
|
|
+}
|
|
|
+
|
|
|
+int epoll_close(epoll_t port_handle)
|
|
|
+{
|
|
|
+ epoll_port_data_t *port_data = (epoll_port_data_t *)port_handle;
|
|
|
+
|
|
|
+ port_data->epoll_close();
|
|
|
+
|
|
|
+ /* Finally, remove the port data. */
|
|
|
+ delete port_data;
|
|
|
+ port_data = NULL;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int epoll__initialize()
|
|
|
+{
|
|
|
+ HMODULE ntdll;
|
|
|
+ int r;
|
|
|
+ WSADATA wsa_data;
|
|
|
+
|
|
|
+ r = WSAStartup(MAKEWORD(2, 2), &wsa_data);
|
|
|
+ if (r != 0)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ ntdll = LoadLibraryW(L"ntdll.dll");
|
|
|
+ if (ntdll == NULL)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ pNtDeviceIoControlFile = (PNTDEVICEIOCONTROLFILE)GetProcAddress(ntdll, "NtDeviceIoControlFile");
|
|
|
+ if (pNtDeviceIoControlFile == NULL)
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int epoll__afd_poll(SOCKET socket, AFD_POLL_INFO *info, OVERLAPPED *overlapped)
|
|
|
+{
|
|
|
+ IO_STATUS_BLOCK iosb;
|
|
|
+ IO_STATUS_BLOCK *iosb_ptr;
|
|
|
+ HANDLE event = NULL;
|
|
|
+ void *apc_context;
|
|
|
+ NTSTATUS status;
|
|
|
+ DWORD error;
|
|
|
+
|
|
|
+ if (overlapped != NULL)
|
|
|
+ {
|
|
|
+ /* Overlapped operation. */
|
|
|
+ iosb_ptr = (IO_STATUS_BLOCK *)&overlapped->Internal;
|
|
|
+ event = overlapped->hEvent;
|
|
|
+
|
|
|
+ /* Do not report iocp completion if hEvent is tagged. */
|
|
|
+ if ((uintptr_t)event & 1)
|
|
|
+ {
|
|
|
+ event = (HANDLE)((uintptr_t)event & ~(uintptr_t)1);
|
|
|
+ apc_context = NULL;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ apc_context = overlapped;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ /* Blocking operation. */
|
|
|
+ iosb_ptr = &iosb;
|
|
|
+ event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
+ if (event == NULL)
|
|
|
+ {
|
|
|
+ return SOCKET_ERROR;
|
|
|
+ }
|
|
|
+ apc_context = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ iosb_ptr->Status = STATUS_PENDING;
|
|
|
+ status = pNtDeviceIoControlFile((HANDLE)socket,
|
|
|
+ event,
|
|
|
+ NULL,
|
|
|
+ apc_context,
|
|
|
+ iosb_ptr,
|
|
|
+ IOCTL_AFD_POLL,
|
|
|
+ info,
|
|
|
+ sizeof *info,
|
|
|
+ info,
|
|
|
+ sizeof *info);
|
|
|
+
|
|
|
+ if (overlapped == NULL)
|
|
|
+ {
|
|
|
+ /* If this is a blocking operation, wait for the event to become */
|
|
|
+ /* signaled, and then grab the real status from the io status block. */
|
|
|
+ if (status == STATUS_PENDING)
|
|
|
+ {
|
|
|
+ DWORD r = WaitForSingleObject(event, INFINITE);
|
|
|
+
|
|
|
+ if (r == WAIT_FAILED)
|
|
|
+ {
|
|
|
+ DWORD saved_error = GetLastError();
|
|
|
+ CloseHandle(event);
|
|
|
+ WSASetLastError(saved_error);
|
|
|
+ return SOCKET_ERROR;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = iosb.Status;
|
|
|
+ }
|
|
|
+
|
|
|
+ CloseHandle(event);
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (status)
|
|
|
+ {
|
|
|
+ case STATUS_SUCCESS:
|
|
|
+ error = ERROR_SUCCESS;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case STATUS_PENDING:
|
|
|
+ error = WSA_IO_PENDING;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ error = epoll__ntstatus_to_winsock_error(status);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ WSASetLastError(error);
|
|
|
+
|
|
|
+ if (error == ERROR_SUCCESS)
|
|
|
+ {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return SOCKET_ERROR;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int epoll__ntstatus_to_winsock_error(NTSTATUS status)
|
|
|
+{
|
|
|
+ switch (status)
|
|
|
+ {
|
|
|
+ case STATUS_SUCCESS:
|
|
|
+ return ERROR_SUCCESS;
|
|
|
+
|
|
|
+ case STATUS_PENDING:
|
|
|
+ return ERROR_IO_PENDING;
|
|
|
+
|
|
|
+ case STATUS_INVALID_HANDLE:
|
|
|
+ case STATUS_OBJECT_TYPE_MISMATCH:
|
|
|
+ return WSAENOTSOCK;
|
|
|
+
|
|
|
+ case STATUS_INSUFFICIENT_RESOURCES:
|
|
|
+ case STATUS_PAGEFILE_QUOTA:
|
|
|
+ case STATUS_COMMITMENT_LIMIT:
|
|
|
+ case STATUS_WORKING_SET_QUOTA:
|
|
|
+ case STATUS_NO_MEMORY:
|
|
|
+ case STATUS_CONFLICTING_ADDRESSES:
|
|
|
+ case STATUS_QUOTA_EXCEEDED:
|
|
|
+ case STATUS_TOO_MANY_PAGING_FILES:
|
|
|
+ case STATUS_REMOTE_RESOURCES:
|
|
|
+ case STATUS_TOO_MANY_ADDRESSES:
|
|
|
+ return WSAENOBUFS;
|
|
|
+
|
|
|
+ case STATUS_SHARING_VIOLATION:
|
|
|
+ case STATUS_ADDRESS_ALREADY_EXISTS:
|
|
|
+ return WSAEADDRINUSE;
|
|
|
+
|
|
|
+ case STATUS_LINK_TIMEOUT:
|
|
|
+ case STATUS_IO_TIMEOUT:
|
|
|
+ case STATUS_TIMEOUT:
|
|
|
+ return WSAETIMEDOUT;
|
|
|
+
|
|
|
+ case STATUS_GRACEFUL_DISCONNECT:
|
|
|
+ return WSAEDISCON;
|
|
|
+
|
|
|
+ case STATUS_REMOTE_DISCONNECT:
|
|
|
+ case STATUS_CONNECTION_RESET:
|
|
|
+ case STATUS_LINK_FAILED:
|
|
|
+ case STATUS_CONNECTION_DISCONNECTED:
|
|
|
+ case STATUS_PORT_UNREACHABLE:
|
|
|
+ case STATUS_HOPLIMIT_EXCEEDED:
|
|
|
+ return WSAECONNRESET;
|
|
|
+
|
|
|
+ case STATUS_LOCAL_DISCONNECT:
|
|
|
+ case STATUS_TRANSACTION_ABORTED:
|
|
|
+ case STATUS_CONNECTION_ABORTED:
|
|
|
+ return WSAECONNABORTED;
|
|
|
+
|
|
|
+ case STATUS_BAD_NETWORK_PATH:
|
|
|
+ case STATUS_NETWORK_UNREACHABLE:
|
|
|
+ case STATUS_PROTOCOL_UNREACHABLE:
|
|
|
+ return WSAENETUNREACH;
|
|
|
+
|
|
|
+ case STATUS_HOST_UNREACHABLE:
|
|
|
+ return WSAEHOSTUNREACH;
|
|
|
+
|
|
|
+ case STATUS_CANCELLED:
|
|
|
+ case STATUS_REQUEST_ABORTED:
|
|
|
+ return WSAEINTR;
|
|
|
+
|
|
|
+ case STATUS_BUFFER_OVERFLOW:
|
|
|
+ case STATUS_INVALID_BUFFER_SIZE:
|
|
|
+ return WSAEMSGSIZE;
|
|
|
+
|
|
|
+ case STATUS_BUFFER_TOO_SMALL:
|
|
|
+ case STATUS_ACCESS_VIOLATION:
|
|
|
+ return WSAEFAULT;
|
|
|
+
|
|
|
+ case STATUS_DEVICE_NOT_READY:
|
|
|
+ case STATUS_REQUEST_NOT_ACCEPTED:
|
|
|
+ return WSAEWOULDBLOCK;
|
|
|
+
|
|
|
+ case STATUS_INVALID_NETWORK_RESPONSE:
|
|
|
+ case STATUS_NETWORK_BUSY:
|
|
|
+ case STATUS_NO_SUCH_DEVICE:
|
|
|
+ case STATUS_NO_SUCH_FILE:
|
|
|
+ case STATUS_OBJECT_PATH_NOT_FOUND:
|
|
|
+ case STATUS_OBJECT_NAME_NOT_FOUND:
|
|
|
+ case STATUS_UNEXPECTED_NETWORK_ERROR:
|
|
|
+ return WSAENETDOWN;
|
|
|
+
|
|
|
+ case STATUS_INVALID_CONNECTION:
|
|
|
+ return WSAENOTCONN;
|
|
|
+
|
|
|
+ case STATUS_REMOTE_NOT_LISTENING:
|
|
|
+ case STATUS_CONNECTION_REFUSED:
|
|
|
+ return WSAECONNREFUSED;
|
|
|
+
|
|
|
+ case STATUS_PIPE_DISCONNECTED:
|
|
|
+ return WSAESHUTDOWN;
|
|
|
+
|
|
|
+ case STATUS_INVALID_ADDRESS:
|
|
|
+ case STATUS_INVALID_ADDRESS_COMPONENT:
|
|
|
+ return WSAEADDRNOTAVAIL;
|
|
|
+
|
|
|
+ case STATUS_NOT_SUPPORTED:
|
|
|
+ case STATUS_NOT_IMPLEMENTED:
|
|
|
+ return WSAEOPNOTSUPP;
|
|
|
+
|
|
|
+ case STATUS_ACCESS_DENIED:
|
|
|
+ return WSAEACCES;
|
|
|
+
|
|
|
+ default:
|
|
|
+ if ((status & (FACILITY_NTWIN32 << 16)) == (FACILITY_NTWIN32 << 16) &&
|
|
|
+ (status & (ERROR_SEVERITY_ERROR | ERROR_SEVERITY_WARNING)))
|
|
|
+ {
|
|
|
+ /* It's a windows error that has been previously mapped to an */
|
|
|
+ /* ntstatus code. */
|
|
|
+ return (DWORD)(status & 0xffff);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ /* The default fallback for unmappable ntstatus codes. */
|
|
|
+ return WSAEINVAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|