From c321b2a7d4d40f2b9591f990f0e02b1e4533d7fa Mon Sep 17 00:00:00 2001 From: Constantin Papizh <p.const@bk.ru> Date: Thu, 28 Nov 2019 22:21:58 +0300 Subject: [PATCH] wepoll issues --- 3rdparty/libmemcached/libmemcached/common.h | 2 - CMakeLists.txt | 22 +- lib/[x86_64CLANG]/wepoll[x86_64CLANG].a | Bin 35328 -> 0 bytes libdap | 2 +- libdap-chain | 2 +- libdap-chain-cs-dag | 2 +- libdap-chain-cs-dag-poa | 2 +- libdap-chain-cs-dag-pos | 2 +- libdap-chain-gdb | 2 +- libdap-chain-global-db | 2 +- libdap-chain-mempool | 2 +- libdap-chain-net | 2 +- libdap-chain-net-srv | 2 +- libdap-chain-net-srv-app | 2 +- libdap-chain-net-srv-app-db | 2 +- libdap-chain-net-srv-datum | 2 +- libdap-chain-net-srv-datum-pool | 2 +- libdap-chain-wallet | 2 +- libdap-client | 2 +- libdap-server | 2 +- libdap-server-core | 2 +- libdap-server-udp | 2 +- libdap-stream | 2 +- libdap-stream-ch | 2 +- libdap-stream-ch-chain | 2 +- libdap-stream-ch-chain-net | 2 +- libdap-stream-ch-chain-net-srv | 2 +- libdap-stream-ch-vpn | 2 +- sources/main.c | 3 - sources/main_node_cli_net.c | 3 - sources/main_node_cli_shell.c | 3 - sources/main_node_tool.c | 3 - sources/wepoll/LICENSE | 28 + sources/wepoll/README.md | 202 ++ sources/wepoll/wepoll.c | 2189 +++++++++++++++++ .../include => sources/wepoll}/wepoll.h | 43 +- 36 files changed, 2488 insertions(+), 60 deletions(-) delete mode 100644 lib/[x86_64CLANG]/wepoll[x86_64CLANG].a create mode 100644 sources/wepoll/LICENSE create mode 100644 sources/wepoll/README.md create mode 100644 sources/wepoll/wepoll.c rename {3rdparty/wepoll/include => sources/wepoll}/wepoll.h (52%) diff --git a/3rdparty/libmemcached/libmemcached/common.h b/3rdparty/libmemcached/libmemcached/common.h index 5761657c2..da828a6d0 100644 --- a/3rdparty/libmemcached/libmemcached/common.h +++ b/3rdparty/libmemcached/libmemcached/common.h @@ -43,8 +43,6 @@ #ifdef WIN32 -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 #include <winsock2.h> #include <windows.h> diff --git a/CMakeLists.txt b/CMakeLists.txt index a182c8410..4e94d63c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ if(WIN32) add_definitions("-DHAVE_PREAD") add_definitions("-DHAVE_MMAP") add_definitions("-DHAVE_STRNDUP") + add_definitions("-DWINVER=0x0600 -D_WIN32_WINNT=0x0600") if(DAP_RELEASE) set(_CCOPT "-static -std=gnu11 -Wall -O3 -fno-ident -ffast-math -ftree-vectorize -mfpmath=sse -mmmx -msse2 -fno-asynchronous-unwind-tables -ffunction-sections -Wl,--gc-sections -Wl,--strip-all") @@ -94,12 +95,11 @@ if(WIN32) include_directories(libdap/src/win32/) include_directories(3rdparty/libmemcached/) include_directories(3rdparty/libmemcached/win32/) - include_directories(3rdparty/wepoll/include/) include_directories(3rdparty/uthash/src/) include_directories(3rdparty/libjson-c/) include_directories(3rdparty/curl/include/) include_directories(3rdparty/libsqlite3/) - + include_directories(sources/wepoll/) include_directories(libdap-server-http-db-auth/) include_directories(libdap-chain-net-srv-vpn/) endif() @@ -177,11 +177,9 @@ if(WIN32) add_executable(${PROJECT_NAME} "sources/main.c" "sources/exh_win32.c" "sources/sig_win32_handler.c") add_executable(${PROJECT_NAME}-cli "sources/main_node_cli.c" "sources/main_node_cli_shell.c" "sources/main_node_cli_net.c" ) add_executable(${PROJECT_NAME}-tool "sources/main_node_tool.c" ) - target_link_libraries(${PROJECT_NAME}-cli dap_chain_net ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libjson-c[x86_64CLANG].a ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libmemcached[x86_64CLANG].a - ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/wepoll[x86_64CLANG].a ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libsqlite3[x86_64CLANG].a ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libmongoc[x86_64CLANG].a ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libbson[x86_64CLANG].a @@ -210,9 +208,9 @@ if(WIN32) Bcrypt Crypt32 Secur32 - userenv + userenv ) - set_property(TARGET ${PROJECT_NAME}-cli APPEND_STRING PROPERTY LINK_FLAGS "-mconsole") + set_property(TARGET ${PROJECT_NAME}-cli APPEND_STRING PROPERTY LINK_FLAGS "-mconsole") target_link_libraries(${PROJECT_NAME}-tool dap_core dap_crypto dap_server_core dap_enc_server dap_udp_server dap_session dap_enc_server dap_stream dap_stream_ch_vpn dap_stream_ch_chain dap_stream_ch_chain_net dap_stream_ch_chain_net_srv dap_chain dap_chain_crypto dap_client @@ -223,7 +221,6 @@ if(WIN32) ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libjson-c[x86_64CLANG].a ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libmemcached[x86_64CLANG].a - ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/wepoll[x86_64CLANG].a ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libsqlite3[x86_64CLANG].a ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libmongoc[x86_64CLANG].a ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libbson[x86_64CLANG].a @@ -252,9 +249,9 @@ if(WIN32) Bcrypt Crypt32 Secur32 - userenv + userenv ) - set_property(TARGET ${PROJECT_NAME}-tool APPEND_STRING PROPERTY LINK_FLAGS "-mconsole") + set_property(TARGET ${PROJECT_NAME}-tool APPEND_STRING PROPERTY LINK_FLAGS "-mconsole") target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_server_core dap_enc_server dap_udp_server dap_session dap_enc_server dap_stream dap_stream_ch_vpn dap_stream_ch_chain dap_stream_ch_chain_net @@ -265,7 +262,6 @@ if(WIN32) dap_chain_wallet dap_chain_global_db dap_chain_mempool dap_chain_gdb ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libjson-c[x86_64CLANG].a ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libmemcached[x86_64CLANG].a - ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/wepoll[x86_64CLANG].a ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libsqlite3[x86_64CLANG].a ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libmongoc[x86_64CLANG].a ${CMAKE_CURRENT_SOURCE_DIR}/lib/[x86_64CLANG]/libbson[x86_64CLANG].a @@ -294,9 +290,9 @@ if(WIN32) Bcrypt Crypt32 Secur32 - userenv + userenv ) - set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY LINK_FLAGS "-mwindows") + set_property(TARGET ${PROJECT_NAME} APPEND_STRING PROPERTY LINK_FLAGS "-mwindows") #dap_chain_net_srv_vpn dap_server_http_db_auth @@ -315,7 +311,7 @@ target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_crypto dap_server_ dap_chain_net_srv_datum dap_chain_net_srv_datum_pool dap_chain_net_srv_vpn dap_chain_wallet dap_chain_global_db dap_chain_mempool dap_chain_gdb m pthread magic dap_server_http_db dap_server_http_db_auth) -target_link_libraries(${PROJECT_NAME}-cli m dap_chain_net curl) +target_link_libraries(${PROJECT_NAME}-cli m dap_chain_net curl) target_link_libraries(${PROJECT_NAME}-tool dap_core dap_crypto dap_server_core dap_enc_server dap_udp_server dap_session dap_enc_server dap_stream dap_stream_ch_vpn dap_stream_ch_chain dap_stream_ch_chain_net diff --git a/lib/[x86_64CLANG]/wepoll[x86_64CLANG].a b/lib/[x86_64CLANG]/wepoll[x86_64CLANG].a deleted file mode 100644 index ccfe34c9e9e4664ffabcbd9832ff991afcf5e22f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35328 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 DZ)z8d diff --git a/libdap b/libdap index 1c0614797..9053a0597 160000 --- a/libdap +++ b/libdap @@ -1 +1 @@ -Subproject commit 1c0614797a3a2a2f4c179630025600e177637a65 +Subproject commit 9053a0597613f8dc685bfd756b058494b8aaef14 diff --git a/libdap-chain b/libdap-chain index 04be73e68..65f715269 160000 --- a/libdap-chain +++ b/libdap-chain @@ -1 +1 @@ -Subproject commit 04be73e68e1994e86a71154370616a3a3b2b85ae +Subproject commit 65f715269361e3d9ba317537e655961f31bf2247 diff --git a/libdap-chain-cs-dag b/libdap-chain-cs-dag index 447d6db4e..4d83c5afc 160000 --- a/libdap-chain-cs-dag +++ b/libdap-chain-cs-dag @@ -1 +1 @@ -Subproject commit 447d6db4e8587d6118e968eb1109fbf2a3356247 +Subproject commit 4d83c5afc1e0f71c24b48603557372f8bb20f021 diff --git a/libdap-chain-cs-dag-poa b/libdap-chain-cs-dag-poa index db06bbd6a..757f58797 160000 --- a/libdap-chain-cs-dag-poa +++ b/libdap-chain-cs-dag-poa @@ -1 +1 @@ -Subproject commit db06bbd6a784ff2496f2d1bb9cbb54924efe83b6 +Subproject commit 757f58797844421ce4b3b369e11dff9065acbfb6 diff --git a/libdap-chain-cs-dag-pos b/libdap-chain-cs-dag-pos index b1a0d674d..52bf11864 160000 --- a/libdap-chain-cs-dag-pos +++ b/libdap-chain-cs-dag-pos @@ -1 +1 @@ -Subproject commit b1a0d674dbb56938e0ba58862ad1d638420e554f +Subproject commit 52bf11864520d7090362713cadf87fe0d63bd8a3 diff --git a/libdap-chain-gdb b/libdap-chain-gdb index 761812252..86d32bd6d 160000 --- a/libdap-chain-gdb +++ b/libdap-chain-gdb @@ -1 +1 @@ -Subproject commit 76181225215f91f3a2a42671f735f70ade46d045 +Subproject commit 86d32bd6dc2159e7fe3eb50996dd6b8c8f51be5d diff --git a/libdap-chain-global-db b/libdap-chain-global-db index 8c6bbf445..b7d2fb60f 160000 --- a/libdap-chain-global-db +++ b/libdap-chain-global-db @@ -1 +1 @@ -Subproject commit 8c6bbf4458bd10614bf48bb2da7e84b4f80f0d33 +Subproject commit b7d2fb60fe97c6f8e2a08cb41429d1f33a3bc353 diff --git a/libdap-chain-mempool b/libdap-chain-mempool index 4bbdcf60e..74421dd77 160000 --- a/libdap-chain-mempool +++ b/libdap-chain-mempool @@ -1 +1 @@ -Subproject commit 4bbdcf60e10de8462820e45d7cc3715dca4c2139 +Subproject commit 74421dd778babd15c0e8e2c28f9b4e4c1d93d41e diff --git a/libdap-chain-net b/libdap-chain-net index 94ff5a32e..8de304672 160000 --- a/libdap-chain-net +++ b/libdap-chain-net @@ -1 +1 @@ -Subproject commit 94ff5a32e30465d3e5986a11753caf57f67e5096 +Subproject commit 8de30467285db82cdeef34e95eae60efecec95d3 diff --git a/libdap-chain-net-srv b/libdap-chain-net-srv index 5edea70af..cafb5af49 160000 --- a/libdap-chain-net-srv +++ b/libdap-chain-net-srv @@ -1 +1 @@ -Subproject commit 5edea70af6a5ede0c759e9d83a5741376f4da9fb +Subproject commit cafb5af4984e8ee347518c0718f5c68733b51f3a diff --git a/libdap-chain-net-srv-app b/libdap-chain-net-srv-app index e5f23784d..18e4d357d 160000 --- a/libdap-chain-net-srv-app +++ b/libdap-chain-net-srv-app @@ -1 +1 @@ -Subproject commit e5f23784de6b8cc16b80b4bc755c67df5fc7565f +Subproject commit 18e4d357d6d984b96ea8c95582d206217ab3f032 diff --git a/libdap-chain-net-srv-app-db b/libdap-chain-net-srv-app-db index ce7f0333b..9e115e17a 160000 --- a/libdap-chain-net-srv-app-db +++ b/libdap-chain-net-srv-app-db @@ -1 +1 @@ -Subproject commit ce7f0333b77f8290428949b593e0ca2bd3df01b3 +Subproject commit 9e115e17ab30a8b6df0507471e2c08a4dad8c184 diff --git a/libdap-chain-net-srv-datum b/libdap-chain-net-srv-datum index 5186f865f..602391159 160000 --- a/libdap-chain-net-srv-datum +++ b/libdap-chain-net-srv-datum @@ -1 +1 @@ -Subproject commit 5186f865f3a1bd10ef61f3cb039d85c9b32c7ad7 +Subproject commit 602391159d47da34b08d8da743812a26a2efdd97 diff --git a/libdap-chain-net-srv-datum-pool b/libdap-chain-net-srv-datum-pool index 0eb7c0802..883e227be 160000 --- a/libdap-chain-net-srv-datum-pool +++ b/libdap-chain-net-srv-datum-pool @@ -1 +1 @@ -Subproject commit 0eb7c08027ff1a643c90b5ca59a8e4267e71ca6f +Subproject commit 883e227be78ca7f2aa33a9283b5122198875dad3 diff --git a/libdap-chain-wallet b/libdap-chain-wallet index 5273ba5ba..3fcaedb1f 160000 --- a/libdap-chain-wallet +++ b/libdap-chain-wallet @@ -1 +1 @@ -Subproject commit 5273ba5ba0a15dd94e680f9d961ab150f3764848 +Subproject commit 3fcaedb1f33a3db1bde6050f824fdabf07e9ad34 diff --git a/libdap-client b/libdap-client index d3f5458f3..b583f811f 160000 --- a/libdap-client +++ b/libdap-client @@ -1 +1 @@ -Subproject commit d3f5458f352294547b1ac68404d5ccdc8dea28ba +Subproject commit b583f811fe3221c51f491809095340e0a6ff47af diff --git a/libdap-server b/libdap-server index e3f07ae78..d7401c149 160000 --- a/libdap-server +++ b/libdap-server @@ -1 +1 @@ -Subproject commit e3f07ae78aaecfada9f44abdd48a4ebbce9e2184 +Subproject commit d7401c1493dbbd86e843c639182469c09331d077 diff --git a/libdap-server-core b/libdap-server-core index 73a86ef82..a50355cba 160000 --- a/libdap-server-core +++ b/libdap-server-core @@ -1 +1 @@ -Subproject commit 73a86ef8202ee67a05098d186aac2deacb207f63 +Subproject commit a50355cbaa236b4e02ebc64e8db5d53125c8527d diff --git a/libdap-server-udp b/libdap-server-udp index a14daa6c3..8e0641bce 160000 --- a/libdap-server-udp +++ b/libdap-server-udp @@ -1 +1 @@ -Subproject commit a14daa6c39aed9bba32684872d03afc7778db61e +Subproject commit 8e0641bceb42e785dae16040ef3458a178e1b9f5 diff --git a/libdap-stream b/libdap-stream index eb3f6b659..af3ea5274 160000 --- a/libdap-stream +++ b/libdap-stream @@ -1 +1 @@ -Subproject commit eb3f6b659c2ad25e1b9906eb9820e46de7443103 +Subproject commit af3ea52745feb78f9ca9f23125dd42dbbd144731 diff --git a/libdap-stream-ch b/libdap-stream-ch index daeb35e46..c90d1ec2c 160000 --- a/libdap-stream-ch +++ b/libdap-stream-ch @@ -1 +1 @@ -Subproject commit daeb35e4680f659d41ade4da7514138e56278685 +Subproject commit c90d1ec2c98a7062d5a3afdf71df873e829f72c4 diff --git a/libdap-stream-ch-chain b/libdap-stream-ch-chain index 7dcfe38b6..f5fe9e183 160000 --- a/libdap-stream-ch-chain +++ b/libdap-stream-ch-chain @@ -1 +1 @@ -Subproject commit 7dcfe38b65abc0b04d4b2fc2292e22153322c7d6 +Subproject commit f5fe9e183e788af329d863645986d1c1cf17a65a diff --git a/libdap-stream-ch-chain-net b/libdap-stream-ch-chain-net index 6875d05d0..cd646f000 160000 --- a/libdap-stream-ch-chain-net +++ b/libdap-stream-ch-chain-net @@ -1 +1 @@ -Subproject commit 6875d05d0d3c3da2c9562b4840f48a5267e865ca +Subproject commit cd646f0008051a5bc9a9c32f4ba6641f9ed4c444 diff --git a/libdap-stream-ch-chain-net-srv b/libdap-stream-ch-chain-net-srv index e136afe77..9f7ed19b5 160000 --- a/libdap-stream-ch-chain-net-srv +++ b/libdap-stream-ch-chain-net-srv @@ -1 +1 @@ -Subproject commit e136afe77716b6e327310ccaa7f58ad75c63b2c5 +Subproject commit 9f7ed19b5956822f2587dba7f09886c48e139a8e diff --git a/libdap-stream-ch-vpn b/libdap-stream-ch-vpn index 29383e0b4..02652f21a 160000 --- a/libdap-stream-ch-vpn +++ b/libdap-stream-ch-vpn @@ -1 +1 @@ -Subproject commit 29383e0b47628acf0dc085f69a87a449dd03acb5 +Subproject commit 02652f21a7d7b741193610487d0de05dd2d51f57 diff --git a/sources/main.c b/sources/main.c index 026fe91f7..f42202f88 100644 --- a/sources/main.c +++ b/sources/main.c @@ -34,15 +34,12 @@ #include <signal.h> #ifdef _WIN32 -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 #include <winsock2.h> #include <windows.h> #include <mswsock.h> #include <ws2tcpip.h> #include <io.h> //#include "wrappers.h" -#include <wepoll.h> #include <pthread.h> #include "userenv.h" diff --git a/sources/main_node_cli_net.c b/sources/main_node_cli_net.c index 022d5baa6..35c81bbab 100644 --- a/sources/main_node_cli_net.c +++ b/sources/main_node_cli_net.c @@ -39,15 +39,12 @@ #include <assert.h> #ifdef _WIN32 -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 #include <winsock2.h> #include <windows.h> #include <mswsock.h> #include <ws2tcpip.h> #include <io.h> //#include "wrappers.h" -#include <wepoll.h> #include <pthread.h> #else #include <sys/socket.h> diff --git a/sources/main_node_cli_shell.c b/sources/main_node_cli_shell.c index 9f8397c70..90e45d13a 100644 --- a/sources/main_node_cli_shell.c +++ b/sources/main_node_cli_shell.c @@ -15,15 +15,12 @@ #include <locale.h> #ifdef _WIN32 -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 #include <winsock2.h> #include <windows.h> #include <mswsock.h> #include <ws2tcpip.h> #include <io.h> //#include "wrappers.h" -#include <wepoll.h> #include <pthread.h> #else #include <sys/ttydefaults.h> diff --git a/sources/main_node_tool.c b/sources/main_node_tool.c index 9fcab399d..53a7e1662 100644 --- a/sources/main_node_tool.c +++ b/sources/main_node_tool.c @@ -29,15 +29,12 @@ #include <string.h> #ifdef _WIN32 -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 #include <winsock2.h> #include <windows.h> #include <mswsock.h> #include <ws2tcpip.h> #include <io.h> //#include "wrappers.h" -#include <wepoll.h> #endif #include <pthread.h> diff --git a/sources/wepoll/LICENSE b/sources/wepoll/LICENSE new file mode 100644 index 000000000..6c8b1c842 --- /dev/null +++ b/sources/wepoll/LICENSE @@ -0,0 +1,28 @@ +wepoll - epoll for Windows +https://github.com/piscisaureus/wepoll + +Copyright 2012-2019, Bert Belder <bertbelder@gmail.com> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/sources/wepoll/README.md b/sources/wepoll/README.md new file mode 100644 index 000000000..d334d0833 --- /dev/null +++ b/sources/wepoll/README.md @@ -0,0 +1,202 @@ +# wepoll - epoll for windows + +[![][ci status badge]][ci status link] + +This library implements the [epoll][man epoll] API for Windows +applications. It is fast and scalable, and it closely resembles the API +and behavior of Linux' epoll. + +## Rationale + +Unlike Linux, OS X, and many other operating systems, Windows doesn't +have a good API for receiving socket state notifications. It only +supports the `select` and `WSAPoll` APIs, but they +[don't scale][select scale] and suffer from +[other issues][wsapoll broken]. + +Using I/O completion ports isn't always practical when software is +designed to be cross-platform. Wepoll offers an alternative that is +much closer to a drop-in replacement for software that was designed +to run on Linux. + +## Features + +* Can poll 100000s of sockets efficiently. +* Fully thread-safe. +* Multiple threads can poll the same epoll port. +* Sockets can be added to multiple epoll sets. +* All epoll events (`EPOLLIN`, `EPOLLOUT`, `EPOLLPRI`, `EPOLLRDHUP`) + are supported. +* Level-triggered and one-shot (`EPOLLONESTHOT`) modes are supported +* Trivial to embed: you need [only two files][dist]. + +## Limitations + +* Only works with sockets. +* Edge-triggered (`EPOLLET`) mode isn't supported. + +## 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]).<br> +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 + +### 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 + +```c +HANDLE epoll_create(int size); +HANDLE epoll_create1(int flags); +``` + +* Create a new epoll instance (port). +* `size` is ignored but most be greater than zero. +* `flags` must be zero as there are no supported flags. +* Returns `NULL` on failure. +* [Linux man page][man epoll_create] + +### epoll_close + +```c +int epoll_close(HANDLE ephnd); +``` + +* Close an epoll port. +* Do not attempt to close the epoll port with `close()`, + `CloseHandle()` or `closesocket()`. + +### epoll_ctl + +```c +int epoll_ctl(HANDLE ephnd, + int op, + SOCKET sock, + struct epoll_event* event); +``` + +* Control which socket events are monitored by an epoll port. +* `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()`][msdn socket], + [`WSASocket()`][msdn wsasocket], or [`accept()`][msdn accept]. +* `event` should be a pointer to a [`struct epoll_event`](#struct-epoll_event).<br> + 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.<br> + 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 + +```c +int epoll_wait(HANDLE ephnd, + struct epoll_event* events, + int maxevents, + int timeout); +``` + +* Receive socket events from an epoll port. +* `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 + +```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.<br> + 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 error<sup>1</sup> | +| `EPOLLHUP` | socket hang-up<sup>1</sup> | +| `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 | + +<sup>1</sup>: 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 +[dist]: https://github.com/piscisaureus/wepoll/tree/dist +[man epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html +[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 +[wepoll.h]: https://github.com/piscisaureus/wepoll/blob/dist/wepoll.h diff --git a/sources/wepoll/wepoll.c b/sources/wepoll/wepoll.c new file mode 100644 index 000000000..651673aad --- /dev/null +++ b/sources/wepoll/wepoll.c @@ -0,0 +1,2189 @@ +/* + * wepoll - epoll for Windows + * https://github.com/piscisaureus/wepoll + * + * Copyright 2012-2019, Bert Belder <bertbelder@gmail.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WEPOLL_EXPORT +#define WEPOLL_EXPORT +#endif + +#include <stdint.h> + +enum EPOLL_EVENTS { + EPOLLIN = (int) (1U << 0), + EPOLLPRI = (int) (1U << 1), + EPOLLOUT = (int) (1U << 2), + EPOLLERR = (int) (1U << 3), + EPOLLHUP = (int) (1U << 4), + EPOLLRDNORM = (int) (1U << 6), + EPOLLRDBAND = (int) (1U << 7), + EPOLLWRNORM = (int) (1U << 8), + EPOLLWRBAND = (int) (1U << 9), + EPOLLMSG = (int) (1U << 10), /* Never reported. */ + EPOLLRDHUP = (int) (1U << 13), + EPOLLONESHOT = (int) (1U << 31) +}; + +#define EPOLLIN (1U << 0) +#define EPOLLPRI (1U << 1) +#define EPOLLOUT (1U << 2) +#define EPOLLERR (1U << 3) +#define EPOLLHUP (1U << 4) +#define EPOLLRDNORM (1U << 6) +#define EPOLLRDBAND (1U << 7) +#define EPOLLWRNORM (1U << 8) +#define EPOLLWRBAND (1U << 9) +#define EPOLLMSG (1U << 10) +#define EPOLLRDHUP (1U << 13) +#define EPOLLONESHOT (1U << 31) + +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_MOD 2 +#define EPOLL_CTL_DEL 3 + +typedef void* HANDLE; +typedef uintptr_t SOCKET; + +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; + +struct epoll_event { + uint32_t events; /* Epoll events and flags */ + epoll_data_t data; /* User data variable */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +WEPOLL_EXPORT HANDLE epoll_create(int size); +WEPOLL_EXPORT HANDLE epoll_create1(int flags); + +WEPOLL_EXPORT int epoll_close(HANDLE ephnd); + +WEPOLL_EXPORT int epoll_ctl(HANDLE ephnd, + int op, + SOCKET sock, + struct epoll_event* event); + +WEPOLL_EXPORT int epoll_wait(HANDLE ephnd, + struct epoll_event* events, + int maxevents, + int timeout); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#include <malloc.h> +#include <stdlib.h> + +#define WEPOLL_INTERNAL static +#define WEPOLL_INTERNAL_VAR static + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif + +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif + +#define _WIN32_WINNT 0x0600 + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifndef __GNUC__ +#pragma warning(push, 1) +#endif + +#include <WS2tcpip.h> +#include <WinSock2.h> +#include <Windows.h> + +#ifndef __GNUC__ +#pragma warning(pop) +#endif + +WEPOLL_INTERNAL int nt_global_init(void); + +typedef LONG NTSTATUS; +typedef NTSTATUS* PNTSTATUS; + +#ifndef NT_SUCCESS +#define NT_SUCCESS(status) (((NTSTATUS)(status)) >= 0) +#endif + +#ifndef STATUS_SUCCESS +#define STATUS_SUCCESS ((NTSTATUS) 0x00000000L) +#endif + +#ifndef STATUS_PENDING +#define STATUS_PENDING ((NTSTATUS) 0x00000103L) +#endif + +#ifndef STATUS_CANCELLED +#define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L) +#endif + +typedef struct _IO_STATUS_BLOCK { + NTSTATUS Status; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef VOID(NTAPI* PIO_APC_ROUTINE)(PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG Reserved); + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +#define RTL_CONSTANT_STRING(s) \ + { sizeof(s) - sizeof((s)[0]), sizeof(s), s } + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; + +#define RTL_CONSTANT_OBJECT_ATTRIBUTES(ObjectName, Attributes) \ + { sizeof(OBJECT_ATTRIBUTES), NULL, ObjectName, Attributes, NULL, NULL } + +#ifndef FILE_OPEN +#define FILE_OPEN 0x00000001UL +#endif + +#define KEYEDEVENT_WAIT 0x00000001UL +#define KEYEDEVENT_WAKE 0x00000002UL +#define KEYEDEVENT_ALL_ACCESS \ + (STANDARD_RIGHTS_REQUIRED | KEYEDEVENT_WAIT | KEYEDEVENT_WAKE) + +#define NT_NTDLL_IMPORT_LIST(X) \ + X(NTSTATUS, \ + NTAPI, \ + NtCreateFile, \ + (PHANDLE FileHandle, \ + ACCESS_MASK DesiredAccess, \ + POBJECT_ATTRIBUTES ObjectAttributes, \ + PIO_STATUS_BLOCK IoStatusBlock, \ + PLARGE_INTEGER AllocationSize, \ + ULONG FileAttributes, \ + ULONG ShareAccess, \ + ULONG CreateDisposition, \ + ULONG CreateOptions, \ + PVOID EaBuffer, \ + ULONG EaLength)) \ + \ + X(NTSTATUS, \ + NTAPI, \ + NtCreateKeyedEvent, \ + (PHANDLE KeyedEventHandle, \ + ACCESS_MASK DesiredAccess, \ + POBJECT_ATTRIBUTES ObjectAttributes, \ + ULONG Flags)) \ + \ + X(NTSTATUS, \ + NTAPI, \ + NtDeviceIoControlFile, \ + (HANDLE FileHandle, \ + HANDLE Event, \ + PIO_APC_ROUTINE ApcRoutine, \ + PVOID ApcContext, \ + PIO_STATUS_BLOCK IoStatusBlock, \ + ULONG IoControlCode, \ + PVOID InputBuffer, \ + ULONG InputBufferLength, \ + PVOID OutputBuffer, \ + ULONG OutputBufferLength)) \ + \ + X(NTSTATUS, \ + NTAPI, \ + NtReleaseKeyedEvent, \ + (HANDLE KeyedEventHandle, \ + PVOID KeyValue, \ + BOOLEAN Alertable, \ + PLARGE_INTEGER Timeout)) \ + \ + X(NTSTATUS, \ + NTAPI, \ + NtWaitForKeyedEvent, \ + (HANDLE KeyedEventHandle, \ + PVOID KeyValue, \ + BOOLEAN Alertable, \ + PLARGE_INTEGER Timeout)) \ + \ + X(ULONG, WINAPI, RtlNtStatusToDosError, (NTSTATUS Status)) + +#define X(return_type, attributes, name, parameters) \ + WEPOLL_INTERNAL_VAR return_type(attributes* name) parameters; +NT_NTDLL_IMPORT_LIST(X) +#undef X + +#include <assert.h> +#include <stddef.h> + +#ifndef _SSIZE_T_DEFINED +typedef intptr_t ssize_t; +#endif + +#define array_count(a) (sizeof(a) / (sizeof((a)[0]))) + +#define container_of(ptr, type, member) \ + ((type*) ((uintptr_t) (ptr) - offsetof(type, member))) + +#define unused_var(v) ((void) (v)) + +/* Polyfill `inline` for older versions of msvc (up to Visual Studio 2013) */ +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define inline __inline +#endif + +#define AFD_POLL_RECEIVE 0x0001 +#define AFD_POLL_RECEIVE_EXPEDITED 0x0002 +#define AFD_POLL_SEND 0x0004 +#define AFD_POLL_DISCONNECT 0x0008 +#define AFD_POLL_ABORT 0x0010 +#define AFD_POLL_LOCAL_CLOSE 0x0020 +#define AFD_POLL_ACCEPT 0x0080 +#define AFD_POLL_CONNECT_FAIL 0x0100 + +typedef struct _AFD_POLL_HANDLE_INFO { + HANDLE Handle; + ULONG Events; + NTSTATUS Status; +} AFD_POLL_HANDLE_INFO, *PAFD_POLL_HANDLE_INFO; + +typedef struct _AFD_POLL_INFO { + LARGE_INTEGER Timeout; + ULONG NumberOfHandles; + ULONG Exclusive; + AFD_POLL_HANDLE_INFO Handles[1]; +} AFD_POLL_INFO, *PAFD_POLL_INFO; + +WEPOLL_INTERNAL int afd_create_helper_handle(HANDLE iocp, + HANDLE* afd_helper_handle_out); + +WEPOLL_INTERNAL int afd_poll(HANDLE afd_helper_handle, + AFD_POLL_INFO* poll_info, + OVERLAPPED* overlapped); + +#define return_map_error(value) \ + do { \ + err_map_win_error(); \ + return (value); \ + } while (0) + +#define return_set_error(value, error) \ + do { \ + err_set_win_error(error); \ + return (value); \ + } while (0) + +WEPOLL_INTERNAL void err_map_win_error(void); +WEPOLL_INTERNAL void err_set_win_error(DWORD error); +WEPOLL_INTERNAL int err_check_handle(HANDLE handle); + +WEPOLL_INTERNAL int ws_global_init(void); +WEPOLL_INTERNAL SOCKET ws_get_base_socket(SOCKET socket); + +#define IOCTL_AFD_POLL 0x00012024 + +static UNICODE_STRING afd__helper_name = + RTL_CONSTANT_STRING(L"\\Device\\Afd\\Wepoll"); + +static OBJECT_ATTRIBUTES afd__helper_attributes = + RTL_CONSTANT_OBJECT_ATTRIBUTES(&afd__helper_name, 0); + +int afd_create_helper_handle(HANDLE iocp, HANDLE* afd_helper_handle_out) { + HANDLE afd_helper_handle; + IO_STATUS_BLOCK iosb; + NTSTATUS status; + + /* By opening \Device\Afd without specifying any extended attributes, we'll + * get a handle that lets us talk to the AFD driver, but that doesn't have an + * associated endpoint (so it's not a socket). */ + status = NtCreateFile(&afd_helper_handle, + SYNCHRONIZE, + &afd__helper_attributes, + &iosb, + NULL, + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + 0, + NULL, + 0); + if (status != STATUS_SUCCESS) + return_set_error(-1, RtlNtStatusToDosError(status)); + + if (CreateIoCompletionPort(afd_helper_handle, iocp, 0, 0) == NULL) + goto error; + + if (!SetFileCompletionNotificationModes(afd_helper_handle, + FILE_SKIP_SET_EVENT_ON_HANDLE)) + goto error; + + *afd_helper_handle_out = afd_helper_handle; + return 0; + +error: + CloseHandle(afd_helper_handle); + return_map_error(-1); +} + +int afd_poll(HANDLE afd_helper_handle, + AFD_POLL_INFO* poll_info, + OVERLAPPED* overlapped) { + IO_STATUS_BLOCK* iosb; + HANDLE event; + void* apc_context; + NTSTATUS status; + + /* Blocking operation is not supported. */ + assert(overlapped != NULL); + + iosb = (IO_STATUS_BLOCK*) &overlapped->Internal; + event = overlapped->hEvent; + + /* Do what other windows APIs would do: if hEvent has it's lowest bit set, + * don't post a completion to the completion port. */ + if ((uintptr_t) event & 1) { + event = (HANDLE)((uintptr_t) event & ~(uintptr_t) 1); + apc_context = NULL; + } else { + apc_context = overlapped; + } + + iosb->Status = STATUS_PENDING; + status = NtDeviceIoControlFile(afd_helper_handle, + event, + NULL, + apc_context, + iosb, + IOCTL_AFD_POLL, + poll_info, + sizeof *poll_info, + poll_info, + sizeof *poll_info); + + if (status == STATUS_SUCCESS) + return 0; + else if (status == STATUS_PENDING) + return_set_error(-1, ERROR_IO_PENDING); + else + return_set_error(-1, RtlNtStatusToDosError(status)); +} + +WEPOLL_INTERNAL int epoll_global_init(void); + +WEPOLL_INTERNAL int init(void); + +#include <stdbool.h> + +typedef struct queue_node queue_node_t; + +typedef struct queue_node { + queue_node_t* prev; + queue_node_t* next; +} queue_node_t; + +typedef struct queue { + queue_node_t head; +} queue_t; + +WEPOLL_INTERNAL void queue_init(queue_t* queue); +WEPOLL_INTERNAL void queue_node_init(queue_node_t* node); + +WEPOLL_INTERNAL queue_node_t* queue_first(const queue_t* queue); +WEPOLL_INTERNAL queue_node_t* queue_last(const queue_t* queue); + +WEPOLL_INTERNAL void queue_prepend(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_append(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_move_first(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_move_last(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_remove(queue_node_t* node); + +WEPOLL_INTERNAL bool queue_empty(const queue_t* queue); +WEPOLL_INTERNAL bool queue_enqueued(const queue_node_t* node); + +typedef struct port_state port_state_t; +typedef struct poll_group poll_group_t; + +WEPOLL_INTERNAL poll_group_t* poll_group_acquire(port_state_t* port); +WEPOLL_INTERNAL void poll_group_release(poll_group_t* poll_group); + +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 HANDLE + poll_group_get_afd_helper_handle(poll_group_t* poll_group); + +/* N.b.: the tree functions do not set errno or LastError when they fail. Each + * of the API functions has at most one failure mode. It is up to the caller to + * set an appropriate error code when necessary. */ + +typedef struct tree tree_t; +typedef struct tree_node tree_node_t; + +typedef struct tree { + tree_node_t* root; +} tree_t; + +typedef struct tree_node { + tree_node_t* left; + tree_node_t* right; + tree_node_t* parent; + uintptr_t key; + bool red; +} tree_node_t; + +WEPOLL_INTERNAL void tree_init(tree_t* tree); +WEPOLL_INTERNAL void tree_node_init(tree_node_t* node); + +WEPOLL_INTERNAL int tree_add(tree_t* tree, tree_node_t* node, uintptr_t key); +WEPOLL_INTERNAL void tree_del(tree_t* tree, tree_node_t* node); + +WEPOLL_INTERNAL tree_node_t* tree_find(const tree_t* tree, uintptr_t key); +WEPOLL_INTERNAL tree_node_t* tree_root(const tree_t* tree); + +typedef struct port_state port_state_t; +typedef struct sock_state sock_state_t; + +WEPOLL_INTERNAL sock_state_t* sock_new(port_state_t* port_state, + SOCKET socket); +WEPOLL_INTERNAL void sock_delete(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL void sock_force_delete(port_state_t* port_state, + sock_state_t* sock_state); + +WEPOLL_INTERNAL int sock_set_event(port_state_t* port_state, + sock_state_t* sock_state, + const struct epoll_event* ev); + +WEPOLL_INTERNAL int sock_update(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL int sock_feed_event(port_state_t* port_state, + OVERLAPPED* overlapped, + struct epoll_event* ev); + +WEPOLL_INTERNAL sock_state_t* sock_state_from_queue_node( + queue_node_t* queue_node); +WEPOLL_INTERNAL queue_node_t* sock_state_to_queue_node( + sock_state_t* sock_state); +WEPOLL_INTERNAL sock_state_t* sock_state_from_tree_node( + tree_node_t* tree_node); +WEPOLL_INTERNAL tree_node_t* sock_state_to_tree_node(sock_state_t* sock_state); + +/* The reflock is a special kind of lock that normally prevents a chunk of + * memory from being freed, but does allow the chunk of memory to eventually be + * released in a coordinated fashion. + * + * Under normal operation, threads increase and decrease the reference count, + * which are wait-free operations. + * + * Exactly once during the reflock's lifecycle, a thread holding a reference to + * the lock may "destroy" the lock; this operation blocks until all other + * threads holding a reference to the lock have dereferenced it. After + * "destroy" returns, the calling thread may assume that no other threads have + * a reference to the lock. + * + * Attemmpting to lock or destroy a lock after reflock_unref_and_destroy() has + * been called is invalid and results in undefined behavior. Therefore the user + * should use another lock to guarantee that this can't happen. + */ + +typedef struct reflock { + volatile long state; /* 32-bit Interlocked APIs operate on `long` values. */ +} reflock_t; + +WEPOLL_INTERNAL int reflock_global_init(void); + +WEPOLL_INTERNAL void reflock_init(reflock_t* reflock); +WEPOLL_INTERNAL void reflock_ref(reflock_t* reflock); +WEPOLL_INTERNAL void reflock_unref(reflock_t* reflock); +WEPOLL_INTERNAL void reflock_unref_and_destroy(reflock_t* reflock); + +typedef struct ts_tree { + tree_t tree; + SRWLOCK lock; +} ts_tree_t; + +typedef struct ts_tree_node { + tree_node_t tree_node; + reflock_t reflock; +} ts_tree_node_t; + +WEPOLL_INTERNAL void ts_tree_init(ts_tree_t* rtl); +WEPOLL_INTERNAL void ts_tree_node_init(ts_tree_node_t* node); + +WEPOLL_INTERNAL int ts_tree_add(ts_tree_t* ts_tree, + ts_tree_node_t* node, + uintptr_t key); + +WEPOLL_INTERNAL ts_tree_node_t* ts_tree_del_and_ref(ts_tree_t* ts_tree, + uintptr_t key); +WEPOLL_INTERNAL ts_tree_node_t* ts_tree_find_and_ref(ts_tree_t* ts_tree, + uintptr_t key); + +WEPOLL_INTERNAL void ts_tree_node_unref(ts_tree_node_t* node); +WEPOLL_INTERNAL void ts_tree_node_unref_and_destroy(ts_tree_node_t* node); + +typedef struct port_state port_state_t; +typedef struct sock_state sock_state_t; + +typedef struct port_state { + HANDLE iocp; + tree_t sock_tree; + queue_t sock_update_queue; + queue_t sock_deleted_queue; + queue_t poll_group_queue; + ts_tree_node_t handle_tree_node; + CRITICAL_SECTION lock; + size_t active_poll_count; +} port_state_t; + +WEPOLL_INTERNAL port_state_t* port_new(HANDLE* iocp_out); +WEPOLL_INTERNAL int port_close(port_state_t* port_state); +WEPOLL_INTERNAL int port_delete(port_state_t* port_state); + +WEPOLL_INTERNAL int port_wait(port_state_t* port_state, + struct epoll_event* events, + int maxevents, + int timeout); + +WEPOLL_INTERNAL int port_ctl(port_state_t* port_state, + int op, + SOCKET sock, + struct epoll_event* ev); + +WEPOLL_INTERNAL int port_register_socket_handle(port_state_t* port_state, + sock_state_t* sock_state, + SOCKET socket); +WEPOLL_INTERNAL void port_unregister_socket_handle(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL sock_state_t* port_find_socket(port_state_t* port_state, + SOCKET socket); + +WEPOLL_INTERNAL void port_request_socket_update(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL void port_cancel_socket_update(port_state_t* port_state, + sock_state_t* sock_state); + +WEPOLL_INTERNAL void port_add_deleted_socket(port_state_t* port_state, + sock_state_t* sock_state); +WEPOLL_INTERNAL void port_remove_deleted_socket(port_state_t* port_state, + sock_state_t* sock_state); + +static ts_tree_t epoll__handle_tree; + +static inline port_state_t* epoll__handle_tree_node_to_port( + ts_tree_node_t* tree_node) { + return container_of(tree_node, port_state_t, handle_tree_node); +} + +int epoll_global_init(void) { + ts_tree_init(&epoll__handle_tree); + return 0; +} + +static HANDLE epoll__create(void) { + port_state_t* port_state; + HANDLE ephnd; + + if (init() < 0) + return NULL; + + port_state = port_new(&ephnd); + if (port_state == NULL) + return NULL; + + if (ts_tree_add(&epoll__handle_tree, + &port_state->handle_tree_node, + (uintptr_t) ephnd) < 0) { + /* This should never happen. */ + port_delete(port_state); + return_set_error(NULL, ERROR_ALREADY_EXISTS); + } + + return ephnd; +} + +HANDLE epoll_create(int size) { + if (size <= 0) + return_set_error(NULL, ERROR_INVALID_PARAMETER); + + return epoll__create(); +} + +HANDLE epoll_create1(int flags) { + if (flags != 0) + return_set_error(NULL, ERROR_INVALID_PARAMETER); + + return epoll__create(); +} + +int epoll_close(HANDLE ephnd) { + ts_tree_node_t* tree_node; + port_state_t* port_state; + + if (init() < 0) + return -1; + + tree_node = ts_tree_del_and_ref(&epoll__handle_tree, (uintptr_t) ephnd); + if (tree_node == NULL) { + err_set_win_error(ERROR_INVALID_PARAMETER); + goto err; + } + + port_state = epoll__handle_tree_node_to_port(tree_node); + port_close(port_state); + + ts_tree_node_unref_and_destroy(tree_node); + + return port_delete(port_state); + +err: + err_check_handle(ephnd); + return -1; +} + +int epoll_ctl(HANDLE ephnd, int op, SOCKET sock, struct epoll_event* ev) { + ts_tree_node_t* tree_node; + port_state_t* port_state; + int r; + + if (init() < 0) + return -1; + + tree_node = ts_tree_find_and_ref(&epoll__handle_tree, (uintptr_t) ephnd); + if (tree_node == NULL) { + err_set_win_error(ERROR_INVALID_PARAMETER); + goto err; + } + + port_state = epoll__handle_tree_node_to_port(tree_node); + r = port_ctl(port_state, op, sock, ev); + + ts_tree_node_unref(tree_node); + + if (r < 0) + goto err; + + return 0; + +err: + /* On Linux, in the case of epoll_ctl_mod(), EBADF takes priority over other + * errors. Wepoll mimics this behavior. */ + err_check_handle(ephnd); + err_check_handle((HANDLE) sock); + return -1; +} + +int epoll_wait(HANDLE ephnd, + struct epoll_event* events, + int maxevents, + int timeout) { + ts_tree_node_t* tree_node; + port_state_t* port_state; + int num_events; + + if (maxevents <= 0) + return_set_error(-1, ERROR_INVALID_PARAMETER); + + if (init() < 0) + return -1; + + tree_node = ts_tree_find_and_ref(&epoll__handle_tree, (uintptr_t) ephnd); + if (tree_node == NULL) { + err_set_win_error(ERROR_INVALID_PARAMETER); + goto err; + } + + port_state = epoll__handle_tree_node_to_port(tree_node); + num_events = port_wait(port_state, events, maxevents, timeout); + + ts_tree_node_unref(tree_node); + + if (num_events < 0) + goto err; + + return num_events; + +err: + err_check_handle(ephnd); + return -1; +} + +#include <errno.h> + +#define ERR__ERRNO_MAPPINGS(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) + +static errno_t err__map_win_error_to_errno(DWORD error) { + switch (error) { +#define X(error_sym, errno_sym) \ + case error_sym: \ + return errno_sym; + ERR__ERRNO_MAPPINGS(X) +#undef X + } + return EINVAL; +} + +void err_map_win_error(void) { + errno = err__map_win_error_to_errno(GetLastError()); +} + +void err_set_win_error(DWORD error) { + SetLastError(error); + errno = err__map_win_error_to_errno(error); +} + +int err_check_handle(HANDLE handle) { + DWORD flags; + + /* GetHandleInformation() succeeds when passed INVALID_HANDLE_VALUE, so check + * for this condition explicitly. */ + if (handle == INVALID_HANDLE_VALUE) + return_set_error(-1, ERROR_INVALID_HANDLE); + + if (!GetHandleInformation(handle, &flags)) + return_map_error(-1); + + return 0; +} + +static bool init__done = false; +static INIT_ONCE init__once = INIT_ONCE_STATIC_INIT; + +static BOOL CALLBACK init__once_callback(INIT_ONCE* once, + void* parameter, + void** context) { + unused_var(once); + unused_var(parameter); + unused_var(context); + + /* N.b. that initialization order matters here. */ + if (ws_global_init() < 0 || nt_global_init() < 0 || + reflock_global_init() < 0 || epoll_global_init() < 0) + return FALSE; + + init__done = true; + return TRUE; +} + +int init(void) { + if (!init__done && + !InitOnceExecuteOnce(&init__once, init__once_callback, NULL, NULL)) + /* `InitOnceExecuteOnce()` itself is infallible, and it doesn't set any + * error code when the once-callback returns FALSE. We return -1 here to + * indicate that global initialization failed; the failing init function is + * resposible for setting `errno` and calling `SetLastError()`. */ + return -1; + + return 0; +} + +/* Set up a workaround for the following problem: + * FARPROC addr = GetProcAddress(...); + * MY_FUNC func = (MY_FUNC) addr; <-- GCC 8 warning/error. + * MY_FUNC func = (MY_FUNC) (void*) addr; <-- MSVC warning/error. + * To compile cleanly with either compiler, do casts with this "bridge" type: + * MY_FUNC func = (MY_FUNC) (nt__fn_ptr_cast_t) addr; */ +#ifdef __GNUC__ +typedef void* nt__fn_ptr_cast_t; +#else +typedef FARPROC nt__fn_ptr_cast_t; +#endif + +#define X(return_type, attributes, name, parameters) \ + WEPOLL_INTERNAL return_type(attributes* name) parameters = NULL; +NT_NTDLL_IMPORT_LIST(X) +#undef X + +int nt_global_init(void) { + HMODULE ntdll; + FARPROC fn_ptr; + + ntdll = GetModuleHandleW(L"ntdll.dll"); + if (ntdll == NULL) + return -1; + +#define X(return_type, attributes, name, parameters) \ + fn_ptr = GetProcAddress(ntdll, #name); \ + if (fn_ptr == NULL) \ + return -1; \ + name = (return_type(attributes*) parameters)(nt__fn_ptr_cast_t) fn_ptr; + NT_NTDLL_IMPORT_LIST(X) +#undef X + + return 0; +} + +#include <string.h> + +static const size_t POLL_GROUP__MAX_GROUP_SIZE = 32; + +typedef struct poll_group { + port_state_t* port_state; + queue_node_t queue_node; + HANDLE afd_helper_handle; + size_t group_size; +} poll_group_t; + +static poll_group_t* poll_group__new(port_state_t* port_state) { + poll_group_t* poll_group = malloc(sizeof *poll_group); + if (poll_group == NULL) + return_set_error(NULL, ERROR_NOT_ENOUGH_MEMORY); + + memset(poll_group, 0, sizeof *poll_group); + + queue_node_init(&poll_group->queue_node); + poll_group->port_state = port_state; + + if (afd_create_helper_handle(port_state->iocp, + &poll_group->afd_helper_handle) < 0) { + free(poll_group); + return NULL; + } + + queue_append(&port_state->poll_group_queue, &poll_group->queue_node); + + return poll_group; +} + +void poll_group_delete(poll_group_t* poll_group) { + assert(poll_group->group_size == 0); + CloseHandle(poll_group->afd_helper_handle); + 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); +} + +HANDLE poll_group_get_afd_helper_handle(poll_group_t* poll_group) { + return poll_group->afd_helper_handle; +} + +poll_group_t* poll_group_acquire(port_state_t* port_state) { + queue_t* queue = &port_state->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_GROUP_SIZE) + poll_group = poll_group__new(port_state); + if (poll_group == NULL) + return NULL; + + if (++poll_group->group_size == POLL_GROUP__MAX_GROUP_SIZE) + queue_move_first(&port_state->poll_group_queue, &poll_group->queue_node); + + return poll_group; +} + +void poll_group_release(poll_group_t* poll_group) { + port_state_t* port_state = poll_group->port_state; + + poll_group->group_size--; + assert(poll_group->group_size < POLL_GROUP__MAX_GROUP_SIZE); + + queue_move_last(&port_state->poll_group_queue, &poll_group->queue_node); + + /* Poll groups are currently only freed when the epoll port is closed. */ +} + +#define PORT__MAX_ON_STACK_COMPLETIONS 256 + +static port_state_t* port__alloc(void) { + port_state_t* port_state = malloc(sizeof *port_state); + if (port_state == NULL) + return_set_error(NULL, ERROR_NOT_ENOUGH_MEMORY); + + return port_state; +} + +static void port__free(port_state_t* port) { + assert(port != NULL); + free(port); +} + +static HANDLE port__create_iocp(void) { + HANDLE iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (iocp == NULL) + return_map_error(NULL); + + return iocp; +} + +port_state_t* port_new(HANDLE* iocp_out) { + port_state_t* port_state; + HANDLE iocp; + + port_state = port__alloc(); + if (port_state == NULL) + goto err1; + + iocp = port__create_iocp(); + if (iocp == NULL) + goto err2; + + memset(port_state, 0, sizeof *port_state); + + port_state->iocp = iocp; + tree_init(&port_state->sock_tree); + queue_init(&port_state->sock_update_queue); + queue_init(&port_state->sock_deleted_queue); + queue_init(&port_state->poll_group_queue); + ts_tree_node_init(&port_state->handle_tree_node); + InitializeCriticalSection(&port_state->lock); + + *iocp_out = iocp; + return port_state; + +err2: + port__free(port_state); +err1: + return NULL; +} + +static int port__close_iocp(port_state_t* port_state) { + HANDLE iocp = port_state->iocp; + port_state->iocp = NULL; + + if (!CloseHandle(iocp)) + return_map_error(-1); + + return 0; +} + +int port_close(port_state_t* port_state) { + int result; + + EnterCriticalSection(&port_state->lock); + result = port__close_iocp(port_state); + LeaveCriticalSection(&port_state->lock); + + return result; +} + +int port_delete(port_state_t* port_state) { + tree_node_t* tree_node; + queue_node_t* queue_node; + + /* At this point the IOCP port should have been closed. */ + assert(port_state->iocp == NULL); + + while ((tree_node = tree_root(&port_state->sock_tree)) != NULL) { + sock_state_t* sock_state = sock_state_from_tree_node(tree_node); + sock_force_delete(port_state, sock_state); + } + + while ((queue_node = queue_first(&port_state->sock_deleted_queue)) != NULL) { + sock_state_t* sock_state = sock_state_from_queue_node(queue_node); + sock_force_delete(port_state, sock_state); + } + + while ((queue_node = queue_first(&port_state->poll_group_queue)) != NULL) { + poll_group_t* poll_group = poll_group_from_queue_node(queue_node); + poll_group_delete(poll_group); + } + + assert(queue_empty(&port_state->sock_update_queue)); + + DeleteCriticalSection(&port_state->lock); + + port__free(port_state); + + return 0; +} + +static int port__update_events(port_state_t* port_state) { + queue_t* sock_update_queue = &port_state->sock_update_queue; + + /* Walk the queue, submitting new poll requests for every socket that needs + * it. */ + while (!queue_empty(sock_update_queue)) { + queue_node_t* queue_node = queue_first(sock_update_queue); + sock_state_t* sock_state = sock_state_from_queue_node(queue_node); + + if (sock_update(port_state, sock_state) < 0) + return -1; + + /* sock_update() removes the socket from the update queue. */ + } + + return 0; +} + +static void port__update_events_if_polling(port_state_t* port_state) { + if (port_state->active_poll_count > 0) + port__update_events(port_state); +} + +static int port__feed_events(port_state_t* port_state, + struct epoll_event* epoll_events, + OVERLAPPED_ENTRY* iocp_events, + DWORD iocp_event_count) { + int epoll_event_count = 0; + DWORD i; + + for (i = 0; i < iocp_event_count; i++) { + OVERLAPPED* overlapped = iocp_events[i].lpOverlapped; + struct epoll_event* ev = &epoll_events[epoll_event_count]; + + epoll_event_count += sock_feed_event(port_state, overlapped, ev); + } + + return epoll_event_count; +} + +static int port__poll(port_state_t* port_state, + struct epoll_event* epoll_events, + OVERLAPPED_ENTRY* iocp_events, + DWORD maxevents, + DWORD timeout) { + DWORD completion_count; + + if (port__update_events(port_state) < 0) + return -1; + + port_state->active_poll_count++; + + LeaveCriticalSection(&port_state->lock); + + BOOL r = GetQueuedCompletionStatusEx(port_state->iocp, + iocp_events, + maxevents, + &completion_count, + timeout, + FALSE); + + EnterCriticalSection(&port_state->lock); + + port_state->active_poll_count--; + + if (!r) + return_map_error(-1); + + return port__feed_events( + port_state, epoll_events, iocp_events, completion_count); +} + +int port_wait(port_state_t* port_state, + struct epoll_event* events, + int maxevents, + int timeout) { + OVERLAPPED_ENTRY stack_iocp_events[PORT__MAX_ON_STACK_COMPLETIONS]; + OVERLAPPED_ENTRY* iocp_events; + uint64_t due = 0; + DWORD gqcs_timeout; + int result; + + /* Check whether `maxevents` is in range. */ + if (maxevents <= 0) + return_set_error(-1, ERROR_INVALID_PARAMETER); + + /* Decide whether the IOCP completion list can live on the stack, or allocate + * memory for it on the heap. */ + if ((size_t) maxevents <= array_count(stack_iocp_events)) { + iocp_events = stack_iocp_events; + } else if ((iocp_events = + malloc((size_t) maxevents * sizeof *iocp_events)) == NULL) { + iocp_events = stack_iocp_events; + maxevents = array_count(stack_iocp_events); + } + + /* Compute the timeout for GetQueuedCompletionStatus, and the wait end + * time, if the user specified a timeout other than zero or infinite. */ + if (timeout > 0) { + due = GetTickCount64() + (uint64_t) timeout; + gqcs_timeout = (DWORD) timeout; + } else if (timeout == 0) { + gqcs_timeout = 0; + } else { + gqcs_timeout = INFINITE; + } + + EnterCriticalSection(&port_state->lock); + + /* Dequeue completion packets until either at least one interesting event + * has been discovered, or the timeout is reached. */ + for (;;) { + uint64_t now; + + result = port__poll( + port_state, events, iocp_events, (DWORD) maxevents, gqcs_timeout); + if (result < 0 || result > 0) + break; /* Result, error, or time-out. */ + + if (timeout < 0) + continue; /* When timeout is negative, never time out. */ + + /* Update time. */ + now = GetTickCount64(); + + /* 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); + } + + port__update_events_if_polling(port_state); + + LeaveCriticalSection(&port_state->lock); + + if (iocp_events != stack_iocp_events) + free(iocp_events); + + if (result >= 0) + return result; + else if (GetLastError() == WAIT_TIMEOUT) + return 0; + else + return -1; +} + +static int port__ctl_add(port_state_t* port_state, + SOCKET sock, + struct epoll_event* ev) { + sock_state_t* sock_state = sock_new(port_state, sock); + if (sock_state == NULL) + return -1; + + if (sock_set_event(port_state, sock_state, ev) < 0) { + sock_delete(port_state, sock_state); + return -1; + } + + port__update_events_if_polling(port_state); + + return 0; +} + +static int port__ctl_mod(port_state_t* port_state, + SOCKET sock, + struct epoll_event* ev) { + sock_state_t* sock_state = port_find_socket(port_state, sock); + if (sock_state == NULL) + return -1; + + if (sock_set_event(port_state, sock_state, ev) < 0) + return -1; + + port__update_events_if_polling(port_state); + + return 0; +} + +static int port__ctl_del(port_state_t* port_state, SOCKET sock) { + sock_state_t* sock_state = port_find_socket(port_state, sock); + if (sock_state == NULL) + return -1; + + sock_delete(port_state, sock_state); + + return 0; +} + +static int port__ctl_op(port_state_t* port_state, + int op, + SOCKET sock, + struct epoll_event* ev) { + switch (op) { + case EPOLL_CTL_ADD: + return port__ctl_add(port_state, sock, ev); + case EPOLL_CTL_MOD: + return port__ctl_mod(port_state, sock, ev); + case EPOLL_CTL_DEL: + return port__ctl_del(port_state, sock); + default: + return_set_error(-1, ERROR_INVALID_PARAMETER); + } +} + +int port_ctl(port_state_t* port_state, + int op, + SOCKET sock, + struct epoll_event* ev) { + int result; + + EnterCriticalSection(&port_state->lock); + result = port__ctl_op(port_state, op, sock, ev); + LeaveCriticalSection(&port_state->lock); + + return result; +} + +int port_register_socket_handle(port_state_t* port_state, + sock_state_t* sock_state, + SOCKET socket) { + if (tree_add(&port_state->sock_tree, + sock_state_to_tree_node(sock_state), + socket) < 0) + return_set_error(-1, ERROR_ALREADY_EXISTS); + return 0; +} + +void port_unregister_socket_handle(port_state_t* port_state, + sock_state_t* sock_state) { + tree_del(&port_state->sock_tree, sock_state_to_tree_node(sock_state)); +} + +sock_state_t* port_find_socket(port_state_t* port_state, SOCKET socket) { + tree_node_t* tree_node = tree_find(&port_state->sock_tree, socket); + if (tree_node == NULL) + return_set_error(NULL, ERROR_NOT_FOUND); + return sock_state_from_tree_node(tree_node); +} + +void port_request_socket_update(port_state_t* port_state, + sock_state_t* sock_state) { + if (queue_enqueued(sock_state_to_queue_node(sock_state))) + return; + queue_append(&port_state->sock_update_queue, + sock_state_to_queue_node(sock_state)); +} + +void port_cancel_socket_update(port_state_t* port_state, + sock_state_t* sock_state) { + unused_var(port_state); + if (!queue_enqueued(sock_state_to_queue_node(sock_state))) + return; + queue_remove(sock_state_to_queue_node(sock_state)); +} + +void port_add_deleted_socket(port_state_t* port_state, + sock_state_t* sock_state) { + if (queue_enqueued(sock_state_to_queue_node(sock_state))) + return; + queue_append(&port_state->sock_deleted_queue, + sock_state_to_queue_node(sock_state)); +} + +void port_remove_deleted_socket(port_state_t* port_state, + sock_state_t* sock_state) { + unused_var(port_state); + if (!queue_enqueued(sock_state_to_queue_node(sock_state))) + return; + queue_remove(sock_state_to_queue_node(sock_state)); +} + +void queue_init(queue_t* queue) { + queue_node_init(&queue->head); +} + +void queue_node_init(queue_node_t* node) { + node->prev = node; + node->next = node; +} + +static inline void queue__detach_node(queue_node_t* node) { + node->prev->next = node->next; + node->next->prev = node->prev; +} + +queue_node_t* queue_first(const queue_t* queue) { + return !queue_empty(queue) ? queue->head.next : NULL; +} + +queue_node_t* queue_last(const queue_t* queue) { + return !queue_empty(queue) ? queue->head.prev : NULL; +} + +void queue_prepend(queue_t* queue, queue_node_t* node) { + node->next = queue->head.next; + node->prev = &queue->head; + node->next->prev = node; + queue->head.next = node; +} + +void queue_append(queue_t* queue, queue_node_t* node) { + node->next = &queue->head; + node->prev = queue->head.prev; + node->prev->next = node; + queue->head.prev = node; +} + +void queue_move_first(queue_t* queue, queue_node_t* node) { + queue__detach_node(node); + queue_prepend(queue, node); +} + +void queue_move_last(queue_t* queue, queue_node_t* node) { + queue__detach_node(node); + queue_append(queue, node); +} + +void queue_remove(queue_node_t* node) { + queue__detach_node(node); + queue_node_init(node); +} + +bool queue_empty(const queue_t* queue) { + return !queue_enqueued(&queue->head); +} + +bool queue_enqueued(const queue_node_t* node) { + return node->prev != node; +} + +static const long REFLOCK__REF = (long) 0x00000001; +static const long REFLOCK__REF_MASK = (long) 0x0fffffff; +static const long REFLOCK__DESTROY = (long) 0x10000000; +static const long REFLOCK__DESTROY_MASK = (long) 0xf0000000; +static const long REFLOCK__POISON = (long) 0x300dead0; + +static HANDLE reflock__keyed_event = NULL; + +int reflock_global_init(void) { + NTSTATUS status = + NtCreateKeyedEvent(&reflock__keyed_event, KEYEDEVENT_ALL_ACCESS, NULL, 0); + if (status != STATUS_SUCCESS) + return_set_error(-1, RtlNtStatusToDosError(status)); + return 0; +} + +void reflock_init(reflock_t* reflock) { + reflock->state = 0; +} + +static void reflock__signal_event(void* address) { + NTSTATUS status = + NtReleaseKeyedEvent(reflock__keyed_event, address, FALSE, NULL); + if (status != STATUS_SUCCESS) + abort(); +} + +static void reflock__await_event(void* address) { + NTSTATUS status = + NtWaitForKeyedEvent(reflock__keyed_event, address, FALSE, NULL); + if (status != STATUS_SUCCESS) + abort(); +} + +void reflock_ref(reflock_t* reflock) { + long state = InterlockedAdd(&reflock->state, REFLOCK__REF); + + /* Verify that the counter didn't overflow and the lock isn't destroyed. */ + assert((state & REFLOCK__DESTROY_MASK) == 0); + unused_var(state); +} + +void reflock_unref(reflock_t* reflock) { + long state = InterlockedAdd(&reflock->state, -REFLOCK__REF); + + /* Verify that the lock was referenced and not already destroyed. */ + assert((state & REFLOCK__DESTROY_MASK & ~REFLOCK__DESTROY) == 0); + + if (state == REFLOCK__DESTROY) + reflock__signal_event(reflock); +} + +void reflock_unref_and_destroy(reflock_t* reflock) { + long state = + InterlockedAdd(&reflock->state, REFLOCK__DESTROY - REFLOCK__REF); + long ref_count = state & REFLOCK__REF_MASK; + + /* Verify that the lock was referenced and not already destroyed. */ + assert((state & REFLOCK__DESTROY_MASK) == REFLOCK__DESTROY); + + if (ref_count != 0) + reflock__await_event(reflock); + + state = InterlockedExchange(&reflock->state, REFLOCK__POISON); + assert(state == REFLOCK__DESTROY); +} + +static const uint32_t SOCK__KNOWN_EPOLL_EVENTS = + EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | + EPOLLRDBAND | EPOLLWRNORM | EPOLLWRBAND | EPOLLMSG | EPOLLRDHUP; + +typedef enum sock__poll_status { + SOCK__POLL_IDLE = 0, + SOCK__POLL_PENDING, + SOCK__POLL_CANCELLED +} sock__poll_status_t; + +typedef struct sock_state { + OVERLAPPED overlapped; + AFD_POLL_INFO poll_info; + queue_node_t queue_node; + tree_node_t tree_node; + poll_group_t* poll_group; + SOCKET base_socket; + epoll_data_t user_data; + uint32_t user_events; + uint32_t pending_events; + sock__poll_status_t poll_status; + bool delete_pending; +} sock_state_t; + +static inline sock_state_t* sock__alloc(void) { + sock_state_t* sock_state = malloc(sizeof *sock_state); + if (sock_state == NULL) + return_set_error(NULL, ERROR_NOT_ENOUGH_MEMORY); + return sock_state; +} + +static inline void sock__free(sock_state_t* sock_state) { + free(sock_state); +} + +static int sock__cancel_poll(sock_state_t* sock_state) { + HANDLE afd_helper_handle = + poll_group_get_afd_helper_handle(sock_state->poll_group); + assert(sock_state->poll_status == SOCK__POLL_PENDING); + + /* CancelIoEx() may fail with ERROR_NOT_FOUND if the overlapped operation has + * already completed. This is not a problem and we proceed normally. */ + if (!HasOverlappedIoCompleted(&sock_state->overlapped) && + !CancelIoEx(afd_helper_handle, &sock_state->overlapped) && + GetLastError() != ERROR_NOT_FOUND) + return_map_error(-1); + + sock_state->poll_status = SOCK__POLL_CANCELLED; + sock_state->pending_events = 0; + return 0; +} + +sock_state_t* sock_new(port_state_t* port_state, SOCKET socket) { + SOCKET base_socket; + poll_group_t* poll_group; + sock_state_t* sock_state; + + if (socket == 0 || socket == INVALID_SOCKET) + return_set_error(NULL, ERROR_INVALID_HANDLE); + + base_socket = ws_get_base_socket(socket); + if (base_socket == INVALID_SOCKET) + return NULL; + + poll_group = poll_group_acquire(port_state); + if (poll_group == NULL) + return NULL; + + sock_state = sock__alloc(); + if (sock_state == NULL) + goto err1; + + memset(sock_state, 0, sizeof *sock_state); + + sock_state->base_socket = base_socket; + sock_state->poll_group = poll_group; + + tree_node_init(&sock_state->tree_node); + queue_node_init(&sock_state->queue_node); + + if (port_register_socket_handle(port_state, sock_state, socket) < 0) + goto err2; + + return sock_state; + +err2: + sock__free(sock_state); +err1: + poll_group_release(poll_group); + + return NULL; +} + +static int sock__delete(port_state_t* port_state, + sock_state_t* sock_state, + bool force) { + if (!sock_state->delete_pending) { + if (sock_state->poll_status == SOCK__POLL_PENDING) + sock__cancel_poll(sock_state); + + port_cancel_socket_update(port_state, sock_state); + port_unregister_socket_handle(port_state, sock_state); + + sock_state->delete_pending = true; + } + + /* If the poll request still needs to complete, the sock_state object can't + * be free()d yet. `sock_feed_event()` or `port_close()` will take care + * of this later. */ + if (force || sock_state->poll_status == SOCK__POLL_IDLE) { + /* Free the sock_state now. */ + port_remove_deleted_socket(port_state, sock_state); + poll_group_release(sock_state->poll_group); + sock__free(sock_state); + } else { + /* Free the socket later. */ + port_add_deleted_socket(port_state, sock_state); + } + + return 0; +} + +void sock_delete(port_state_t* port_state, sock_state_t* sock_state) { + sock__delete(port_state, sock_state, false); +} + +void sock_force_delete(port_state_t* port_state, sock_state_t* sock_state) { + sock__delete(port_state, sock_state, true); +} + +int sock_set_event(port_state_t* port_state, + sock_state_t* sock_state, + const struct epoll_event* ev) { + /* EPOLLERR and EPOLLHUP are always reported, even when not requested by the + * caller. However they are disabled after a event has been reported for a + * socket for which the EPOLLONESHOT flag as set. */ + uint32_t events = ev->events | EPOLLERR | EPOLLHUP; + + sock_state->user_events = events; + sock_state->user_data = ev->data; + + if ((events & SOCK__KNOWN_EPOLL_EVENTS & ~sock_state->pending_events) != 0) + port_request_socket_update(port_state, sock_state); + + return 0; +} + +static inline DWORD sock__epoll_events_to_afd_events(uint32_t epoll_events) { + /* Always monitor for AFD_POLL_LOCAL_CLOSE, which is triggered when the + * socket is closed with closesocket() or CloseHandle(). */ + DWORD afd_events = AFD_POLL_LOCAL_CLOSE; + + if (epoll_events & (EPOLLIN | EPOLLRDNORM)) + afd_events |= AFD_POLL_RECEIVE | AFD_POLL_ACCEPT; + if (epoll_events & (EPOLLPRI | EPOLLRDBAND)) + afd_events |= AFD_POLL_RECEIVE_EXPEDITED; + if (epoll_events & (EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND)) + afd_events |= AFD_POLL_SEND; + if (epoll_events & (EPOLLIN | EPOLLRDNORM | EPOLLRDHUP)) + afd_events |= AFD_POLL_DISCONNECT; + if (epoll_events & EPOLLHUP) + afd_events |= AFD_POLL_ABORT; + if (epoll_events & EPOLLERR) + afd_events |= AFD_POLL_CONNECT_FAIL; + + return afd_events; +} + +static inline uint32_t sock__afd_events_to_epoll_events(DWORD afd_events) { + uint32_t epoll_events = 0; + + if (afd_events & (AFD_POLL_RECEIVE | AFD_POLL_ACCEPT)) + epoll_events |= EPOLLIN | EPOLLRDNORM; + if (afd_events & AFD_POLL_RECEIVE_EXPEDITED) + epoll_events |= EPOLLPRI | EPOLLRDBAND; + if (afd_events & AFD_POLL_SEND) + epoll_events |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND; + if (afd_events & AFD_POLL_DISCONNECT) + epoll_events |= EPOLLIN | EPOLLRDNORM | EPOLLRDHUP; + if (afd_events & AFD_POLL_ABORT) + epoll_events |= EPOLLHUP; + if (afd_events & AFD_POLL_CONNECT_FAIL) + /* Linux reports all these events after connect() has failed. */ + epoll_events |= + EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLRDNORM | EPOLLWRNORM | EPOLLRDHUP; + + return epoll_events; +} + +int sock_update(port_state_t* port_state, sock_state_t* sock_state) { + assert(!sock_state->delete_pending); + + if ((sock_state->poll_status == SOCK__POLL_PENDING) && + (sock_state->user_events & SOCK__KNOWN_EPOLL_EVENTS & + ~sock_state->pending_events) == 0) { + /* All the events the user is interested in are already being monitored by + * the pending poll operation. It might spuriously complete because of an + * event that we're no longer interested in; when that happens we'll submit + * a new poll operation with the updated event mask. */ + + } else if (sock_state->poll_status == SOCK__POLL_PENDING) { + /* A poll operation is already pending, but it's not monitoring for all the + * events that the user is interested in. Therefore, cancel the pending + * poll operation; when we receive it's completion package, a new poll + * operation will be submitted with the correct event mask. */ + if (sock__cancel_poll(sock_state) < 0) + return -1; + + } else if (sock_state->poll_status == SOCK__POLL_CANCELLED) { + /* The poll operation has already been cancelled, we're still waiting for + * it to return. For now, there's nothing that needs to be done. */ + + } else if (sock_state->poll_status == SOCK__POLL_IDLE) { + /* No poll operation is pending; start one. */ + sock_state->poll_info.Exclusive = FALSE; + sock_state->poll_info.NumberOfHandles = 1; + sock_state->poll_info.Timeout.QuadPart = INT64_MAX; + sock_state->poll_info.Handles[0].Handle = (HANDLE) sock_state->base_socket; + sock_state->poll_info.Handles[0].Status = 0; + sock_state->poll_info.Handles[0].Events = + sock__epoll_events_to_afd_events(sock_state->user_events); + + memset(&sock_state->overlapped, 0, sizeof sock_state->overlapped); + + if (afd_poll(poll_group_get_afd_helper_handle(sock_state->poll_group), + &sock_state->poll_info, + &sock_state->overlapped) < 0) { + switch (GetLastError()) { + case ERROR_IO_PENDING: + /* Overlapped poll operation in progress; this is expected. */ + break; + case ERROR_INVALID_HANDLE: + /* Socket closed; it'll be dropped from the epoll set. */ + return sock__delete(port_state, sock_state, false); + default: + /* Other errors are propagated to the caller. */ + return_map_error(-1); + } + } + + /* The poll request was successfully submitted. */ + sock_state->poll_status = SOCK__POLL_PENDING; + sock_state->pending_events = sock_state->user_events; + + } else { + /* Unreachable. */ + assert(false); + } + + port_cancel_socket_update(port_state, sock_state); + return 0; +} + +int sock_feed_event(port_state_t* port_state, + OVERLAPPED* overlapped, + struct epoll_event* ev) { + sock_state_t* sock_state = + container_of(overlapped, sock_state_t, overlapped); + AFD_POLL_INFO* poll_info = &sock_state->poll_info; + uint32_t epoll_events = 0; + + sock_state->poll_status = SOCK__POLL_IDLE; + sock_state->pending_events = 0; + + if (sock_state->delete_pending) { + /* Socket has been deleted earlier and can now be freed. */ + return sock__delete(port_state, sock_state, false); + + } else if ((NTSTATUS) overlapped->Internal == STATUS_CANCELLED) { + /* The poll request was cancelled by CancelIoEx. */ + + } else if (!NT_SUCCESS(overlapped->Internal)) { + /* The overlapped request itself failed in an unexpected way. */ + epoll_events = EPOLLERR; + + } else if (poll_info->NumberOfHandles < 1) { + /* This poll operation succeeded but didn't report any socket events. */ + + } else if (poll_info->Handles[0].Events & AFD_POLL_LOCAL_CLOSE) { + /* The poll operation reported that the socket was closed. */ + return sock__delete(port_state, sock_state, false); + + } else { + /* Events related to our socket were reported. */ + epoll_events = + sock__afd_events_to_epoll_events(poll_info->Handles[0].Events); + } + + /* Requeue the socket so a new poll request will be submitted. */ + port_request_socket_update(port_state, sock_state); + + /* Filter out events that the user didn't ask for. */ + epoll_events &= sock_state->user_events; + + /* Return if there are no epoll events to report. */ + if (epoll_events == 0) + return 0; + + /* If the the socket has the EPOLLONESHOT flag set, unmonitor all events, + * even EPOLLERR and EPOLLHUP. But always keep looking for closed sockets. */ + if (sock_state->user_events & EPOLLONESHOT) + sock_state->user_events = 0; + + ev->data = sock_state->user_data; + ev->events = epoll_events; + return 1; +} + +queue_node_t* sock_state_to_queue_node(sock_state_t* sock_state) { + return &sock_state->queue_node; +} + +sock_state_t* sock_state_from_tree_node(tree_node_t* tree_node) { + return container_of(tree_node, sock_state_t, tree_node); +} + +tree_node_t* sock_state_to_tree_node(sock_state_t* sock_state) { + return &sock_state->tree_node; +} + +sock_state_t* sock_state_from_queue_node(queue_node_t* queue_node) { + return container_of(queue_node, sock_state_t, queue_node); +} + +void ts_tree_init(ts_tree_t* ts_tree) { + tree_init(&ts_tree->tree); + InitializeSRWLock(&ts_tree->lock); +} + +void ts_tree_node_init(ts_tree_node_t* node) { + tree_node_init(&node->tree_node); + reflock_init(&node->reflock); +} + +int ts_tree_add(ts_tree_t* ts_tree, ts_tree_node_t* node, uintptr_t key) { + int r; + + AcquireSRWLockExclusive(&ts_tree->lock); + r = tree_add(&ts_tree->tree, &node->tree_node, key); + ReleaseSRWLockExclusive(&ts_tree->lock); + + return r; +} + +static inline ts_tree_node_t* ts_tree__find_node(ts_tree_t* ts_tree, + uintptr_t key) { + tree_node_t* tree_node = tree_find(&ts_tree->tree, key); + if (tree_node == NULL) + return NULL; + + return container_of(tree_node, ts_tree_node_t, tree_node); +} + +ts_tree_node_t* ts_tree_del_and_ref(ts_tree_t* ts_tree, uintptr_t key) { + ts_tree_node_t* ts_tree_node; + + AcquireSRWLockExclusive(&ts_tree->lock); + + ts_tree_node = ts_tree__find_node(ts_tree, key); + if (ts_tree_node != NULL) { + tree_del(&ts_tree->tree, &ts_tree_node->tree_node); + reflock_ref(&ts_tree_node->reflock); + } + + ReleaseSRWLockExclusive(&ts_tree->lock); + + return ts_tree_node; +} + +ts_tree_node_t* ts_tree_find_and_ref(ts_tree_t* ts_tree, uintptr_t key) { + ts_tree_node_t* ts_tree_node; + + AcquireSRWLockShared(&ts_tree->lock); + + ts_tree_node = ts_tree__find_node(ts_tree, key); + if (ts_tree_node != NULL) + reflock_ref(&ts_tree_node->reflock); + + ReleaseSRWLockShared(&ts_tree->lock); + + return ts_tree_node; +} + +void ts_tree_node_unref(ts_tree_node_t* node) { + reflock_unref(&node->reflock); +} + +void ts_tree_node_unref_and_destroy(ts_tree_node_t* node) { + reflock_unref_and_destroy(&node->reflock); +} + +void tree_init(tree_t* tree) { + memset(tree, 0, sizeof *tree); +} + +void tree_node_init(tree_node_t* node) { + memset(node, 0, sizeof *node); +} + +#define TREE__ROTATE(cis, trans) \ + tree_node_t* p = node; \ + tree_node_t* q = node->trans; \ + 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->trans = q->cis; \ + if (p->trans) \ + p->trans->parent = p; \ + q->cis = p; + +static inline void tree__rotate_left(tree_t* tree, tree_node_t* node) { + TREE__ROTATE(left, right) +} + +static inline void tree__rotate_right(tree_t* tree, tree_node_t* node) { + TREE__ROTATE(right, left) +} + +#define TREE__INSERT_OR_DESCEND(side) \ + if (parent->side) { \ + parent = parent->side; \ + } else { \ + parent->side = node; \ + break; \ + } + +#define TREE__FIXUP_AFTER_INSERT(cis, trans) \ + 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(tree, parent); \ + node = parent; \ + parent = node->parent; \ + } \ + parent->red = false; \ + grandparent->red = true; \ + tree__rotate_##trans(tree, grandparent); \ + } + +int tree_add(tree_t* tree, tree_node_t* node, uintptr_t key) { + tree_node_t* parent; + + parent = tree->root; + if (parent) { + for (;;) { + if (key < parent->key) { + TREE__INSERT_OR_DESCEND(left) + } else if (key > parent->key) { + TREE__INSERT_OR_DESCEND(right) + } else { + return -1; + } + } + } else { + tree->root = node; + } + + node->key = key; + node->left = node->right = NULL; + node->parent = parent; + node->red = true; + + for (; parent && parent->red; parent = node->parent) { + if (parent == parent->parent->left) { + TREE__FIXUP_AFTER_INSERT(left, right) + } else { + TREE__FIXUP_AFTER_INSERT(right, left) + } + } + tree->root->red = false; + + return 0; +} + +#define TREE__FIXUP_AFTER_REMOVE(cis, trans) \ + tree_node_t* sibling = parent->trans; \ + \ + if (sibling->red) { \ + sibling->red = false; \ + parent->red = true; \ + tree__rotate_##cis(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(tree, sibling); \ + sibling = parent->trans; \ + } \ + sibling->red = parent->red; \ + parent->red = sibling->trans->red = false; \ + tree__rotate_##cis(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; + bool red; + + if (!left) { + next = right; + } else if (!right) { + next = left; + } else { + next = right; + while (next->left) + next = next->left; + } + + if (parent) { + if (parent->left == node) + parent->left = next; + else + parent->right = next; + } else { + tree->root = next; + } + + if (left && right) { + red = next->red; + next->red = node->red; + next->left = left; + left->parent = next; + if (next != right) { + parent = next->parent; + next->parent = node->parent; + node = next->right; + parent->left = node; + next->right = right; + right->parent = next; + } else { + next->parent = parent; + parent = next; + node = next->right; + } + } else { + red = node->red; + node = next; + } + + if (node) + node->parent = parent; + if (red) + return; + if (node && node->red) { + node->red = false; + return; + } + + do { + if (node == tree->root) + break; + if (node == parent->left) { + TREE__FIXUP_AFTER_REMOVE(left, right) + } else { + TREE__FIXUP_AFTER_REMOVE(right, left) + } + node = parent; + parent = parent->parent; + } while (!node->red); + + if (node) + node->red = false; +} + +tree_node_t* tree_find(const tree_t* tree, uintptr_t key) { + tree_node_t* node = tree->root; + while (node) { + if (key < node->key) + node = node->left; + else if (key > node->key) + node = node->right; + else + return node; + } + return NULL; +} + +tree_node_t* tree_root(const tree_t* tree) { + return tree->root; +} + +#ifndef SIO_BASE_HANDLE +#define SIO_BASE_HANDLE 0x48000022 +#endif + +int ws_global_init(void) { + int r; + WSADATA wsa_data; + + r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (r != 0) + return_set_error(-1, (DWORD) r); + + 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_map_error(INVALID_SOCKET); + + return base_socket; +} diff --git a/3rdparty/wepoll/include/wepoll.h b/sources/wepoll/wepoll.h similarity index 52% rename from 3rdparty/wepoll/include/wepoll.h rename to sources/wepoll/wepoll.h index 4cca4c2d0..eebde2111 100644 --- a/3rdparty/wepoll/include/wepoll.h +++ b/sources/wepoll/wepoll.h @@ -1,3 +1,34 @@ +/* + * wepoll - epoll for Windows + * https://github.com/piscisaureus/wepoll + * + * Copyright 2012-2019, Bert Belder <bertbelder@gmail.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + #ifndef WEPOLL_H_ #define WEPOLL_H_ @@ -7,8 +38,6 @@ #include <stdint.h> -/* clang-format off */ - enum EPOLL_EVENTS { EPOLLIN = (int) (1U << 0), EPOLLPRI = (int) (1U << 1), @@ -41,8 +70,6 @@ enum EPOLL_EVENTS { #define EPOLL_CTL_MOD 2 #define EPOLL_CTL_DEL 3 -/* clang-format on */ - typedef void* HANDLE; typedef uintptr_t SOCKET; @@ -69,15 +96,15 @@ WEPOLL_EXPORT HANDLE epoll_create1(int flags); WEPOLL_EXPORT int epoll_close(HANDLE ephnd); -WEPOLL_EXPORT int epoll_ctl( HANDLE ephnd, +WEPOLL_EXPORT int epoll_ctl(HANDLE ephnd, int op, SOCKET sock, - struct epoll_event* event ); + struct epoll_event* event); -WEPOLL_EXPORT int epoll_wait( HANDLE ephnd, +WEPOLL_EXPORT int epoll_wait(HANDLE ephnd, struct epoll_event* events, int maxevents, - int timeout ); + int timeout); #ifdef __cplusplus } /* extern "C" */ -- GitLab