From cb43cb5c06aeff8f65d0194e279b9a2b04de9714 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 7 Mar 2018 03:07:42 +0100 Subject: [PATCH 01/28] ci: add experimental test environment with LSPs installed --- .appveyor.yml | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index e941ae1..a11891b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -70,17 +70,52 @@ environment: -DCMAKE_C_FLAGS=-m64 -DCMAKE_CXX_FLAGS=-m64 + - job: msvc-vs2013-x86-LSP + appveyor_build_worker_image: Visual Studio 2013 + cmake_options: -G "Visual Studio 12 2013" + lsp: true + appveyor_save_cache_on_error: true + + - job: msvc-vs2017-x64-LSP + appveyor_build_worker_image: Visual Studio 2017 + cmake_options: -G "Visual Studio 15 2017 Win64" + lsp: true + appveyor_save_cache_on_error: true + +matrix: + allow_failures: + - lsp: true + configuration: Debug clone_folder: c:\wepoll shallow_clone: true clone_depth: 1 +cache: + - c:\proxif + init: - cmd: set install: - - ps: Install-Product node 'Current' + - ps: >- + Install-Product node 'Current' + + if ($env:lsp) { + if (!(Test-Path c:\proxif\setup.exe)) { + $null = &mkdir -Force c:\proxif + Invoke-WebRequest -Uri http://www.proxifier.com/download/ProxifierSetup.exe -OutFile "c:\proxif\setup.exe" + } + + Start-Process -Wait c:\proxif\setup.exe -ArgumentList /silent,/norestart + + $catalog = &netsh winsock show catalog + echo $catalog + if (!($catalog -like "*Layered Chain Entry*")) { + throw "LSP not installed" + } + } - cmd: set path=%path:C:\Program Files\Git\usr\bin;=% - cmd: set path=%path%;%mingw_path% - cmd: set path=%path%;%vs_path% From 002c7f089969e3cf9c7716f25825e7669eaf0a6e Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 8 Mar 2018 21:20:38 +0100 Subject: [PATCH 02/28] src: add ws.c and ws.h for winsock-related stuff --- src/init.c | 17 +++-------------- src/ws.c | 14 ++++++++++++++ src/ws.h | 8 ++++++++ 3 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 src/ws.c create mode 100644 src/ws.h diff --git a/src/init.c b/src/init.c index 5f75421..3e03af6 100644 --- a/src/init.c +++ b/src/init.c @@ -1,27 +1,15 @@ #include #include "api.h" -#include "error.h" #include "init.h" #include "nt.h" #include "reflock.h" #include "util.h" -#include "win.h" +#include "ws.h" static bool _initialized = false; static INIT_ONCE _once = INIT_ONCE_STATIC_INIT; -static int _winsock_global_init(void) { - int r; - WSADATA wsa_data; - - r = WSAStartup(MAKEWORD(2, 2), &wsa_data); - if (r != 0) - return_error(-1); - - return 0; -} - static BOOL CALLBACK _init_once_callback(INIT_ONCE* once, void* parameter, void** context) { @@ -29,7 +17,8 @@ static BOOL CALLBACK _init_once_callback(INIT_ONCE* once, unused_var(parameter); unused_var(context); - if (_winsock_global_init() < 0 || nt_global_init() < 0 || + /* N.b. that initialization order matters here. */ + if (ws_global_init() < 0 || nt_global_init() < 0 || reflock_global_init() < 0 || api_global_init() < 0) return FALSE; diff --git a/src/ws.c b/src/ws.c new file mode 100644 index 0000000..4260b28 --- /dev/null +++ b/src/ws.c @@ -0,0 +1,14 @@ +#include "ws.h" +#include "error.h" +#include "win.h" + +int ws_global_init(void) { + int r; + WSADATA wsa_data; + + r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (r != 0) + return_error(-1); + + return 0; +} diff --git a/src/ws.h b/src/ws.h new file mode 100644 index 0000000..6be473c --- /dev/null +++ b/src/ws.h @@ -0,0 +1,8 @@ +#ifndef WEPOLL_WS_H_ +#define WEPOLL_WS_H_ + +#include "internal.h" + +WEPOLL_INTERNAL int ws_global_init(void); + +#endif /* WEPOLL_WS_H_ */ From 47497ee19d5d180045485e323ff66be819ba104b Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 8 Mar 2018 21:47:39 +0100 Subject: [PATCH 03/28] ws: report the correct error when WSAStartup() fails --- src/error.c | 7 ++++++- src/ws.c | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/error.c b/src/error.c index 6b81179..373fd41 100644 --- a/src/error.c +++ b/src/error.c @@ -87,6 +87,7 @@ X(WSAEFAULT, EFAULT) \ X(WSAEHOSTDOWN, EHOSTUNREACH) \ X(WSAEHOSTUNREACH, EHOSTUNREACH) \ + X(WSAEINPROGRESS, EBUSY) \ X(WSAEINTR, EINTR) \ X(WSAEINVAL, EINVAL) \ X(WSAEISCONN, EISCONN) \ @@ -98,9 +99,13 @@ X(WSAENOTCONN, ENOTCONN) \ X(WSAENOTSOCK, ENOTSOCK) \ X(WSAEOPNOTSUPP, EOPNOTSUPP) \ + X(WSAEPROCLIM, ENOMEM) \ X(WSAESHUTDOWN, EPIPE) \ X(WSAETIMEDOUT, ETIMEDOUT) \ - X(WSAEWOULDBLOCK, EWOULDBLOCK) + X(WSAEWOULDBLOCK, EWOULDBLOCK) \ + X(WSANOTINITIALISED, ENETDOWN) \ + X(WSASYSNOTREADY, ENETDOWN) \ + X(WSAVERNOTSUPPORTED, ENOSYS) errno_t err_map_win_error_to_errno(DWORD error) { switch (error) { diff --git a/src/ws.c b/src/ws.c index 4260b28..774a269 100644 --- a/src/ws.c +++ b/src/ws.c @@ -8,7 +8,7 @@ int ws_global_init(void) { r = WSAStartup(MAKEWORD(2, 2), &wsa_data); if (r != 0) - return_error(-1); + return_error(-1, r); return 0; } From 7fc24cef6440e5626f4b55eae4b459737de28788 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Fri, 9 Mar 2018 00:20:42 +0100 Subject: [PATCH 04/28] test: verify that IP and IPv6 sockets can coexist in the same epoll set --- test/test-mixed-socket-types.c | 56 ++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 test/test-mixed-socket-types.c diff --git a/test/test-mixed-socket-types.c b/test/test-mixed-socket-types.c new file mode 100644 index 0000000..c2cf5b0 --- /dev/null +++ b/test/test-mixed-socket-types.c @@ -0,0 +1,56 @@ +#include +#include + +#include "error.h" +#include "test-util.h" +#include "wepoll.h" +#include "win.h" + +static void create_and_add_socket(HANDLE epoll_hnd, + int af, + int type, + int protocol) { + SOCKET sock; + u_long one = 1; + int r; + struct epoll_event ev; + + sock = socket(af, type, protocol); + check(sock != INVALID_SOCKET); + + r = ioctlsocket(sock, FIONBIO, &one); + check(r == 0); + + ev.events = EPOLLOUT | EPOLLONESHOT; + ev.data.sock = sock; + r = epoll_ctl(epoll_hnd, EPOLL_CTL_ADD, sock, &ev); + check(r == 0); +} + +int main(void) { + HANDLE epoll_hnd; + int i, r; + struct epoll_event ev; + + epoll_hnd = epoll_create1(0); + check(epoll_hnd != NULL); + + create_and_add_socket(epoll_hnd, AF_INET, SOCK_DGRAM, IPPROTO_UDP); + create_and_add_socket(epoll_hnd, AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + + for (i = 0; i < 2; i++) { + r = epoll_wait(epoll_hnd, &ev, 1, -1); + check(r == 1); + + r = epoll_ctl(epoll_hnd, EPOLL_CTL_DEL, ev.data.sock, NULL); + check(r == 0); + + r = closesocket(ev.data.sock); + check(r == 0); + } + + r = epoll_close(epoll_hnd); + check(r == 0); + + return 0; +} From 075e1cef0b4abc876f784c53e5b470e0160b1c28 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Fri, 9 Mar 2018 00:27:36 +0100 Subject: [PATCH 05/28] port: remove useless and ineffective locking from ep_port_delete() --- src/port.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/port.c b/src/port.c index 62b76d8..60b4e52 100644 --- a/src/port.c +++ b/src/port.c @@ -90,10 +90,8 @@ int ep_port_delete(ep_port_t* port_info) { queue_node_t* queue_node; size_t i; - EnterCriticalSection(&port_info->lock); - - if (port_info->iocp != NULL) - _ep_port_close_iocp(port_info); + /* At this point the IOCP port should have been closed. */ + assert(port_info->iocp == NULL); while ((tree_node = tree_root(&port_info->sock_tree)) != NULL) { ep_sock_t* sock_info = container_of(tree_node, ep_sock_t, tree_node); @@ -111,8 +109,6 @@ int ep_port_delete(ep_port_t* port_info) { poll_group_allocator_delete(pga); } - LeaveCriticalSection(&port_info->lock); - DeleteCriticalSection(&port_info->lock); _ep_port_free(port_info); From e7e83853543ed4782f31d9d788f1d2c310420f59 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Fri, 9 Mar 2018 01:12:24 +0100 Subject: [PATCH 06/28] port: do not use separate poll groups for different socket types It appears that this was never necessary after all. --- src/afd.c | 34 ++++++++++++++-------------------- src/afd.h | 6 +++--- src/port.c | 31 ++++++++++--------------------- src/port.h | 7 ++----- src/sock.c | 7 ++----- 5 files changed, 31 insertions(+), 54 deletions(-) diff --git a/src/afd.c b/src/afd.c index 33abca5..4a635db 100644 --- a/src/afd.c +++ b/src/afd.c @@ -105,10 +105,9 @@ static SOCKET _afd_get_base_socket(SOCKET socket) { return base_socket; } -static ssize_t _afd_get_protocol_info(SOCKET socket, - WSAPROTOCOL_INFOW* protocol_info) { +static int _afd_get_protocol_info(SOCKET socket, + WSAPROTOCOL_INFOW* protocol_info) { int opt_len; - ssize_t id; size_t i; opt_len = sizeof *protocol_info; @@ -119,36 +118,31 @@ static ssize_t _afd_get_protocol_info(SOCKET socket, &opt_len) != 0) return_error(-1); - id = -1; for (i = 0; i < array_count(AFD_PROVIDER_GUID_LIST); i++) { if (memcmp(&protocol_info->ProviderId, &AFD_PROVIDER_GUID_LIST[i], sizeof protocol_info->ProviderId) == 0) { - id = i; - break; + return 0; } } - /* Check if the protocol uses an msafd socket. */ - if (id < 0) - return_error(-1, ERROR_DEVICE_FEATURE_NOT_SUPPORTED); - - return id; + /* Socket doesn't appear to be controlled by MSAFD. */ + return_error(-1, ERROR_DEVICE_FEATURE_NOT_SUPPORTED); } -WEPOLL_INTERNAL ssize_t afd_get_protocol(SOCKET socket, - SOCKET* afd_socket_out, - WSAPROTOCOL_INFOW* protocol_info) { - ssize_t id; +WEPOLL_INTERNAL int afd_get_protocol_info(SOCKET socket, + SOCKET* afd_socket_out, + WSAPROTOCOL_INFOW* protocol_info) { SOCKET afd_socket; + int r; /* Try to get protocol information, assuming that the given socket is an AFD * socket. This should almost always be the case, and if it is, that saves us * a call to WSAIoctl(). */ afd_socket = socket; - id = _afd_get_protocol_info(afd_socket, protocol_info); + r = _afd_get_protocol_info(afd_socket, protocol_info); - if (id < 0) { + if (r < 0) { /* If getting protocol information failed, it might be due to the socket * not being an AFD socket. If so, attempt to fetch the underlying base * socket, then try again to obtain protocol information. */ @@ -160,11 +154,11 @@ WEPOLL_INTERNAL ssize_t afd_get_protocol(SOCKET socket, if (afd_socket == INVALID_SOCKET || afd_socket == socket) return_error(-1, error); - id = _afd_get_protocol_info(afd_socket, protocol_info); - if (id < 0) + r = _afd_get_protocol_info(afd_socket, protocol_info); + if (r < 0) return -1; } *afd_socket_out = afd_socket; - return id; + return r; } diff --git a/src/afd.h b/src/afd.h index 4345174..fbbce33 100644 --- a/src/afd.h +++ b/src/afd.h @@ -56,9 +56,9 @@ WEPOLL_INTERNAL int afd_poll(SOCKET driver_socket, AFD_POLL_INFO* poll_info, OVERLAPPED* overlapped); -WEPOLL_INTERNAL ssize_t afd_get_protocol(SOCKET socket, - SOCKET* afd_socket_out, - WSAPROTOCOL_INFOW* protocol_info); +WEPOLL_INTERNAL int afd_get_protocol_info(SOCKET socket, + SOCKET* afd_socket_out, + WSAPROTOCOL_INFOW* protocol_info); /* clang-format off */ diff --git a/src/port.c b/src/port.c index 60b4e52..a721a3a 100644 --- a/src/port.c +++ b/src/port.c @@ -88,7 +88,6 @@ int ep_port_close(ep_port_t* port_info) { int ep_port_delete(ep_port_t* port_info) { tree_node_t* tree_node; queue_node_t* queue_node; - size_t i; /* At this point the IOCP port should have been closed. */ assert(port_info->iocp == NULL); @@ -103,11 +102,8 @@ int ep_port_delete(ep_port_t* port_info) { ep_sock_force_delete(port_info, sock_info); } - for (i = 0; i < array_count(port_info->poll_group_allocators); i++) { - poll_group_allocator_t* pga = port_info->poll_group_allocators[i]; - if (pga != NULL) - poll_group_allocator_delete(pga); - } + if (port_info->poll_group_allocator != NULL) + poll_group_allocator_delete(port_info->poll_group_allocator); DeleteCriticalSection(&port_info->lock); @@ -357,26 +353,19 @@ ep_sock_t* ep_port_find_socket(ep_port_t* port_info, SOCKET socket) { } static poll_group_allocator_t* _ep_port_get_poll_group_allocator( - ep_port_t* port_info, - size_t protocol_id, - const WSAPROTOCOL_INFOW* protocol_info) { - poll_group_allocator_t** pga; + ep_port_t* port_info, const WSAPROTOCOL_INFOW* protocol_info) { + if (port_info->poll_group_allocator == NULL) { + port_info->poll_group_allocator = + poll_group_allocator_new(port_info, protocol_info); + } - assert(protocol_id < array_count(port_info->poll_group_allocators)); - - pga = &port_info->poll_group_allocators[protocol_id]; - if (*pga == NULL) - *pga = poll_group_allocator_new(port_info, protocol_info); - - return *pga; + return port_info->poll_group_allocator; } poll_group_t* ep_port_acquire_poll_group( - ep_port_t* port_info, - size_t protocol_id, - const WSAPROTOCOL_INFOW* protocol_info) { + ep_port_t* port_info, const WSAPROTOCOL_INFOW* protocol_info) { poll_group_allocator_t* pga = - _ep_port_get_poll_group_allocator(port_info, protocol_id, protocol_info); + _ep_port_get_poll_group_allocator(port_info, protocol_info); return poll_group_acquire(pga); } diff --git a/src/port.h b/src/port.h index d9855e4..a7dea7e 100644 --- a/src/port.h +++ b/src/port.h @@ -16,8 +16,7 @@ typedef struct ep_sock ep_sock_t; typedef struct ep_port { HANDLE iocp; - poll_group_allocator_t* - poll_group_allocators[array_count(AFD_PROVIDER_GUID_LIST)]; + poll_group_allocator_t* poll_group_allocator; tree_t sock_tree; queue_t sock_update_queue; queue_t sock_deleted_queue; @@ -41,9 +40,7 @@ WEPOLL_INTERNAL int ep_port_ctl(ep_port_t* port_info, struct epoll_event* ev); WEPOLL_INTERNAL poll_group_t* ep_port_acquire_poll_group( - ep_port_t* port_info, - size_t protocol_id, - const WSAPROTOCOL_INFOW* protocol_info); + ep_port_t* port_info, const WSAPROTOCOL_INFOW* protocol_info); WEPOLL_INTERNAL void ep_port_release_poll_group(ep_port_t* port_info, poll_group_t* poll_group); diff --git a/src/sock.c b/src/sock.c index 8cdfb05..2b3b678 100644 --- a/src/sock.c +++ b/src/sock.c @@ -173,7 +173,6 @@ static int _ep_sock_cancel_poll(_ep_sock_private_t* sock_private) { ep_sock_t* ep_sock_new(ep_port_t* port_info, SOCKET socket) { SOCKET afd_socket; - ssize_t protocol_id; WSAPROTOCOL_INFOW protocol_info; poll_group_t* poll_group; _ep_sock_private_t* sock_private; @@ -181,12 +180,10 @@ ep_sock_t* ep_sock_new(ep_port_t* port_info, SOCKET socket) { if (socket == 0 || socket == INVALID_SOCKET) return_error(NULL, ERROR_INVALID_HANDLE); - protocol_id = afd_get_protocol(socket, &afd_socket, &protocol_info); - if (protocol_id < 0) + if (afd_get_protocol_info(socket, &afd_socket, &protocol_info) < 0) return NULL; - poll_group = - ep_port_acquire_poll_group(port_info, protocol_id, &protocol_info); + poll_group = ep_port_acquire_poll_group(port_info, &protocol_info); if (poll_group == NULL) return NULL; From 06342920d67d24b8bb9eaff69792f9cf4726d4e9 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 1 May 2018 20:38:25 +0200 Subject: [PATCH 07/28] tree: reduce code duplication --- src/tree.c | 219 ++++++++++++++++++++--------------------------------- 1 file changed, 83 insertions(+), 136 deletions(-) diff --git a/src/tree.c b/src/tree.c index 7d304ec..efd8439 100644 --- a/src/tree.c +++ b/src/tree.c @@ -5,50 +5,6 @@ #include "tree.h" -static void _tree_rotate_left(tree_t* tree, tree_node_t* node) { - tree_node_t* p = node; - tree_node_t* q = node->right; - tree_node_t* parent = p->parent; - - if (parent) { - if (parent->left == p) - parent->left = q; - else - parent->right = q; - } else { - tree->root = q; - } - - q->parent = parent; - p->parent = q; - p->right = q->left; - if (p->right) - p->right->parent = p; - q->left = p; -} - -static void _tree_rotate_right(tree_t* tree, tree_node_t* node) { - tree_node_t* p = node; - tree_node_t* q = node->left; - tree_node_t* parent = p->parent; - - if (parent) { - if (parent->left == p) - parent->left = q; - else - parent->right = q; - } else { - tree->root = q; - } - - q->parent = parent; - p->parent = q; - p->left = q->right; - if (p->left) - p->left->parent = p; - q->right = p; -} - void tree_init(tree_t* tree) { memset(tree, 0, sizeof *tree); } @@ -57,28 +13,66 @@ void tree_node_init(tree_node_t* node) { memset(node, 0, sizeof *node); } +#define _tree_rotate(cis, trans, tree, node) \ + do { \ + tree_node_t* p = node; \ + tree_node_t* q = node->trans; \ + tree_node_t* parent1 = p->parent; \ + \ + if (parent1) { \ + if (parent1->left == p) \ + parent1->left = q; \ + else \ + parent1->right = q; \ + } else { \ + tree->root = q; \ + } \ + \ + q->parent = parent1; \ + p->parent = q; \ + p->trans = q->cis; \ + if (p->trans) \ + p->trans->parent = p; \ + q->cis = p; \ + } while (0) + +#define _tree_add_insert_or_descend(side, parent, node) \ + if (parent->side) { \ + parent = parent->side; \ + } else { \ + parent->side = node; \ + break; \ + } + +#define _tree_add_fixup(cis, trans, tree, parent, node) \ + tree_node_t* grandparent = parent->parent; \ + tree_node_t* uncle = grandparent->trans; \ + \ + if (uncle && uncle->red) { \ + parent->red = uncle->red = false; \ + grandparent->red = true; \ + node = grandparent; \ + } else { \ + if (node == parent->trans) { \ + _tree_rotate(cis, trans, tree, parent); \ + node = parent; \ + parent = node->parent; \ + } \ + parent->red = false; \ + grandparent->red = true; \ + _tree_rotate(trans, cis, tree, grandparent); \ + } + int tree_add(tree_t* tree, tree_node_t* node, uintptr_t key) { tree_node_t* parent; - tree_node_t* grandparent; - tree_node_t* uncle; parent = tree->root; if (parent) { for (;;) { if (key < parent->key) { - if (parent->left) { - parent = parent->left; - } else { - parent->left = node; - break; - } + _tree_add_insert_or_descend(left, parent, node); } else if (key > parent->key) { - if (parent->right) { - parent = parent->right; - } else { - parent->right = node; - break; - } + _tree_add_insert_or_descend(right, parent, node); } else { return -1; } @@ -92,54 +86,48 @@ int tree_add(tree_t* tree, tree_node_t* node, uintptr_t key) { node->parent = parent; node->red = true; - while (parent && parent->red) { - grandparent = parent->parent; - if (parent == grandparent->left) { - uncle = grandparent->right; - if (uncle && uncle->red) { - parent->red = uncle->red = false; - grandparent->red = true; - node = grandparent; - } else { - if (node == parent->right) { - _tree_rotate_left(tree, parent); - node = parent; - parent = node->parent; - } - parent->red = false; - grandparent->red = true; - _tree_rotate_right(tree, grandparent); - } + for (; parent && parent->red; parent = node->parent) { + if (parent == parent->parent->left) { + _tree_add_fixup(left, right, tree, parent, node); } else { - uncle = grandparent->left; - if (uncle && uncle->red) { - parent->red = uncle->red = false; - grandparent->red = true; - node = grandparent; - } else { - if (node == parent->left) { - _tree_rotate_right(tree, parent); - node = parent; - parent = node->parent; - } - parent->red = false; - grandparent->red = true; - _tree_rotate_left(tree, grandparent); - } + _tree_add_fixup(right, left, tree, parent, node); } - parent = node->parent; } tree->root->red = false; return 0; } +#define _tree_del_fixup(cis, trans, tree, node) \ + tree_node_t* sibling = parent->trans; \ + \ + if (sibling->red) { \ + sibling->red = false; \ + parent->red = true; \ + _tree_rotate(cis, trans, tree, parent); \ + sibling = parent->trans; \ + } \ + if ((sibling->left && sibling->left->red) || \ + (sibling->right && sibling->right->red)) { \ + if (!sibling->trans || !sibling->trans->red) { \ + sibling->cis->red = false; \ + sibling->red = true; \ + _tree_rotate(trans, cis, tree, sibling); \ + sibling = parent->trans; \ + } \ + sibling->red = parent->red; \ + parent->red = sibling->trans->red = false; \ + _tree_rotate(cis, trans, tree, parent); \ + node = tree->root; \ + break; \ + } \ + sibling->red = true; + void tree_del(tree_t* tree, tree_node_t* node) { tree_node_t* parent = node->parent; tree_node_t* left = node->left; tree_node_t* right = node->right; tree_node_t* next; - tree_node_t* sibling; bool red; if (!left) { @@ -196,51 +184,10 @@ void tree_del(tree_t* tree, tree_node_t* node) { if (node == tree->root) break; if (node == parent->left) { - sibling = parent->right; - if (sibling->red) { - sibling->red = false; - parent->red = true; - _tree_rotate_left(tree, parent); - sibling = parent->right; - } - if ((sibling->left && sibling->left->red) || - (sibling->right && sibling->right->red)) { - if (!sibling->right || !sibling->right->red) { - sibling->left->red = false; - sibling->red = true; - _tree_rotate_right(tree, sibling); - sibling = parent->right; - } - sibling->red = parent->red; - parent->red = sibling->right->red = false; - _tree_rotate_left(tree, parent); - node = tree->root; - break; - } + _tree_del_fixup(left, right, tree, node); } else { - sibling = parent->left; - if (sibling->red) { - sibling->red = false; - parent->red = true; - _tree_rotate_right(tree, parent); - sibling = parent->left; - } - if ((sibling->left && sibling->left->red) || - (sibling->right && sibling->right->red)) { - if (!sibling->left || !sibling->left->red) { - sibling->right->red = false; - sibling->red = true; - _tree_rotate_left(tree, sibling); - sibling = parent->left; - } - sibling->red = parent->red; - parent->red = sibling->left->red = false; - _tree_rotate_right(tree, parent); - node = tree->root; - break; - } + _tree_del_fixup(right, left, tree, node); } - sibling->red = true; node = parent; parent = parent->parent; } while (!node->red); From 9d61ddfddb3151e7de24f30386f9099f7be330c2 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 1 May 2018 22:11:13 +0200 Subject: [PATCH 08/28] reflock: remove _sync_sub_and_fetch() --- src/reflock.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/reflock.c b/src/reflock.c index a8a51f9..3c3db68 100644 --- a/src/reflock.c +++ b/src/reflock.c @@ -50,12 +50,6 @@ static inline uint32_t _sync_add_and_fetch(volatile uint32_t* target, return InterlockedAdd((volatile long*) target, value); } -static inline uint32_t _sync_sub_and_fetch(volatile uint32_t* target, - uint32_t value) { - uint32_t add_value = -(int32_t) value; - return _sync_add_and_fetch(target, add_value); -} - static inline uint32_t _sync_fetch_and_set(volatile uint32_t* target, uint32_t value) { static_assert(sizeof(*target) == sizeof(long), ""); @@ -69,7 +63,7 @@ void reflock_ref(reflock_t* reflock) { } void reflock_unref(reflock_t* reflock) { - uint32_t state = _sync_sub_and_fetch(&reflock->state, _REF); + uint32_t state = _sync_add_and_fetch(&reflock->state, -(int32_t) _REF); uint32_t ref_count = state & _REF_MASK; uint32_t destroy = state & _DESTROY_MASK; From de68d70f9cf731d25670b46b40f3445199d0a495 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 1 May 2018 22:44:32 +0200 Subject: [PATCH 09/28] port: fix unlikely bug in _ep_port_wait() time-out behavior --- src/port.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/port.c b/src/port.c index a721a3a..938db25 100644 --- a/src/port.c +++ b/src/port.c @@ -225,7 +225,7 @@ int ep_port_wait(ep_port_t* port_info, /* Dequeue completion packets until either at least one interesting event * has been discovered, or the timeout is reached. */ - do { + for (;;) { ULONGLONG now; result = @@ -234,16 +234,20 @@ int ep_port_wait(ep_port_t* port_info, break; /* Result, error, or time-out. */ if (timeout < 0) - continue; /* _ep_port_wait() never times out. */ + continue; /* When timeout is negative, never time out. */ - /* Check for time-out. */ + /* Update time. */ now = GetTickCount64(); - if (now >= due) - break; - /* Recompute timeout. */ + /* Do not allow the due time to be in the past. */ + if (now >= due) { + SetLastError(WAIT_TIMEOUT); + break; + } + + /* Recompute time-out argument for GetQueuedCompletionStatus. */ gqcs_timeout = (DWORD)(due - now); - } while (gqcs_timeout > 0); + } _ep_port_update_events_if_polling(port_info); From ba343a08987aa8c80f9ad15be889ede7286ac569 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Tue, 1 May 2018 23:50:59 +0200 Subject: [PATCH 10/28] afd: move afd provider guid list from header to c file --- src/afd.c | 20 ++++++++++++++++++-- src/afd.h | 18 ------------------ 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/afd.c b/src/afd.c index 4a635db..fb8d5b4 100644 --- a/src/afd.c +++ b/src/afd.c @@ -17,6 +17,22 @@ #define SIO_BASE_HANDLE 0x48000022 #endif +/* clang-format off */ +static const GUID _AFD_PROVIDER_GUID_LIST[] = { + /* MSAFD Tcpip [TCP+UDP+RAW / IP] */ + {0xe70f1aa0, 0xab8b, 0x11cf, + {0x8c, 0xa3, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}, + /* MSAFD Tcpip [TCP+UDP+RAW / IPv6] */ + {0xf9eab0c0, 0x26d4, 0x11d0, + {0xbb, 0xbf, 0x00, 0xaa, 0x00, 0x6c, 0x34, 0xe4}}, + /* MSAFD RfComm [Bluetooth] */ + {0x9fc48064, 0x7298, 0x43e4, + {0xb7, 0xbd, 0x18, 0x1f, 0x20, 0x89, 0x79, 0x2a}}, + /* MSAFD Irda [IrDA] */ + {0x3972523d, 0x2af1, 0x11d1, + {0xb6, 0x55, 0x00, 0x80, 0x5f, 0x36, 0x42, 0xcc}}}; +/* clang-format on */ + int afd_poll(SOCKET driver_socket, AFD_POLL_INFO* poll_info, OVERLAPPED* overlapped) { @@ -118,9 +134,9 @@ static int _afd_get_protocol_info(SOCKET socket, &opt_len) != 0) return_error(-1); - for (i = 0; i < array_count(AFD_PROVIDER_GUID_LIST); i++) { + for (i = 0; i < array_count(_AFD_PROVIDER_GUID_LIST); i++) { if (memcmp(&protocol_info->ProviderId, - &AFD_PROVIDER_GUID_LIST[i], + &_AFD_PROVIDER_GUID_LIST[i], sizeof protocol_info->ProviderId) == 0) { return 0; } diff --git a/src/afd.h b/src/afd.h index fbbce33..12b3e0e 100644 --- a/src/afd.h +++ b/src/afd.h @@ -60,22 +60,4 @@ WEPOLL_INTERNAL int afd_get_protocol_info(SOCKET socket, SOCKET* afd_socket_out, WSAPROTOCOL_INFOW* protocol_info); -/* clang-format off */ - -static const GUID AFD_PROVIDER_GUID_LIST[] = { - /* MSAFD Tcpip [TCP+UDP+RAW / IP] */ - {0xe70f1aa0, 0xab8b, 0x11cf, - {0x8c, 0xa3, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}, - /* MSAFD Tcpip [TCP+UDP+RAW / IPv6] */ - {0xf9eab0c0, 0x26d4, 0x11d0, - {0xbb, 0xbf, 0x00, 0xaa, 0x00, 0x6c, 0x34, 0xe4}}, - /* MSAFD RfComm [Bluetooth] */ - {0x9fc48064, 0x7298, 0x43e4, - {0xb7, 0xbd, 0x18, 0x1f, 0x20, 0x89, 0x79, 0x2a}}, - /* MSAFD Irda [IrDA] */ - {0x3972523d, 0x2af1, 0x11d1, - {0xb6, 0x55, 0x00, 0x80, 0x5f, 0x36, 0x42, 0xcc}}}; - -/* clang-format on */ - #endif /* WEPOLL_AFD_H_ */ From c69f361564dad42d7a854d36e8395f2a48a4b9ac Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 02:04:10 +0200 Subject: [PATCH 11/28] ws: move ws_get_base_socket() from afd.c to ws.c --- src/afd.c | 25 ++----------------------- src/ws.c | 24 +++++++++++++++++++++++- src/ws.h | 3 +++ 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/afd.c b/src/afd.c index fb8d5b4..29494a2 100644 --- a/src/afd.c +++ b/src/afd.c @@ -3,6 +3,7 @@ #include "nt.h" #include "util.h" #include "win.h" +#include "ws.h" #define FILE_DEVICE_NETWORK 0x00000012 #define METHOD_BUFFERED 0 @@ -13,10 +14,6 @@ #define IOCTL_AFD_POLL _AFD_CONTROL_CODE(AFD_POLL, METHOD_BUFFERED) -#ifndef SIO_BASE_HANDLE -#define SIO_BASE_HANDLE 0x48000022 -#endif - /* clang-format off */ static const GUID _AFD_PROVIDER_GUID_LIST[] = { /* MSAFD Tcpip [TCP+UDP+RAW / IP] */ @@ -103,24 +100,6 @@ int afd_poll(SOCKET driver_socket, return_error(-1, RtlNtStatusToDosError(status)); } -static SOCKET _afd_get_base_socket(SOCKET socket) { - SOCKET base_socket; - DWORD bytes; - - if (WSAIoctl(socket, - SIO_BASE_HANDLE, - NULL, - 0, - &base_socket, - sizeof base_socket, - &bytes, - NULL, - NULL) == SOCKET_ERROR) - return_error(INVALID_SOCKET); - - return base_socket; -} - static int _afd_get_protocol_info(SOCKET socket, WSAPROTOCOL_INFOW* protocol_info) { int opt_len; @@ -166,7 +145,7 @@ WEPOLL_INTERNAL int afd_get_protocol_info(SOCKET socket, if (error != ERROR_DEVICE_FEATURE_NOT_SUPPORTED) return -1; - afd_socket = _afd_get_base_socket(socket); + afd_socket = ws_get_base_socket(socket); if (afd_socket == INVALID_SOCKET || afd_socket == socket) return_error(-1, error); diff --git a/src/ws.c b/src/ws.c index 774a269..bcc63aa 100644 --- a/src/ws.c +++ b/src/ws.c @@ -1,6 +1,10 @@ -#include "ws.h" #include "error.h" #include "win.h" +#include "ws.h" + +#ifndef SIO_BASE_HANDLE +#define SIO_BASE_HANDLE 0x48000022 +#endif int ws_global_init(void) { int r; @@ -12,3 +16,21 @@ int ws_global_init(void) { return 0; } + +SOCKET ws_get_base_socket(SOCKET socket) { + SOCKET base_socket; + DWORD bytes; + + if (WSAIoctl(socket, + SIO_BASE_HANDLE, + NULL, + 0, + &base_socket, + sizeof base_socket, + &bytes, + NULL, + NULL) == SOCKET_ERROR) + return_error(INVALID_SOCKET); + + return base_socket; +} diff --git a/src/ws.h b/src/ws.h index 6be473c..86d50cd 100644 --- a/src/ws.h +++ b/src/ws.h @@ -2,7 +2,10 @@ #define WEPOLL_WS_H_ #include "internal.h" +#include "win.h" WEPOLL_INTERNAL int ws_global_init(void); +WEPOLL_INTERNAL SOCKET ws_get_base_socket(SOCKET socket); + #endif /* WEPOLL_WS_H_ */ From 2789bad793b276632c17b32f7a9a6978c58aa04b Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 02:16:09 +0200 Subject: [PATCH 12/28] afd: retrieve protocol info for afd driver socket on startup --- src/afd.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++ src/afd.h | 5 +++ src/init.c | 3 +- src/poll-group.c | 38 ++---------------- src/poll-group.h | 2 +- src/port.c | 11 ++---- src/port.h | 3 +- src/sock.c | 2 +- src/ws.c | 33 ++++++++++++++++ src/ws.h | 2 + 10 files changed, 152 insertions(+), 47 deletions(-) diff --git a/src/afd.c b/src/afd.c index 29494a2..a426701 100644 --- a/src/afd.c +++ b/src/afd.c @@ -1,3 +1,6 @@ +#include +#include + #include "afd.h" #include "error.h" #include "nt.h" @@ -14,6 +17,8 @@ #define IOCTL_AFD_POLL _AFD_CONTROL_CODE(AFD_POLL, METHOD_BUFFERED) +#define _AFD_ANY_PROTOCOL -1 + /* clang-format off */ static const GUID _AFD_PROVIDER_GUID_LIST[] = { /* MSAFD Tcpip [TCP+UDP+RAW / IP] */ @@ -30,6 +35,101 @@ static const GUID _AFD_PROVIDER_GUID_LIST[] = { {0xb6, 0x55, 0x00, 0x80, 0x5f, 0x36, 0x42, 0xcc}}}; /* clang-format on */ +/* This protocol info record is used by afd_create_driver_socket() to create + * sockets that can be used as the first argument to afd_poll(). It is + * populated on startup by afd_global_init(). + */ +static WSAPROTOCOL_INFOW _afd_driver_socket_template; + +static const WSAPROTOCOL_INFOW* _afd_find_protocol_info( + const WSAPROTOCOL_INFOW* infos, size_t infos_count, int protocol_id) { + size_t i, j; + + for (i = 0; i < infos_count; i++) { + const WSAPROTOCOL_INFOW* info = &infos[i]; + + /* Apply protocol id filter. */ + if (protocol_id != _AFD_ANY_PROTOCOL && protocol_id != info->iProtocol) + continue; + + /* Filter out non-MSAFD protocols. */ + for (j = 0; j < array_count(_AFD_PROVIDER_GUID_LIST); j++) { + if (memcmp(&info->ProviderId, + &_AFD_PROVIDER_GUID_LIST[j], + sizeof info->ProviderId) == 0) + return info; + } + } + + return NULL; /* Not found. */ +} + +int afd_global_init(void) { + WSAPROTOCOL_INFOW* infos; + ssize_t infos_count; + const WSAPROTOCOL_INFOW* afd_info; + + /* Load the winsock catalog. */ + infos_count = ws_get_protocol_catalog(&infos); + if (infos_count < 0) + return_error(-1); + + /* Find a WSAPROTOCOL_INDOW structure that we can use to create an MSAFD + * socket. Preferentially we pick a UDP socket, otherwise try TCP or any + * other type. + */ + do { + afd_info = _afd_find_protocol_info(infos, infos_count, IPPROTO_UDP); + if (afd_info != NULL) + break; + + afd_info = _afd_find_protocol_info(infos, infos_count, IPPROTO_TCP); + if (afd_info != NULL) + break; + + afd_info = _afd_find_protocol_info(infos, infos_count, _AFD_ANY_PROTOCOL); + if (afd_info != NULL) + break; + + free(infos); + return_error(-1, WSAENETDOWN); /* No suitable protocol found. */ + } while (0); + + /* Copy found protocol information from the catalog to a static buffer. */ + _afd_driver_socket_template = *afd_info; + + free(infos); + return 0; +} + +int afd_create_driver_socket(HANDLE iocp, SOCKET* driver_socket_out) { + SOCKET socket; + + socket = WSASocketW(_afd_driver_socket_template.iAddressFamily, + _afd_driver_socket_template.iSocketType, + _afd_driver_socket_template.iProtocol, + &_afd_driver_socket_template, + 0, + WSA_FLAG_OVERLAPPED); + if (socket == INVALID_SOCKET) + return_error(-1); + + /* TODO: use WSA_FLAG_NOINHERIT on Windows versions that support it. */ + if (!SetHandleInformation((HANDLE) socket, HANDLE_FLAG_INHERIT, 0)) + goto error; + + if (CreateIoCompletionPort((HANDLE) socket, iocp, 0, 0) == NULL) + goto error; + + *driver_socket_out = socket; + return 0; + +error:; + DWORD error = GetLastError(); + closesocket(socket); + return_error(-1, error); +} + int afd_poll(SOCKET driver_socket, AFD_POLL_INFO* poll_info, OVERLAPPED* overlapped) { diff --git a/src/afd.h b/src/afd.h index 12b3e0e..07e65fd 100644 --- a/src/afd.h +++ b/src/afd.h @@ -52,6 +52,11 @@ typedef struct _AFD_POLL_INFO { AFD_POLL_HANDLE_INFO Handles[1]; } AFD_POLL_INFO, *PAFD_POLL_INFO; +WEPOLL_INTERNAL int afd_global_init(void); + +WEPOLL_INTERNAL int afd_create_driver_socket(HANDLE iocp, + SOCKET* driver_socket_out); + WEPOLL_INTERNAL int afd_poll(SOCKET driver_socket, AFD_POLL_INFO* poll_info, OVERLAPPED* overlapped); diff --git a/src/init.c b/src/init.c index 3e03af6..199ee90 100644 --- a/src/init.c +++ b/src/init.c @@ -1,5 +1,6 @@ #include +#include "afd.h" #include "api.h" #include "init.h" #include "nt.h" @@ -18,7 +19,7 @@ static BOOL CALLBACK _init_once_callback(INIT_ONCE* once, unused_var(context); /* N.b. that initialization order matters here. */ - if (ws_global_init() < 0 || nt_global_init() < 0 || + if (ws_global_init() < 0 || nt_global_init() < 0 || afd_global_init() < 0 || reflock_global_init() < 0 || api_global_init() < 0) return FALSE; diff --git a/src/poll-group.c b/src/poll-group.c index 076b2f4..c6423d4 100644 --- a/src/poll-group.c +++ b/src/poll-group.c @@ -12,7 +12,6 @@ static const size_t _POLL_GROUP_MAX_SIZE = 32; typedef struct poll_group_allocator { ep_port_t* port_info; queue_t poll_group_queue; - WSAPROTOCOL_INFOW protocol_info; } poll_group_allocator_t; typedef struct poll_group { @@ -22,35 +21,6 @@ typedef struct poll_group { size_t group_size; } poll_group_t; -static int _poll_group_create_socket(poll_group_t* poll_group, - WSAPROTOCOL_INFOW* protocol_info, - HANDLE iocp) { - SOCKET socket; - - socket = WSASocketW(protocol_info->iAddressFamily, - protocol_info->iSocketType, - protocol_info->iProtocol, - protocol_info, - 0, - WSA_FLAG_OVERLAPPED); - if (socket == INVALID_SOCKET) - return_error(-1); - - if (!SetHandleInformation((HANDLE) socket, HANDLE_FLAG_INHERIT, 0)) - goto error; - - if (CreateIoCompletionPort((HANDLE) socket, iocp, 0, 0) == NULL) - goto error; - - poll_group->socket = socket; - return 0; - -error:; - DWORD error = GetLastError(); - closesocket(socket); - return_error(-1, error); -} - static poll_group_t* _poll_group_new(poll_group_allocator_t* pga) { poll_group_t* poll_group = malloc(sizeof *poll_group); if (poll_group == NULL) @@ -61,8 +31,8 @@ static poll_group_t* _poll_group_new(poll_group_allocator_t* pga) { queue_node_init(&poll_group->queue_node); poll_group->allocator = pga; - if (_poll_group_create_socket( - poll_group, &pga->protocol_info, pga->port_info->iocp) < 0) { + if (afd_create_driver_socket(pga->port_info->iocp, &poll_group->socket) < + 0) { free(poll_group); return NULL; } @@ -83,15 +53,13 @@ SOCKET poll_group_get_socket(poll_group_t* poll_group) { return poll_group->socket; } -poll_group_allocator_t* poll_group_allocator_new( - ep_port_t* port_info, const WSAPROTOCOL_INFOW* protocol_info) { +poll_group_allocator_t* poll_group_allocator_new(ep_port_t* port_info) { poll_group_allocator_t* pga = malloc(sizeof *pga); if (pga == NULL) return_error(NULL, ERROR_NOT_ENOUGH_MEMORY); queue_init(&pga->poll_group_queue); pga->port_info = port_info; - pga->protocol_info = *protocol_info; return pga; } diff --git a/src/poll-group.h b/src/poll-group.h index 51889d2..7345f32 100644 --- a/src/poll-group.h +++ b/src/poll-group.h @@ -11,7 +11,7 @@ typedef struct poll_group_allocator poll_group_allocator_t; typedef struct poll_group poll_group_t; WEPOLL_INTERNAL poll_group_allocator_t* poll_group_allocator_new( - ep_port_t* port_info, const WSAPROTOCOL_INFOW* protocol_info); + ep_port_t* port_info); WEPOLL_INTERNAL void poll_group_allocator_delete(poll_group_allocator_t* pga); WEPOLL_INTERNAL poll_group_t* poll_group_acquire(poll_group_allocator_t* pga); diff --git a/src/port.c b/src/port.c index 938db25..c05aafe 100644 --- a/src/port.c +++ b/src/port.c @@ -357,19 +357,16 @@ ep_sock_t* ep_port_find_socket(ep_port_t* port_info, SOCKET socket) { } static poll_group_allocator_t* _ep_port_get_poll_group_allocator( - ep_port_t* port_info, const WSAPROTOCOL_INFOW* protocol_info) { + ep_port_t* port_info) { if (port_info->poll_group_allocator == NULL) { - port_info->poll_group_allocator = - poll_group_allocator_new(port_info, protocol_info); + port_info->poll_group_allocator = poll_group_allocator_new(port_info); } return port_info->poll_group_allocator; } -poll_group_t* ep_port_acquire_poll_group( - ep_port_t* port_info, const WSAPROTOCOL_INFOW* protocol_info) { - poll_group_allocator_t* pga = - _ep_port_get_poll_group_allocator(port_info, protocol_info); +poll_group_t* ep_port_acquire_poll_group(ep_port_t* port_info) { + poll_group_allocator_t* pga = _ep_port_get_poll_group_allocator(port_info); return poll_group_acquire(pga); } diff --git a/src/port.h b/src/port.h index a7dea7e..4ff7c4c 100644 --- a/src/port.h +++ b/src/port.h @@ -39,8 +39,7 @@ WEPOLL_INTERNAL int ep_port_ctl(ep_port_t* port_info, SOCKET sock, struct epoll_event* ev); -WEPOLL_INTERNAL poll_group_t* ep_port_acquire_poll_group( - ep_port_t* port_info, const WSAPROTOCOL_INFOW* protocol_info); +WEPOLL_INTERNAL poll_group_t* ep_port_acquire_poll_group(ep_port_t* port_info); WEPOLL_INTERNAL void ep_port_release_poll_group(ep_port_t* port_info, poll_group_t* poll_group); diff --git a/src/sock.c b/src/sock.c index 2b3b678..be5c45d 100644 --- a/src/sock.c +++ b/src/sock.c @@ -183,7 +183,7 @@ ep_sock_t* ep_sock_new(ep_port_t* port_info, SOCKET socket) { if (afd_get_protocol_info(socket, &afd_socket, &protocol_info) < 0) return NULL; - poll_group = ep_port_acquire_poll_group(port_info, &protocol_info); + poll_group = ep_port_acquire_poll_group(port_info); if (poll_group == NULL) return NULL; diff --git a/src/ws.c b/src/ws.c index bcc63aa..e100de5 100644 --- a/src/ws.c +++ b/src/ws.c @@ -1,4 +1,8 @@ +#include +#include + #include "error.h" +#include "util.h" #include "win.h" #include "ws.h" @@ -6,6 +10,8 @@ #define SIO_BASE_HANDLE 0x48000022 #endif +#define _WS_INITIAL_CATALOG_BUFFER_SIZE 0x4000 /* 16kb. */ + int ws_global_init(void) { int r; WSADATA wsa_data; @@ -34,3 +40,30 @@ SOCKET ws_get_base_socket(SOCKET socket) { return base_socket; } + +/* Retrieves a copy of the winsock catalog. + * The infos pointer must be released by the caller with free(). + */ +ssize_t ws_get_protocol_catalog(WSAPROTOCOL_INFOW** infos_out) { + DWORD buffer_size = _WS_INITIAL_CATALOG_BUFFER_SIZE; + int count; + WSAPROTOCOL_INFOW* infos; + + for (;;) { + infos = malloc(buffer_size); + if (infos == NULL) + return_error(-1, ERROR_NOT_ENOUGH_MEMORY); + + count = WSAEnumProtocolsW(NULL, infos, &buffer_size); + if (count == SOCKET_ERROR) { + free(infos); + if (WSAGetLastError() == WSAENOBUFS) + continue; /* Try again with bigger buffer size. */ + else + return_error(-1); + } + + *infos_out = infos; + return count; + } +} diff --git a/src/ws.h b/src/ws.h index 86d50cd..7ab527a 100644 --- a/src/ws.h +++ b/src/ws.h @@ -2,10 +2,12 @@ #define WEPOLL_WS_H_ #include "internal.h" +#include "util.h" #include "win.h" WEPOLL_INTERNAL int ws_global_init(void); WEPOLL_INTERNAL SOCKET ws_get_base_socket(SOCKET socket); +WEPOLL_INTERNAL ssize_t ws_get_protocol_catalog(WSAPROTOCOL_INFOW** infos_out); #endif /* WEPOLL_WS_H_ */ From a54e813d2fc0dbc3767edbb3465bca5d17565691 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 02:24:33 +0200 Subject: [PATCH 13/28] sock: remove unnecessary header include --- src/sock.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sock.c b/src/sock.c index be5c45d..183e9ad 100644 --- a/src/sock.c +++ b/src/sock.c @@ -8,7 +8,6 @@ #include "poll-group.h" #include "port.h" #include "sock.h" -#include "wepoll.h" #define _KNOWN_EPOLL_EVENTS \ (EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | \ From 607ed772167a267d5a4dd8d758466496174a746b Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 02:24:19 +0200 Subject: [PATCH 14/28] sock: do not retrieve winsock protocol info for every socket --- src/afd.c | 58 ------------------------------------------------------ src/afd.h | 4 ---- src/sock.c | 5 +++-- 3 files changed, 3 insertions(+), 64 deletions(-) diff --git a/src/afd.c b/src/afd.c index a426701..5a070ef 100644 --- a/src/afd.c +++ b/src/afd.c @@ -199,61 +199,3 @@ int afd_poll(SOCKET driver_socket, else return_error(-1, RtlNtStatusToDosError(status)); } - -static int _afd_get_protocol_info(SOCKET socket, - WSAPROTOCOL_INFOW* protocol_info) { - int opt_len; - size_t i; - - opt_len = sizeof *protocol_info; - if (getsockopt(socket, - SOL_SOCKET, - SO_PROTOCOL_INFOW, - (char*) protocol_info, - &opt_len) != 0) - return_error(-1); - - for (i = 0; i < array_count(_AFD_PROVIDER_GUID_LIST); i++) { - if (memcmp(&protocol_info->ProviderId, - &_AFD_PROVIDER_GUID_LIST[i], - sizeof protocol_info->ProviderId) == 0) { - return 0; - } - } - - /* Socket doesn't appear to be controlled by MSAFD. */ - return_error(-1, ERROR_DEVICE_FEATURE_NOT_SUPPORTED); -} - -WEPOLL_INTERNAL int afd_get_protocol_info(SOCKET socket, - SOCKET* afd_socket_out, - WSAPROTOCOL_INFOW* protocol_info) { - SOCKET afd_socket; - int r; - - /* Try to get protocol information, assuming that the given socket is an AFD - * socket. This should almost always be the case, and if it is, that saves us - * a call to WSAIoctl(). */ - afd_socket = socket; - r = _afd_get_protocol_info(afd_socket, protocol_info); - - if (r < 0) { - /* If getting protocol information failed, it might be due to the socket - * not being an AFD socket. If so, attempt to fetch the underlying base - * socket, then try again to obtain protocol information. */ - DWORD error = GetLastError(); - if (error != ERROR_DEVICE_FEATURE_NOT_SUPPORTED) - return -1; - - afd_socket = ws_get_base_socket(socket); - if (afd_socket == INVALID_SOCKET || afd_socket == socket) - return_error(-1, error); - - r = _afd_get_protocol_info(afd_socket, protocol_info); - if (r < 0) - return -1; - } - - *afd_socket_out = afd_socket; - return r; -} diff --git a/src/afd.h b/src/afd.h index 07e65fd..21e6f73 100644 --- a/src/afd.h +++ b/src/afd.h @@ -61,8 +61,4 @@ WEPOLL_INTERNAL int afd_poll(SOCKET driver_socket, AFD_POLL_INFO* poll_info, OVERLAPPED* overlapped); -WEPOLL_INTERNAL int afd_get_protocol_info(SOCKET socket, - SOCKET* afd_socket_out, - WSAPROTOCOL_INFOW* protocol_info); - #endif /* WEPOLL_AFD_H_ */ diff --git a/src/sock.c b/src/sock.c index 183e9ad..02d398d 100644 --- a/src/sock.c +++ b/src/sock.c @@ -8,6 +8,7 @@ #include "poll-group.h" #include "port.h" #include "sock.h" +#include "ws.h" #define _KNOWN_EPOLL_EVENTS \ (EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | \ @@ -172,14 +173,14 @@ static int _ep_sock_cancel_poll(_ep_sock_private_t* sock_private) { ep_sock_t* ep_sock_new(ep_port_t* port_info, SOCKET socket) { SOCKET afd_socket; - WSAPROTOCOL_INFOW protocol_info; poll_group_t* poll_group; _ep_sock_private_t* sock_private; if (socket == 0 || socket == INVALID_SOCKET) return_error(NULL, ERROR_INVALID_HANDLE); - if (afd_get_protocol_info(socket, &afd_socket, &protocol_info) < 0) + afd_socket = ws_get_base_socket(socket); + if (afd_socket == INVALID_SOCKET) return NULL; poll_group = ep_port_acquire_poll_group(port_info); From 6a932e0dafbded5249dcea4f71af591a3e601297 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 02:26:47 +0200 Subject: [PATCH 15/28] sock: rename 'afd_socket' to 'base_socket' --- src/sock.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sock.c b/src/sock.c index 02d398d..dfd40ee 100644 --- a/src/sock.c +++ b/src/sock.c @@ -29,7 +29,7 @@ typedef struct _ep_sock_private { ep_sock_t pub; _poll_req_t poll_req; poll_group_t* poll_group; - SOCKET afd_socket; + SOCKET base_socket; epoll_data_t user_data; uint32_t user_events; uint32_t pending_events; @@ -172,15 +172,15 @@ static int _ep_sock_cancel_poll(_ep_sock_private_t* sock_private) { } ep_sock_t* ep_sock_new(ep_port_t* port_info, SOCKET socket) { - SOCKET afd_socket; + SOCKET base_socket; poll_group_t* poll_group; _ep_sock_private_t* sock_private; if (socket == 0 || socket == INVALID_SOCKET) return_error(NULL, ERROR_INVALID_HANDLE); - afd_socket = ws_get_base_socket(socket); - if (afd_socket == INVALID_SOCKET) + base_socket = ws_get_base_socket(socket); + if (base_socket == INVALID_SOCKET) return NULL; poll_group = ep_port_acquire_poll_group(port_info); @@ -193,7 +193,7 @@ ep_sock_t* ep_sock_new(ep_port_t* port_info, SOCKET socket) { memset(sock_private, 0, sizeof *sock_private); - sock_private->afd_socket = afd_socket; + sock_private->base_socket = base_socket; sock_private->poll_group = poll_group; tree_node_init(&sock_private->pub.tree_node); @@ -299,7 +299,7 @@ int ep_sock_update(ep_port_t* port_info, ep_sock_t* sock_info) { if (_poll_req_submit(&sock_private->poll_req, sock_private->user_events, - sock_private->afd_socket, + sock_private->base_socket, driver_socket) < 0) { if (GetLastError() == ERROR_INVALID_HANDLE) /* The socket is broken. It will be dropped from the epoll set. */ From dfeefa878098578c463a0013f42b35956152ce4e Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 02:25:00 +0200 Subject: [PATCH 16/28] error: remove error mapping for ERROR_DEVICE_FEATURE_NOT_SUPPORTED --- src/error.c | 203 ++++++++++++++++++++++++++-------------------------- src/win.h | 5 -- 2 files changed, 101 insertions(+), 107 deletions(-) diff --git a/src/error.c b/src/error.c index 373fd41..4bda854 100644 --- a/src/error.c +++ b/src/error.c @@ -3,108 +3,107 @@ #include "error.h" #include "win.h" -#define _ERROR_ERRNO_MAP(X) \ - X(ERROR_ACCESS_DENIED, EACCES) \ - X(ERROR_ALREADY_EXISTS, EEXIST) \ - X(ERROR_BAD_COMMAND, EACCES) \ - X(ERROR_BAD_EXE_FORMAT, ENOEXEC) \ - X(ERROR_BAD_LENGTH, EACCES) \ - X(ERROR_BAD_NETPATH, ENOENT) \ - X(ERROR_BAD_NET_NAME, ENOENT) \ - X(ERROR_BAD_NET_RESP, ENETDOWN) \ - X(ERROR_BAD_PATHNAME, ENOENT) \ - X(ERROR_BROKEN_PIPE, EPIPE) \ - X(ERROR_CANNOT_MAKE, EACCES) \ - X(ERROR_COMMITMENT_LIMIT, ENOMEM) \ - X(ERROR_CONNECTION_ABORTED, ECONNABORTED) \ - X(ERROR_CONNECTION_ACTIVE, EISCONN) \ - X(ERROR_CONNECTION_REFUSED, ECONNREFUSED) \ - X(ERROR_CRC, EACCES) \ - X(ERROR_DEVICE_FEATURE_NOT_SUPPORTED, EPERM) \ - X(ERROR_DIR_NOT_EMPTY, ENOTEMPTY) \ - X(ERROR_DISK_FULL, ENOSPC) \ - X(ERROR_DUP_NAME, EADDRINUSE) \ - X(ERROR_FILENAME_EXCED_RANGE, ENOENT) \ - X(ERROR_FILE_NOT_FOUND, ENOENT) \ - X(ERROR_GEN_FAILURE, EACCES) \ - X(ERROR_GRACEFUL_DISCONNECT, EPIPE) \ - X(ERROR_HOST_DOWN, EHOSTUNREACH) \ - X(ERROR_HOST_UNREACHABLE, EHOSTUNREACH) \ - X(ERROR_INSUFFICIENT_BUFFER, EFAULT) \ - X(ERROR_INVALID_ADDRESS, EADDRNOTAVAIL) \ - X(ERROR_INVALID_FUNCTION, EINVAL) \ - X(ERROR_INVALID_HANDLE, EBADF) \ - X(ERROR_INVALID_NETNAME, EADDRNOTAVAIL) \ - X(ERROR_INVALID_PARAMETER, EINVAL) \ - X(ERROR_INVALID_USER_BUFFER, EMSGSIZE) \ - X(ERROR_IO_PENDING, EINPROGRESS) \ - X(ERROR_LOCK_VIOLATION, EACCES) \ - X(ERROR_MORE_DATA, EMSGSIZE) \ - X(ERROR_NETNAME_DELETED, ECONNABORTED) \ - X(ERROR_NETWORK_ACCESS_DENIED, EACCES) \ - X(ERROR_NETWORK_BUSY, ENETDOWN) \ - X(ERROR_NETWORK_UNREACHABLE, ENETUNREACH) \ - X(ERROR_NOACCESS, EFAULT) \ - X(ERROR_NONPAGED_SYSTEM_RESOURCES, ENOMEM) \ - X(ERROR_NOT_ENOUGH_MEMORY, ENOMEM) \ - X(ERROR_NOT_ENOUGH_QUOTA, ENOMEM) \ - X(ERROR_NOT_FOUND, ENOENT) \ - X(ERROR_NOT_LOCKED, EACCES) \ - X(ERROR_NOT_READY, EACCES) \ - X(ERROR_NOT_SAME_DEVICE, EXDEV) \ - X(ERROR_NOT_SUPPORTED, ENOTSUP) \ - X(ERROR_NO_MORE_FILES, ENOENT) \ - X(ERROR_NO_SYSTEM_RESOURCES, ENOMEM) \ - X(ERROR_OPERATION_ABORTED, EINTR) \ - X(ERROR_OUT_OF_PAPER, EACCES) \ - X(ERROR_PAGED_SYSTEM_RESOURCES, ENOMEM) \ - X(ERROR_PAGEFILE_QUOTA, ENOMEM) \ - X(ERROR_PATH_NOT_FOUND, ENOENT) \ - X(ERROR_PIPE_NOT_CONNECTED, EPIPE) \ - X(ERROR_PORT_UNREACHABLE, ECONNRESET) \ - X(ERROR_PROTOCOL_UNREACHABLE, ENETUNREACH) \ - X(ERROR_REM_NOT_LIST, ECONNREFUSED) \ - X(ERROR_REQUEST_ABORTED, EINTR) \ - X(ERROR_REQ_NOT_ACCEP, EWOULDBLOCK) \ - X(ERROR_SECTOR_NOT_FOUND, EACCES) \ - X(ERROR_SEM_TIMEOUT, ETIMEDOUT) \ - X(ERROR_SHARING_VIOLATION, EACCES) \ - X(ERROR_TOO_MANY_NAMES, ENOMEM) \ - X(ERROR_TOO_MANY_OPEN_FILES, EMFILE) \ - X(ERROR_UNEXP_NET_ERR, ECONNABORTED) \ - X(ERROR_WAIT_NO_CHILDREN, ECHILD) \ - X(ERROR_WORKING_SET_QUOTA, ENOMEM) \ - X(ERROR_WRITE_PROTECT, EACCES) \ - X(ERROR_WRONG_DISK, EACCES) \ - X(WSAEACCES, EACCES) \ - X(WSAEADDRINUSE, EADDRINUSE) \ - X(WSAEADDRNOTAVAIL, EADDRNOTAVAIL) \ - X(WSAEAFNOSUPPORT, EAFNOSUPPORT) \ - X(WSAECONNABORTED, ECONNABORTED) \ - X(WSAECONNREFUSED, ECONNREFUSED) \ - X(WSAECONNRESET, ECONNRESET) \ - X(WSAEDISCON, EPIPE) \ - X(WSAEFAULT, EFAULT) \ - X(WSAEHOSTDOWN, EHOSTUNREACH) \ - X(WSAEHOSTUNREACH, EHOSTUNREACH) \ - X(WSAEINPROGRESS, EBUSY) \ - X(WSAEINTR, EINTR) \ - X(WSAEINVAL, EINVAL) \ - X(WSAEISCONN, EISCONN) \ - X(WSAEMSGSIZE, EMSGSIZE) \ - X(WSAENETDOWN, ENETDOWN) \ - X(WSAENETRESET, EHOSTUNREACH) \ - X(WSAENETUNREACH, ENETUNREACH) \ - X(WSAENOBUFS, ENOMEM) \ - X(WSAENOTCONN, ENOTCONN) \ - X(WSAENOTSOCK, ENOTSOCK) \ - X(WSAEOPNOTSUPP, EOPNOTSUPP) \ - X(WSAEPROCLIM, ENOMEM) \ - X(WSAESHUTDOWN, EPIPE) \ - X(WSAETIMEDOUT, ETIMEDOUT) \ - X(WSAEWOULDBLOCK, EWOULDBLOCK) \ - X(WSANOTINITIALISED, ENETDOWN) \ - X(WSASYSNOTREADY, ENETDOWN) \ +#define _ERROR_ERRNO_MAP(X) \ + X(ERROR_ACCESS_DENIED, EACCES) \ + X(ERROR_ALREADY_EXISTS, EEXIST) \ + X(ERROR_BAD_COMMAND, EACCES) \ + X(ERROR_BAD_EXE_FORMAT, ENOEXEC) \ + X(ERROR_BAD_LENGTH, EACCES) \ + X(ERROR_BAD_NETPATH, ENOENT) \ + X(ERROR_BAD_NET_NAME, ENOENT) \ + X(ERROR_BAD_NET_RESP, ENETDOWN) \ + X(ERROR_BAD_PATHNAME, ENOENT) \ + X(ERROR_BROKEN_PIPE, EPIPE) \ + X(ERROR_CANNOT_MAKE, EACCES) \ + X(ERROR_COMMITMENT_LIMIT, ENOMEM) \ + X(ERROR_CONNECTION_ABORTED, ECONNABORTED) \ + X(ERROR_CONNECTION_ACTIVE, EISCONN) \ + X(ERROR_CONNECTION_REFUSED, ECONNREFUSED) \ + X(ERROR_CRC, EACCES) \ + X(ERROR_DIR_NOT_EMPTY, ENOTEMPTY) \ + X(ERROR_DISK_FULL, ENOSPC) \ + X(ERROR_DUP_NAME, EADDRINUSE) \ + X(ERROR_FILENAME_EXCED_RANGE, ENOENT) \ + X(ERROR_FILE_NOT_FOUND, ENOENT) \ + X(ERROR_GEN_FAILURE, EACCES) \ + X(ERROR_GRACEFUL_DISCONNECT, EPIPE) \ + X(ERROR_HOST_DOWN, EHOSTUNREACH) \ + X(ERROR_HOST_UNREACHABLE, EHOSTUNREACH) \ + X(ERROR_INSUFFICIENT_BUFFER, EFAULT) \ + X(ERROR_INVALID_ADDRESS, EADDRNOTAVAIL) \ + X(ERROR_INVALID_FUNCTION, EINVAL) \ + X(ERROR_INVALID_HANDLE, EBADF) \ + X(ERROR_INVALID_NETNAME, EADDRNOTAVAIL) \ + X(ERROR_INVALID_PARAMETER, EINVAL) \ + X(ERROR_INVALID_USER_BUFFER, EMSGSIZE) \ + X(ERROR_IO_PENDING, EINPROGRESS) \ + X(ERROR_LOCK_VIOLATION, EACCES) \ + X(ERROR_MORE_DATA, EMSGSIZE) \ + X(ERROR_NETNAME_DELETED, ECONNABORTED) \ + X(ERROR_NETWORK_ACCESS_DENIED, EACCES) \ + X(ERROR_NETWORK_BUSY, ENETDOWN) \ + X(ERROR_NETWORK_UNREACHABLE, ENETUNREACH) \ + X(ERROR_NOACCESS, EFAULT) \ + X(ERROR_NONPAGED_SYSTEM_RESOURCES, ENOMEM) \ + X(ERROR_NOT_ENOUGH_MEMORY, ENOMEM) \ + X(ERROR_NOT_ENOUGH_QUOTA, ENOMEM) \ + X(ERROR_NOT_FOUND, ENOENT) \ + X(ERROR_NOT_LOCKED, EACCES) \ + X(ERROR_NOT_READY, EACCES) \ + X(ERROR_NOT_SAME_DEVICE, EXDEV) \ + X(ERROR_NOT_SUPPORTED, ENOTSUP) \ + X(ERROR_NO_MORE_FILES, ENOENT) \ + X(ERROR_NO_SYSTEM_RESOURCES, ENOMEM) \ + X(ERROR_OPERATION_ABORTED, EINTR) \ + X(ERROR_OUT_OF_PAPER, EACCES) \ + X(ERROR_PAGED_SYSTEM_RESOURCES, ENOMEM) \ + X(ERROR_PAGEFILE_QUOTA, ENOMEM) \ + X(ERROR_PATH_NOT_FOUND, ENOENT) \ + X(ERROR_PIPE_NOT_CONNECTED, EPIPE) \ + X(ERROR_PORT_UNREACHABLE, ECONNRESET) \ + X(ERROR_PROTOCOL_UNREACHABLE, ENETUNREACH) \ + X(ERROR_REM_NOT_LIST, ECONNREFUSED) \ + X(ERROR_REQUEST_ABORTED, EINTR) \ + X(ERROR_REQ_NOT_ACCEP, EWOULDBLOCK) \ + X(ERROR_SECTOR_NOT_FOUND, EACCES) \ + X(ERROR_SEM_TIMEOUT, ETIMEDOUT) \ + X(ERROR_SHARING_VIOLATION, EACCES) \ + X(ERROR_TOO_MANY_NAMES, ENOMEM) \ + X(ERROR_TOO_MANY_OPEN_FILES, EMFILE) \ + X(ERROR_UNEXP_NET_ERR, ECONNABORTED) \ + X(ERROR_WAIT_NO_CHILDREN, ECHILD) \ + X(ERROR_WORKING_SET_QUOTA, ENOMEM) \ + X(ERROR_WRITE_PROTECT, EACCES) \ + X(ERROR_WRONG_DISK, EACCES) \ + X(WSAEACCES, EACCES) \ + X(WSAEADDRINUSE, EADDRINUSE) \ + X(WSAEADDRNOTAVAIL, EADDRNOTAVAIL) \ + X(WSAEAFNOSUPPORT, EAFNOSUPPORT) \ + X(WSAECONNABORTED, ECONNABORTED) \ + X(WSAECONNREFUSED, ECONNREFUSED) \ + X(WSAECONNRESET, ECONNRESET) \ + X(WSAEDISCON, EPIPE) \ + X(WSAEFAULT, EFAULT) \ + X(WSAEHOSTDOWN, EHOSTUNREACH) \ + X(WSAEHOSTUNREACH, EHOSTUNREACH) \ + X(WSAEINPROGRESS, EBUSY) \ + X(WSAEINTR, EINTR) \ + X(WSAEINVAL, EINVAL) \ + X(WSAEISCONN, EISCONN) \ + X(WSAEMSGSIZE, EMSGSIZE) \ + X(WSAENETDOWN, ENETDOWN) \ + X(WSAENETRESET, EHOSTUNREACH) \ + X(WSAENETUNREACH, ENETUNREACH) \ + X(WSAENOBUFS, ENOMEM) \ + X(WSAENOTCONN, ENOTCONN) \ + X(WSAENOTSOCK, ENOTSOCK) \ + X(WSAEOPNOTSUPP, EOPNOTSUPP) \ + X(WSAEPROCLIM, ENOMEM) \ + X(WSAESHUTDOWN, EPIPE) \ + X(WSAETIMEDOUT, ETIMEDOUT) \ + X(WSAEWOULDBLOCK, EWOULDBLOCK) \ + X(WSANOTINITIALISED, ENETDOWN) \ + X(WSASYSNOTREADY, ENETDOWN) \ X(WSAVERNOTSUPPORTED, ENOSYS) errno_t err_map_win_error_to_errno(DWORD error) { diff --git a/src/win.h b/src/win.h index 32d1804..e7fa39b 100644 --- a/src/win.h +++ b/src/win.h @@ -25,9 +25,4 @@ #pragma warning(pop) #endif -#ifndef ERROR_DEVICE_FEATURE_NOT_SUPPORTED -/* Windows headers distributed with MinGW lack a definition for this. */ -#define ERROR_DEVICE_FEATURE_NOT_SUPPORTED 316L -#endif - #endif /* WEPOLL_WIN_H_ */ From c07cc8f7cce824613ccf63f4e8a892030c85e44c Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 03:00:45 +0200 Subject: [PATCH 17/28] poll-group: do away with the poll_group_allocator class --- src/poll-group.c | 60 ++++++++++++++---------------------------------- src/poll-group.h | 11 ++++----- src/port.c | 21 ++++++----------- src/port.h | 2 +- 4 files changed, 30 insertions(+), 64 deletions(-) diff --git a/src/poll-group.c b/src/poll-group.c index c6423d4..11e3ec2 100644 --- a/src/poll-group.c +++ b/src/poll-group.c @@ -9,19 +9,14 @@ static const size_t _POLL_GROUP_MAX_SIZE = 32; -typedef struct poll_group_allocator { - ep_port_t* port_info; - queue_t poll_group_queue; -} poll_group_allocator_t; - typedef struct poll_group { - poll_group_allocator_t* allocator; + ep_port_t* port_info; queue_node_t queue_node; SOCKET socket; size_t group_size; } poll_group_t; -static poll_group_t* _poll_group_new(poll_group_allocator_t* pga) { +static poll_group_t* _poll_group_new(ep_port_t* port_info) { poll_group_t* poll_group = malloc(sizeof *poll_group); if (poll_group == NULL) return_error(NULL, ERROR_NOT_ENOUGH_MEMORY); @@ -29,79 +24,58 @@ static poll_group_t* _poll_group_new(poll_group_allocator_t* pga) { memset(poll_group, 0, sizeof *poll_group); queue_node_init(&poll_group->queue_node); - poll_group->allocator = pga; + poll_group->port_info = port_info; - if (afd_create_driver_socket(pga->port_info->iocp, &poll_group->socket) < - 0) { + if (afd_create_driver_socket(port_info->iocp, &poll_group->socket) < 0) { free(poll_group); return NULL; } - queue_append(&pga->poll_group_queue, &poll_group->queue_node); + queue_append(&port_info->poll_group_queue, &poll_group->queue_node); return poll_group; } -static void _poll_group_delete(poll_group_t* poll_group) { +void poll_group_delete(poll_group_t* poll_group) { assert(poll_group->group_size == 0); closesocket(poll_group->socket); queue_remove(&poll_group->queue_node); free(poll_group); } +poll_group_t* poll_group_from_queue_node(queue_node_t* queue_node) { + return container_of(queue_node, poll_group_t, queue_node); +} + SOCKET poll_group_get_socket(poll_group_t* poll_group) { return poll_group->socket; } -poll_group_allocator_t* poll_group_allocator_new(ep_port_t* port_info) { - poll_group_allocator_t* pga = malloc(sizeof *pga); - if (pga == NULL) - return_error(NULL, ERROR_NOT_ENOUGH_MEMORY); - - queue_init(&pga->poll_group_queue); - pga->port_info = port_info; - - return pga; -} - -void poll_group_allocator_delete(poll_group_allocator_t* pga) { - queue_t* poll_group_queue = &pga->poll_group_queue; - - while (!queue_empty(poll_group_queue)) { - queue_node_t* queue_node = queue_first(poll_group_queue); - poll_group_t* poll_group = - container_of(queue_node, poll_group_t, queue_node); - _poll_group_delete(poll_group); - } - - free(pga); -} - -poll_group_t* poll_group_acquire(poll_group_allocator_t* pga) { - queue_t* queue = &pga->poll_group_queue; +poll_group_t* poll_group_acquire(ep_port_t* port_info) { + queue_t* queue = &port_info->poll_group_queue; poll_group_t* poll_group = !queue_empty(queue) ? container_of(queue_last(queue), poll_group_t, queue_node) : NULL; if (poll_group == NULL || poll_group->group_size >= _POLL_GROUP_MAX_SIZE) - poll_group = _poll_group_new(pga); + poll_group = _poll_group_new(port_info); if (poll_group == NULL) return NULL; if (++poll_group->group_size == _POLL_GROUP_MAX_SIZE) - queue_move_first(&pga->poll_group_queue, &poll_group->queue_node); + queue_move_first(&port_info->poll_group_queue, &poll_group->queue_node); return poll_group; } void poll_group_release(poll_group_t* poll_group) { - poll_group_allocator_t* pga = poll_group->allocator; + ep_port_t* port_info = poll_group->port_info; poll_group->group_size--; assert(poll_group->group_size < _POLL_GROUP_MAX_SIZE); - queue_move_last(&pga->poll_group_queue, &poll_group->queue_node); + queue_move_last(&port_info->poll_group_queue, &poll_group->queue_node); - /* TODO: free the poll_group_t* item at some point. */ + /* Poll groups are currently only freed when the epoll port is closed. */ } diff --git a/src/poll-group.h b/src/poll-group.h index 7345f32..7f64c38 100644 --- a/src/poll-group.h +++ b/src/poll-group.h @@ -7,16 +7,15 @@ #include "win.h" typedef struct ep_port ep_port_t; -typedef struct poll_group_allocator poll_group_allocator_t; typedef struct poll_group poll_group_t; -WEPOLL_INTERNAL poll_group_allocator_t* poll_group_allocator_new( - ep_port_t* port_info); -WEPOLL_INTERNAL void poll_group_allocator_delete(poll_group_allocator_t* pga); +WEPOLL_INTERNAL poll_group_t* poll_group_acquire(ep_port_t* port); +WEPOLL_INTERNAL void poll_group_release(poll_group_t* poll_group); -WEPOLL_INTERNAL poll_group_t* poll_group_acquire(poll_group_allocator_t* pga); -WEPOLL_INTERNAL void poll_group_release(poll_group_t* ds); +WEPOLL_INTERNAL void poll_group_delete(poll_group_t* poll_group); +WEPOLL_INTERNAL poll_group_t* poll_group_from_queue_node( + queue_node_t* queue_node); WEPOLL_INTERNAL SOCKET poll_group_get_socket(poll_group_t* poll_group); #endif /* WEPOLL_POLL_GROUP_H_ */ diff --git a/src/port.c b/src/port.c index c05aafe..8a20fbf 100644 --- a/src/port.c +++ b/src/port.c @@ -50,9 +50,10 @@ ep_port_t* ep_port_new(HANDLE* iocp_out) { memset(port_info, 0, sizeof *port_info); port_info->iocp = iocp; + tree_init(&port_info->sock_tree); queue_init(&port_info->sock_update_queue); queue_init(&port_info->sock_deleted_queue); - tree_init(&port_info->sock_tree); + queue_init(&port_info->poll_group_queue); reflock_tree_node_init(&port_info->handle_tree_node); InitializeCriticalSection(&port_info->lock); @@ -102,8 +103,10 @@ int ep_port_delete(ep_port_t* port_info) { ep_sock_force_delete(port_info, sock_info); } - if (port_info->poll_group_allocator != NULL) - poll_group_allocator_delete(port_info->poll_group_allocator); + while ((queue_node = queue_first(&port_info->poll_group_queue)) != NULL) { + poll_group_t* poll_group = poll_group_from_queue_node(queue_node); + poll_group_delete(poll_group); + } DeleteCriticalSection(&port_info->lock); @@ -356,18 +359,8 @@ ep_sock_t* ep_port_find_socket(ep_port_t* port_info, SOCKET socket) { return sock_info; } -static poll_group_allocator_t* _ep_port_get_poll_group_allocator( - ep_port_t* port_info) { - if (port_info->poll_group_allocator == NULL) { - port_info->poll_group_allocator = poll_group_allocator_new(port_info); - } - - return port_info->poll_group_allocator; -} - poll_group_t* ep_port_acquire_poll_group(ep_port_t* port_info) { - poll_group_allocator_t* pga = _ep_port_get_poll_group_allocator(port_info); - return poll_group_acquire(pga); + return poll_group_acquire(port_info); } void ep_port_release_poll_group(ep_port_t* port_info, diff --git a/src/port.h b/src/port.h index 4ff7c4c..78ebdb5 100644 --- a/src/port.h +++ b/src/port.h @@ -16,10 +16,10 @@ typedef struct ep_sock ep_sock_t; typedef struct ep_port { HANDLE iocp; - poll_group_allocator_t* poll_group_allocator; tree_t sock_tree; queue_t sock_update_queue; queue_t sock_deleted_queue; + queue_t poll_group_queue; reflock_tree_node_t handle_tree_node; CRITICAL_SECTION lock; size_t active_poll_count; From 5bcde85f8b74cea90e8572f75f19faed77dfa3b7 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 03:49:59 +0200 Subject: [PATCH 18/28] sock: call poll_group_acquire() and poll_group_release() directly --- src/port.c | 10 ---------- src/port.h | 4 ---- src/sock.c | 6 +++--- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/port.c b/src/port.c index 8a20fbf..84ffd09 100644 --- a/src/port.c +++ b/src/port.c @@ -359,16 +359,6 @@ ep_sock_t* ep_port_find_socket(ep_port_t* port_info, SOCKET socket) { return sock_info; } -poll_group_t* ep_port_acquire_poll_group(ep_port_t* port_info) { - return poll_group_acquire(port_info); -} - -void ep_port_release_poll_group(ep_port_t* port_info, - poll_group_t* poll_group) { - unused_var(port_info); - poll_group_release(poll_group); -} - void ep_port_request_socket_update(ep_port_t* port_info, ep_sock_t* sock_info) { if (queue_enqueued(&sock_info->queue_node)) diff --git a/src/port.h b/src/port.h index 78ebdb5..472c2e3 100644 --- a/src/port.h +++ b/src/port.h @@ -39,10 +39,6 @@ WEPOLL_INTERNAL int ep_port_ctl(ep_port_t* port_info, SOCKET sock, struct epoll_event* ev); -WEPOLL_INTERNAL poll_group_t* ep_port_acquire_poll_group(ep_port_t* port_info); -WEPOLL_INTERNAL void ep_port_release_poll_group(ep_port_t* port_info, - poll_group_t* poll_group); - WEPOLL_INTERNAL int ep_port_register_socket_handle(ep_port_t* port_info, ep_sock_t* sock_info, SOCKET socket); diff --git a/src/sock.c b/src/sock.c index dfd40ee..0dca3eb 100644 --- a/src/sock.c +++ b/src/sock.c @@ -183,7 +183,7 @@ ep_sock_t* ep_sock_new(ep_port_t* port_info, SOCKET socket) { if (base_socket == INVALID_SOCKET) return NULL; - poll_group = ep_port_acquire_poll_group(port_info); + poll_group = poll_group_acquire(port_info); if (poll_group == NULL) return NULL; @@ -208,7 +208,7 @@ ep_sock_t* ep_sock_new(ep_port_t* port_info, SOCKET socket) { err2: _ep_sock_free(sock_private); err1: - ep_port_release_poll_group(port_info, poll_group); + poll_group_release(poll_group); return NULL; } @@ -234,7 +234,7 @@ static void _ep_sock_delete(ep_port_t* port_info, if (force || sock_private->poll_status == _POLL_IDLE) { /* Free the sock_info now. */ ep_port_remove_deleted_socket(port_info, sock_info); - ep_port_release_poll_group(port_info, sock_private->poll_group); + poll_group_release(sock_private->poll_group); _ep_sock_free(sock_private); } else { /* Free the socket later. */ From 1a8573c4c1c2f1f0cf6b1482094e2f793bb50d0f Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 02:34:57 +0200 Subject: [PATCH 19/28] ci: write less verbose log output when installing the LSP --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index a11891b..4f0edbe 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -103,6 +103,7 @@ install: Install-Product node 'Current' if ($env:lsp) { + echo "Installing Proxifier LSP..." if (!(Test-Path c:\proxif\setup.exe)) { $null = &mkdir -Force c:\proxif Invoke-WebRequest -Uri http://www.proxifier.com/download/ProxifierSetup.exe -OutFile "c:\proxif\setup.exe" @@ -111,7 +112,6 @@ install: Start-Process -Wait c:\proxif\setup.exe -ArgumentList /silent,/norestart $catalog = &netsh winsock show catalog - echo $catalog if (!($catalog -like "*Layered Chain Entry*")) { throw "LSP not installed" } From cab89ae16afe341cb8585ddd2e5a74e6e2f21e39 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 02:34:11 +0200 Subject: [PATCH 20/28] ci: separate node.js and LSP installation steps --- .appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 4f0edbe..1c2dfde 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -99,9 +99,8 @@ init: - cmd: set install: + - ps: Install-Product node 'Current' - ps: >- - Install-Product node 'Current' - if ($env:lsp) { echo "Installing Proxifier LSP..." if (!(Test-Path c:\proxif\setup.exe)) { From df7abafe961f9b7e3d4588711bc55274871c1190 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 02:36:54 +0200 Subject: [PATCH 21/28] ci: no longer allow LSP builds to fail --- .appveyor.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 1c2dfde..666e8d5 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -82,10 +82,6 @@ environment: lsp: true appveyor_save_cache_on_error: true -matrix: - allow_failures: - - lsp: true - configuration: Debug clone_folder: c:\wepoll From 5313db43995ee8a393118315a5b25a47f82a1944 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 04:18:40 +0200 Subject: [PATCH 22/28] build: enable -Wextra for gcc and clang builds --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 53003b6..de9acdf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ link_libraries(ws2_32) if(MSVC) add_compile_options(/Wall /wd4127 /wd4201 /wd4242 /wd4710 /wd4711 /wd4820) else() - add_compile_options(-Wall) + add_compile_options(-Wall -Wextra) endif() file(GLOB SOURCES_SRC src/*.c src/*.h) From f4cfe68f5b3c3d98b98a07052098f7f93e1c24d2 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 04:20:43 +0200 Subject: [PATCH 23/28] build: treat warnings as errors --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de9acdf..cf2c949 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,9 +4,9 @@ project(wepoll) link_libraries(ws2_32) if(MSVC) - add_compile_options(/Wall /wd4127 /wd4201 /wd4242 /wd4710 /wd4711 /wd4820) + add_compile_options(/Wall /WX /wd4127 /wd4201 /wd4242 /wd4710 /wd4711 /wd4820) else() - add_compile_options(-Wall -Wextra) + add_compile_options(-Wall -Wextra -Werror) endif() file(GLOB SOURCES_SRC src/*.c src/*.h) From 8ac9b204da3955b01c55bbbdb95e47293454c67e Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 04:53:46 +0200 Subject: [PATCH 24/28] doc: make it more explicit that man pages are for Linux --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fe95647..d2e85ff 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ HANDLE epoll_create1(int flags); * `size` is ignored but most be greater than zero. * `flags` must be zero as there are no supported flags. * Returns `NULL` on failure. -* [man page][man epoll_create] +* [Linux man page][man epoll_create] ### epoll_close @@ -89,13 +89,14 @@ int epoll_ctl(HANDLE ephnd, * Control which socket events are monitored by an epoll port. * `ephnd` must be a HANDLE created by `epoll_create` or `epoll_create1`. * `op` must be one of `EPOLL_CTL_ADD`, `EPOLL_CTL_MOD`, `EPOLL_CTL_DEL`. +* `sock` must be a valid socket created by `socket()` or ` * It is recommended to always explicitly remove a socket from its epoll set using `EPOLL_CTL_DEL` *before* closing it. As on Linux, sockets are automatically removed from the epoll set when they are closed, but wepoll may not be able to detect this until the next call to `epoll_wait()`. * TODO: expand -* [man page][man epoll_ctl] +* [Linux man page][man epoll_ctl] ### epoll_wait @@ -112,7 +113,7 @@ int epoll_wait(HANDLE ephnd, - 0 when a timeout occurs - ≥1 the number of evens received * TODO: expand -* [man page][man epoll_wait] +* [Linux man page][man epoll_wait] [ci status badge]: https://ci.appveyor.com/api/projects/status/github/piscisaureus/wepoll?branch=master&svg=true From 56f2d70ce3588b9a0cea98a346e66445ad204402 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 06:48:02 +0200 Subject: [PATCH 25/28] doc: document `struct epoll_event`, events and flags --- README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ include/wepoll.h | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d2e85ff..5d16173 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,60 @@ int epoll_wait(HANDLE ephnd, * TODO: expand * [Linux man page][man epoll_wait] +### struct epoll_event + +```c +typedef union epoll_data { + void* ptr; + int fd; + uint32_t u32; + uint64_t u64; + SOCKET sock; /* Windows specific */ + HANDLE hnd; /* Windows specific */ +} epoll_data_t; +``` + +```c +struct epoll_event { + uint32_t events; /* Epoll events and flags */ + epoll_data_t data; /* User data variable */ +}; +``` + +* The `events` field is a bit mask containing the events being + monitored/reported, and optional flags.
+ Flags are accepted by [`epoll_ctl()`](#epoll_ctl), but they are not reported + back by [`epoll_wait()`](#epoll_wait). +* The `data` field can be used to associate application-specific information + with a socket; its value will be returned unmodified by + [`epoll_wait()`](#epoll_wait). +* [Linux man page][man epoll_ctl] + +| Event | Description | +|---------------|----------------------------------------------------------------------| +| `EPOLLIN` | incoming data available, or incoming connection ready to be accepted | +| `EPOLLOUT` | ready to send data, or outgoing connection successfully established | +| `EPOLLRDHUP` | remote peer initiated graceful socket shutdown | +| `EPOLLPRI` | out-of-band data available for reading | +| `EPOLLERR` | socket error1 | +| `EPOLLHUP` | socket hang-up1 | +| `EPOLLRDNORM` | same as `EPOLLIN` | +| `EPOLLRDBAND` | same as `EPOLLPRI` | +| `EPOLLWRNORM` | same as `EPOLLOUT` | +| `EPOLLWRBAND` | same as `EPOLLOUT` | +| `EPOLLMSG` | never reported | + +| Flag | Description | +|------------------|---------------------------| +| `EPOLLONESHOT` | report event(s) only once | +| `EPOLLET` | not supported by wepoll | +| `EPOLLEXCLUSIVE` | not supported by wepoll | +| `EPOLLWAKEUP` | not supported by wepoll | + +1: the `EPOLLERR` and `EPOLLHUP` events may always be reported by +[`epoll_wait()`](#epoll_wait), regardless of the event mask that was passed to +[`epoll_ctl()`](#epoll_ctl). + [ci status badge]: https://ci.appveyor.com/api/projects/status/github/piscisaureus/wepoll?branch=master&svg=true [ci status link]: https://ci.appveyor.com/project/piscisaureus/wepoll/branch/master diff --git a/include/wepoll.h b/include/wepoll.h index dcd5ac4..d5ed8a8 100644 --- a/include/wepoll.h +++ b/include/wepoll.h @@ -56,7 +56,7 @@ typedef union epoll_data { } epoll_data_t; struct epoll_event { - uint32_t events; /* Epoll events */ + uint32_t events; /* Epoll events and flags */ epoll_data_t data; /* User data variable */ }; From d6845acd267e2bc2946f3e65f4b8f2ce3f639144 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 06:49:32 +0200 Subject: [PATCH 26/28] doc: expand epoll_ctl() documentation --- README.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5d16173..356e217 100644 --- a/README.md +++ b/README.md @@ -87,15 +87,21 @@ int epoll_ctl(HANDLE ephnd, ``` * Control which socket events are monitored by an epoll port. -* `ephnd` must be a HANDLE created by `epoll_create` or `epoll_create1`. +* `ephnd` must be a HANDLE created by + [`epoll_create()`](#epoll_createepoll_create1) or + [`epoll_create1()`](#epoll_createepoll_create1). * `op` must be one of `EPOLL_CTL_ADD`, `EPOLL_CTL_MOD`, `EPOLL_CTL_DEL`. -* `sock` must be a valid socket created by `socket()` or ` +* `sock` must be a valid socket created by [`socket()`][msdn socket], + [`WSASocket()`][msdn wsasocket], or [`accept()`][msdn accept]. +* `event` should be a pointer to a [`struct epoll_event`](#struct-epoll_event).
+ If `op` is `EPOLL_CTL_DEL` then the `event` parameter is ignored, and it + may be `NULL`. +* Returns 0 on success, -1 on failure. * It is recommended to always explicitly remove a socket from its epoll - set using `EPOLL_CTL_DEL` *before* closing it. As on Linux, sockets - are automatically removed from the epoll set when they are closed, but - wepoll may not be able to detect this until the next call to - `epoll_wait()`. -* TODO: expand + set using `EPOLL_CTL_DEL` *before* closing it.
+ As on Linux, closed sockets are automatically removed from the epoll set, but + wepoll may not be able to detect that a socket was closed until the next call + to [`epoll_wait()`](#epoll_wait). * [Linux man page][man epoll_ctl] ### epoll_wait @@ -177,6 +183,9 @@ struct epoll_event { [man epoll_create]: http://man7.org/linux/man-pages/man2/epoll_create.2.html [man epoll_ctl]: http://man7.org/linux/man-pages/man2/epoll_ctl.2.html [man epoll_wait]: http://man7.org/linux/man-pages/man2/epoll_wait.2.html +[msdn accept]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms737526(v=vs.85).aspx +[msdn socket]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx +[msdn wsasocket]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx [select scale]: https://daniel.haxx.se/docs/poll-vs-select.html [wsapoll broken]: https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/ [wepoll.c]: https://github.com/piscisaureus/wepoll/blob/dist/wepoll.c From 564db33b82e7bee7ee6ecf032533562c3847f164 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 06:49:56 +0200 Subject: [PATCH 27/28] doc: expand epoll_wait() documentation --- README.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 356e217..fdaf549 100644 --- a/README.md +++ b/README.md @@ -114,11 +114,19 @@ int epoll_wait(HANDLE ephnd, ``` * Receive socket events from an epoll port. -* Returns - - -1 on failure - - 0 when a timeout occurs - - ≥1 the number of evens received -* TODO: expand +* `events` should point to a caller-allocated array of + [`epoll_event`](#struct-epoll_event) structs, which will receive the + reported events. +* `maxevents` is the maximum number of events that will be written to the + `events` array, and must be greater than zero. +* `timeout` specifies whether to block when no events are immediately available. + - `<0` block indefinitely + - `0` report any events that are already waiting, but don't block + - `≥1` block for at most N milliseconds +* Return value: + - `-1` an error occurred + - `0` timed out without any events to report + - `≥1` the number of events stored in the `events` buffer * [Linux man page][man epoll_wait] ### struct epoll_event From 1bcd5d35e393a04d15564c6fa7c29d59a557b5b5 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Wed, 2 May 2018 06:50:12 +0200 Subject: [PATCH 28/28] doc: readme tweaks --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fdaf549..d334d08 100644 --- a/README.md +++ b/README.md @@ -38,21 +38,23 @@ to run on Linux. ## How to use The library is [distributed][dist] as a single source file -([wepoll.c][wepoll.c]) and a single header file ([wepoll.h][wepoll.h]). -Compile the .c file as part of your project, and include the header -wherever needed. +([wepoll.c][wepoll.c]) and a single header file ([wepoll.h][wepoll.h]).
+Compile the .c file as part of your project, and include the header wherever +needed. ## Compatibility * Requires Windows Vista or higher. * Can be compiled with recent versions of MSVC, Clang, and GCC. -## API notes +## API -### General +### General remarks * The epoll port is a `HANDLE`, not a file descriptor. * All functions set both `errno` and `GetLastError()` on failure. +* For more extensive documentation, see the [epoll(7) man page][man epoll], + and the per-function man pages that are linked below. ### epoll_create/epoll_create1