From fa4644d00f91f6031480e177868d755dc39f20ec Mon Sep 17 00:00:00 2001 From: Sandu Liviu Catalin Date: Sun, 22 Aug 2021 20:09:16 +0300 Subject: [PATCH] Update ZMQ to current git. --- vendor/ZMQ/CMakeLists.txt | 28 +- vendor/ZMQ/Makefile.am | 29 +- vendor/ZMQ/README.md | 2 +- vendor/ZMQ/RELICENSE/NLeScienceCenter.md | 13 + vendor/ZMQ/RELICENSE/christhrasher.md | 15 + vendor/ZMQ/RELICENSE/jlsantiago0.md | 13 + vendor/ZMQ/RELICENSE/sabotagebeats.md | 16 + vendor/ZMQ/RELICENSE/saschavv.md | 15 + vendor/ZMQ/RELICENSE/yitzchak.md | 15 + vendor/ZMQ/acinclude.m4 | 5 + vendor/ZMQ/appveyor.yml | 30 +- vendor/ZMQ/builds/android/README.md | 4 +- vendor/ZMQ/builds/android/build.sh | 4 +- vendor/ZMQ/builds/android/ci_build.sh | 2 +- vendor/ZMQ/builds/cmake/platform.hpp.in | 2 + vendor/ZMQ/builds/ios/build_ios.sh | 4 +- vendor/ZMQ/config.sh | 10 + vendor/ZMQ/configure.ac | 22 +- vendor/ZMQ/doc/zmq_poll.txt | 2 +- vendor/ZMQ/doc/zmq_setsockopt.txt | 30 + vendor/ZMQ/external/wepoll/license.txt | 2 +- vendor/ZMQ/external/wepoll/version.txt | 2 +- vendor/ZMQ/external/wepoll/wepoll.c | 953 ++++++++++++----------- vendor/ZMQ/external/wepoll/wepoll.h | 6 +- vendor/ZMQ/include/zmq.h | 19 +- vendor/ZMQ/packaging/redhat/zeromq.spec | 9 +- vendor/ZMQ/src/client.cpp | 1 + vendor/ZMQ/src/clock.cpp | 5 +- vendor/ZMQ/src/command.hpp | 15 +- vendor/ZMQ/src/curve_client_tools.hpp | 10 + vendor/ZMQ/src/dealer.cpp | 1 + vendor/ZMQ/src/dgram.cpp | 5 - vendor/ZMQ/src/dgram.hpp | 2 - vendor/ZMQ/src/dist.cpp | 12 + vendor/ZMQ/src/dist.hpp | 3 + vendor/ZMQ/src/epoll.cpp | 4 +- vendor/ZMQ/src/fq.cpp | 7 +- vendor/ZMQ/src/fq.hpp | 5 - vendor/ZMQ/src/ip.cpp | 32 +- vendor/ZMQ/src/mutex.hpp | 2 +- vendor/ZMQ/src/options.cpp | 29 +- vendor/ZMQ/src/options.hpp | 7 + vendor/ZMQ/src/pair.cpp | 7 +- vendor/ZMQ/src/pair.hpp | 2 - vendor/ZMQ/src/peer.cpp | 1 + vendor/ZMQ/src/pipe.cpp | 14 +- vendor/ZMQ/src/pipe.hpp | 2 + vendor/ZMQ/src/polling_util.hpp | 16 +- vendor/ZMQ/src/radix_tree.cpp | 2 +- vendor/ZMQ/src/random.cpp | 5 + vendor/ZMQ/src/session_base.cpp | 8 +- vendor/ZMQ/src/tcp.cpp | 18 + vendor/ZMQ/src/tcp.hpp | 2 + vendor/ZMQ/src/vmci.cpp | 21 + vendor/ZMQ/src/vmci.hpp | 4 + vendor/ZMQ/src/vmci_address.cpp | 20 +- vendor/ZMQ/src/vmci_address.hpp | 11 +- vendor/ZMQ/src/vmci_connecter.cpp | 273 ++++--- vendor/ZMQ/src/vmci_connecter.hpp | 51 +- vendor/ZMQ/src/vmci_listener.cpp | 104 +-- vendor/ZMQ/src/vmci_listener.hpp | 36 +- vendor/ZMQ/src/ws_engine.cpp | 18 +- vendor/ZMQ/src/xpub.cpp | 12 + vendor/ZMQ/src/yqueue.hpp | 5 +- vendor/ZMQ/src/zmq_draft.h | 4 +- vendor/ZMQ/tests/CMakeLists.txt | 4 + vendor/ZMQ/tests/test_busy_poll.cpp | 58 ++ vendor/ZMQ/tests/test_hiccup_msg.cpp | 76 ++ vendor/ZMQ/tests/test_inproc_connect.cpp | 6 +- vendor/ZMQ/tests/test_pair_vmci.cpp | 6 +- vendor/ZMQ/tests/test_reqrep_vmci.cpp | 10 +- vendor/ZMQ/tests/testutil.hpp | 5 + 72 files changed, 1309 insertions(+), 884 deletions(-) create mode 100644 vendor/ZMQ/RELICENSE/NLeScienceCenter.md create mode 100644 vendor/ZMQ/RELICENSE/christhrasher.md create mode 100644 vendor/ZMQ/RELICENSE/jlsantiago0.md create mode 100644 vendor/ZMQ/RELICENSE/sabotagebeats.md create mode 100644 vendor/ZMQ/RELICENSE/saschavv.md create mode 100644 vendor/ZMQ/RELICENSE/yitzchak.md create mode 100644 vendor/ZMQ/tests/test_busy_poll.cpp create mode 100644 vendor/ZMQ/tests/test_hiccup_msg.cpp diff --git a/vendor/ZMQ/CMakeLists.txt b/vendor/ZMQ/CMakeLists.txt index c8c47764..f1ca1804 100644 --- a/vendor/ZMQ/CMakeLists.txt +++ b/vendor/ZMQ/CMakeLists.txt @@ -75,7 +75,7 @@ endif() option(ENABLE_UBSAN "Build with undefined behavior sanitizer" OFF) if(ENABLE_UBSAN) message(STATUS "Instrumenting with Undefined Behavior Sanitizer") - set(CMAKE_BUILD_TYPE "RelWithDebInfo") + set(CMAKE_BUILD_TYPE "Debug") set(UBSAN_FLAGS "${UBSAN_FLAGS} -fno-omit-frame-pointer") set(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=undefined") set(UBSAN_FLAGS "${UBSAN_FLAGS} -fsanitize=implicit-conversion") @@ -118,15 +118,15 @@ endif() if (NOT MSVC) if(NOT CMAKE_CXX_FLAGS MATCHES "-std=") # use C++11 by default if supported - check_cxx_compiler_flag("-std=gnu++11" COMPILER_SUPPORTS_CXX11) + check_cxx_compiler_flag("-std=c++11" COMPILER_SUPPORTS_CXX11) if(COMPILER_SUPPORTS_CXX11) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif() endif() if(NOT CMAKE_C_FLAGS MATCHES "-std=") - check_c_compiler_flag("-std=gnu11" COMPILER_SUPPORTS_C11) + check_c_compiler_flag("-std=c11" COMPILER_SUPPORTS_C11) if(COMPILER_SUPPORTS_C11) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11") else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") endif() @@ -156,19 +156,19 @@ if(APPLE) endif() if(EXISTS "${CMAKE_SOURCE_DIR}/.git") - message(STATUS "Build and install draft classes and methods") option(ENABLE_DRAFTS "Build and install draft classes and methods" ON) else() - message(STATUS "Not building draft classes and methods") option(ENABLE_DRAFTS "Build and install draft classes and methods" OFF) endif() # Enable WebSocket transport and RadixTree if(ENABLE_DRAFTS) + message(STATUS "Building draft classes and methods") set(ZMQ_BUILD_DRAFT_API 1) option(ENABLE_WS "Enable WebSocket transport" ON) option(ENABLE_RADIX_TREE "Use radix tree implementation to manage subscriptions" ON) else() + message(STATUS "Not building draft classes and methods") option(ENABLE_WS "Enable WebSocket transport" OFF) option(ENABLE_RADIX_TREE "Use radix tree implementation to manage subscriptions" OFF) endif() @@ -263,15 +263,11 @@ endif() option(WITH_LIBSODIUM "Use libsodium instead of built-in tweetnacl" ON) option(WITH_LIBSODIUM_STATIC "Use static libsodium library" OFF) +option(ENABLE_LIBSODIUM_RANDOMBYTES_CLOSE "Automatically close libsodium randombytes. Not threadsafe without getrandom()" ON) option(ENABLE_CURVE "Enable CURVE security" ON) if(ENABLE_CURVE) if(WITH_LIBSODIUM) - # The package name passed to `find_package_handle_standard_args` (sodium) - # does not match the name of the calling package (Sodium). This can lead to - # problems in calling code that expects `find_package` result variables - # (e.g., `_FOUND`) to follow a certain pattern. - #find_package("Sodium") find_package("sodium") if(SODIUM_FOUND) message(STATUS "Using libsodium for CURVE security") @@ -281,6 +277,9 @@ if(ENABLE_CURVE) endif() set(ZMQ_USE_LIBSODIUM 1) set(ZMQ_HAVE_CURVE 1) + if (ENABLE_LIBSODIUM_RANDOMBYTES_CLOSE) + set(ZMQ_LIBSODIUM_RANDOMBYTES_CLOSE 1) + endif() else() message( WARNING @@ -474,6 +473,7 @@ message(STATUS "Using polling method in zmq_poll(er)_* API: ${API_POLLER}") string(TOUPPER ${API_POLLER} UPPER_API_POLLER) set(ZMQ_POLL_BASED_ON_${UPPER_API_POLLER} 1) +# special alignment settings execute_process( COMMAND getconf LEVEL1_DCACHE_LINESIZE OUTPUT_VARIABLE CACHELINE_SIZE @@ -486,6 +486,7 @@ else() set(ZMQ_CACHELINE_SIZE ${CACHELINE_SIZE}) endif() message(STATUS "Using ${ZMQ_CACHELINE_SIZE} bytes alignment for lock-free data structures") +check_cxx_symbol_exists(posix_memalign stdlib.h HAVE_POSIX_MEMALIGN) if(NOT CYGWIN) # TODO cannot we simply do 'if(WIN32) set(ZMQ_HAVE_WINDOWS ON)' or similar? @@ -565,6 +566,7 @@ else() check_cxx_symbol_exists(if_nametoindex net/if.h HAVE_IF_NAMETOINDEX) check_cxx_symbol_exists(SO_PEERCRED sys/socket.h ZMQ_HAVE_SO_PEERCRED) check_cxx_symbol_exists(LOCAL_PEERCRED sys/socket.h ZMQ_HAVE_LOCAL_PEERCRED) + check_cxx_symbol_exists(SO_BUSY_POLL sys/socket.h ZMQ_HAVE_BUSY_POLL) endif() if(NOT MINGW) @@ -1375,7 +1377,7 @@ else() add_library(libzmq SHARED $ ${public_headers} ${html-docs} ${readme-docs} ${zmq-pkgconfig} ${CMAKE_CURRENT_BINARY_DIR}/version.rc) endif() - + endif() # NOTE: the SOVERSION and VERSION MUST be the same as the one generated by libtool! It is NOT the same as the # version of the package. diff --git a/vendor/ZMQ/Makefile.am b/vendor/ZMQ/Makefile.am index 45b07504..5a5bb33b 100644 --- a/vendor/ZMQ/Makefile.am +++ b/vendor/ZMQ/Makefile.am @@ -1024,19 +1024,19 @@ endif endif if HAVE_VMCI -test_apps += test_pair_vmci test_reqrep_vmci +test_apps += tests/test_pair_vmci tests/test_reqrep_vmci -test_pair_vmci_SOURCES = tests/test_pair_vmci.cpp -test_pair_vmci_LDADD = ${TESTUTIL_LIBS} src/libzmq.la -test_pair_vmci_CPPFLAGS = ${TESTUTIL_CPPFLAGS} -test_pair_vmci_LDFLAGS = @LIBZMQ_VMCI_LDFLAGS@ -test_pair_vmci_CXXFLAGS = @LIBZMQ_VMCI_CXXFLAGS@ +tests_test_pair_vmci_SOURCES = tests/test_pair_vmci.cpp +tests_test_pair_vmci_LDADD = ${TESTUTIL_LIBS} src/libzmq.la +tests_test_pair_vmci_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +tests_test_pair_vmci_LDFLAGS = @LIBZMQ_VMCI_LDFLAGS@ +tests_test_pair_vmci_CXXFLAGS = @LIBZMQ_VMCI_CXXFLAGS@ -test_reqrep_vmci_SOURCES = tests/test_reqrep_vmci.cpp -test_reqrep_vmci_LDADD = ${TESTUTIL_LIBS} src/libzmq.la -test_reqrep_vmci_CPPFLAGS = ${TESTUTIL_CPPFLAGS} -test_reqrep_vmci_LDFLAGS = @LIBZMQ_VMCI_LDFLAGS@ -test_reqrep_vmci_CXXFLAGS = @LIBZMQ_VMCI_CXXFLAGS@ +tests_test_reqrep_vmci_SOURCES = tests/test_reqrep_vmci.cpp +tests_test_reqrep_vmci_LDADD = ${TESTUTIL_LIBS} src/libzmq.la +tests_test_reqrep_vmci_CPPFLAGS = ${TESTUTIL_CPPFLAGS} +tests_test_reqrep_vmci_LDFLAGS = @LIBZMQ_VMCI_LDFLAGS@ +tests_test_reqrep_vmci_CXXFLAGS = @LIBZMQ_VMCI_CXXFLAGS@ endif @@ -1056,7 +1056,8 @@ test_apps += tests/test_poller \ tests/test_msg_init \ tests/test_hello_msg \ tests/test_disconnect_msg \ - tests/test_channel + tests/test_channel \ + tests/test_hiccup_msg tests_test_poller_SOURCES = tests/test_poller.cpp tests_test_poller_LDADD = ${TESTUTIL_LIBS} src/libzmq.la @@ -1121,6 +1122,10 @@ tests_test_disconnect_msg_CPPFLAGS = ${TESTUTIL_CPPFLAGS} tests_test_channel_SOURCES = tests/test_channel.cpp tests_test_channel_LDADD = ${TESTUTIL_LIBS} src/libzmq.la tests_test_channel_CPPFLAGS = ${TESTUTIL_CPPFLAGS} + +tests_test_hiccup_msg_SOURCES = tests/test_hiccup_msg.cpp +tests_test_hiccup_msg_LDADD = ${TESTUTIL_LIBS} src/libzmq.la +tests_test_hiccup_msg_CPPFLAGS = ${TESTUTIL_CPPFLAGS} endif if FUZZING_ENGINE_LIB diff --git a/vendor/ZMQ/README.md b/vendor/ZMQ/README.md index e31712a9..5cccba32 100644 --- a/vendor/ZMQ/README.md +++ b/vendor/ZMQ/README.md @@ -1,6 +1,6 @@ # ZeroMQ -[![Build Status](https://travis-ci.org/zeromq/libzmq.png?branch=master)](https://travis-ci.org/zeromq/libzmq) +[![Build Status](https://travis-ci.com/zeromq/libzmq.svg?branch=master)](https://travis-ci.com/zeromq/libzmq) [![Build status](https://ci.appveyor.com/api/projects/status/e2ks424yrs1un3wt?svg=true)](https://ci.appveyor.com/project/zeromq/libzmq) [![Coverage Status](https://coveralls.io/repos/github/zeromq/libzmq/badge.svg?branch=master)](https://coveralls.io/github/zeromq/libzmq?branch=master) diff --git a/vendor/ZMQ/RELICENSE/NLeScienceCenter.md b/vendor/ZMQ/RELICENSE/NLeScienceCenter.md new file mode 100644 index 00000000..7623c1cb --- /dev/null +++ b/vendor/ZMQ/RELICENSE/NLeScienceCenter.md @@ -0,0 +1,13 @@ +# Permission to Relicense under MPLv2 + +This is a statement by the Netherlands eScience Center +that grants permission to relicense its copyrights in the libzmq C++ +library (ZeroMQ) under the Mozilla Public License v2 (MPLv2). + +A portion of the commits made by the Github handle "egpbos", with +commit author "E. G. Patrick Bos p.bos@esciencecenter.nl", are copyright of the Netherlands eScience Center. +This document hereby grants the libzmq project team to relicense libzmq, +including all past, present and future contributions of the author listed above. + +Rob van Nieuwpoort +2021/06/15 diff --git a/vendor/ZMQ/RELICENSE/christhrasher.md b/vendor/ZMQ/RELICENSE/christhrasher.md new file mode 100644 index 00000000..a74a9b2d --- /dev/null +++ b/vendor/ZMQ/RELICENSE/christhrasher.md @@ -0,0 +1,15 @@ +# Permission to Relicense under MPLv2 or any other OSI approved license chosen by the current ZeroMQ BDFL + +This is a statement by Chris Thrasher that grants permission to relicense +its copyrights in the libzmq C++ library (ZeroMQ) under the Mozilla Public +License v2 (MPLv2) or any other Open Source Initiative approved license chosen +by the current ZeroMQ BDFL (Benevolent Dictator for Life). + +A portion of the commits made by the Github handle "ChrisThrasher", with +commit author "Chris Thrasher ", are copyright of +Chris Thrasher. This document hereby grants the libzmq project team to +relicense libzmq, including all past, present and future contributions of the +author listed above. + +Chris Thrasher +2021/07/01 diff --git a/vendor/ZMQ/RELICENSE/jlsantiago0.md b/vendor/ZMQ/RELICENSE/jlsantiago0.md new file mode 100644 index 00000000..2e740da9 --- /dev/null +++ b/vendor/ZMQ/RELICENSE/jlsantiago0.md @@ -0,0 +1,13 @@ + Permission to Relicense under MPLv2 + +This is a statement by Jose L. Santiago +that grants permission to relicense its copyrights in the libzmq C++ +library (ZeroMQ) under the Mozilla Public License v2 (MPLv2). + +A portion of the commits made by the Github handle "jlsantiago0", with +commit author "Jose L. Santiago", are copyright of Jose L. Santiago. +This document hereby grants the libzmq project team to relicense libzmq, +including all past, present and future contributions of the author listed above. + +Jose L. Santiago +2021/02/18 diff --git a/vendor/ZMQ/RELICENSE/sabotagebeats.md b/vendor/ZMQ/RELICENSE/sabotagebeats.md new file mode 100644 index 00000000..e62df117 --- /dev/null +++ b/vendor/ZMQ/RELICENSE/sabotagebeats.md @@ -0,0 +1,16 @@ + +# Permission to Relicense under MPLv2 or any other OSI approved license chosen by the current ZeroMQ BDFL + +This is a statement by sabotagebeats +that grants permission to relicense its copyrights in the libzmq C++ +library (ZeroMQ) under the Mozilla Public License v2 (MPLv2) or any other +Open Source Initiative approved license chosen by the current ZeroMQ +BDFL (Benevolent Dictator for Life). + +A portion of the commits made by the Github handle "sabotagebeats", with +commit author "sabotagebeats", are copyright of sabotagebeats . +This document hereby grants the libzmq project team to relicense libzmq, +including all past, present and future contributions of the author listed above. + +sabotagebeats +2021/7/24 \ No newline at end of file diff --git a/vendor/ZMQ/RELICENSE/saschavv.md b/vendor/ZMQ/RELICENSE/saschavv.md new file mode 100644 index 00000000..2328140f --- /dev/null +++ b/vendor/ZMQ/RELICENSE/saschavv.md @@ -0,0 +1,15 @@ +# Permission to Relicense under MPLv2 or any other OSI approved license chosen by the current ZeroMQ BDFL + +This is a statement by Sascha van Vliet +that grants permission to relicense its copyrights in the libzmq C++ +library (ZeroMQ) under the Mozilla Public License v2 (MPLv2) or any other +Open Source Initiative approved license chosen by the current ZeroMQ +BDFL (Benevolent Dictator for Life). + +A portion of the commits made by the Github handle "saschavv", with +commit author "Sascha van Vliet ", are copyright of Sascha van Vliet. +This document hereby grants the libzmq project team to relicense libzmq, +including all past, present and future contributions of the author listed above. + +Sascha van Vliet +2021/07/30 diff --git a/vendor/ZMQ/RELICENSE/yitzchak.md b/vendor/ZMQ/RELICENSE/yitzchak.md new file mode 100644 index 00000000..5498c79d --- /dev/null +++ b/vendor/ZMQ/RELICENSE/yitzchak.md @@ -0,0 +1,15 @@ +# Permission to Relicense under MPLv2 or any other OSI approved license chosen by the current ZeroMQ BDFL + +This is a statement by Tarn W. Burton +that grants permission to relicense its copyrights in the libzmq C++ +library (ZeroMQ) under the Mozilla Public License v2 (MPLv2) or any other +Open Source Initiative approved license chosen by the current ZeroMQ +BDFL (Benevolent Dictator for Life). + +A portion of the commits made by the Github handle "yitzchak", with +commit author "Tarn W. Burton", are copyright of Tarn W. Burton. +This document hereby grants the libzmq project team to relicense libzmq, +including all past, present and future contributions of the author listed above. + +Tarn W. Burton +2021/05/16 diff --git a/vendor/ZMQ/acinclude.m4 b/vendor/ZMQ/acinclude.m4 index 07b9ffc4..ab8a3670 100644 --- a/vendor/ZMQ/acinclude.m4 +++ b/vendor/ZMQ/acinclude.m4 @@ -1212,6 +1212,11 @@ AC_DEFUN([LIBZMQ_CHECK_CACHELINE], [{ # the value the kernel knows on Linux zmq_cacheline_size=$(cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size 2>/dev/null || echo 64) fi + fi + if test "x$zmq_cacheline_size" = "xundefined"; then + # On some platforms e.g. Fedora33 s390x the cacheline size reported + # by getconf as 'undefined'. + zmq_cacheline_size=64 fi AC_MSG_NOTICE([Using "$zmq_cacheline_size" bytes alignment for lock-free data structures]) AC_DEFINE_UNQUOTED(ZMQ_CACHELINE_SIZE, $zmq_cacheline_size, [Using "$zmq_cacheline_size" bytes alignment for lock-free data structures]) diff --git a/vendor/ZMQ/appveyor.yml b/vendor/ZMQ/appveyor.yml index b401c314..446015ab 100644 --- a/vendor/ZMQ/appveyor.yml +++ b/vendor/ZMQ/appveyor.yml @@ -175,11 +175,15 @@ install: - cmd: echo "Generator='%CMAKE_GENERATOR%'" - cmd: echo "Platform='%Platform%'" - cmd: if "%WITH_LIBSODIUM%"=="ON" set LIBSODIUMDIR=C:\projects\libsodium + - cmd: if "%WITH_LIBSODIUM%"=="ON" ( + git config --global user.email "test@appveyor.com" && + git config --global user.name "appveyor" + ) - cmd: if "%WITH_LIBSODIUM%"=="ON" ( if not exist "%LIBSODIUMDIR%" ( git clone --branch stable --depth 1 --quiet "https://github.com/jedisct1/libsodium.git" %LIBSODIUMDIR% ) else ( - git -C "%LIBSODIUMDIR%" pull + git -C "%LIBSODIUMDIR%" fetch --all && git -C "%LIBSODIUMDIR%" reset --hard origin/stable ) ) - cmd: if "%WITH_LIBSODIUM%"=="ON" msbuild /v:minimal /maxcpucount:%NUMBER_OF_PROCESSORS% /p:Configuration=%Configuration%DLL %LIBSODIUMDIR%\builds\msvc\%MSVCYEAR%\libsodium\libsodium.vcxproj @@ -272,18 +276,18 @@ test_script: - cmd: if "%APPVEYOR_REPO_TAG%"=="false" (ctest -C "%Configuration%" -V %TEST_OPTIONS%) deploy: - - provider: BinTray - username: somdoron - api_key: - secure: B4TC4GvUMbwX13Skh2Kvyc6SnqLkjNS9W0gtN2yxUC2Y4oQSCK2F4eRgtpMpCasg - subject: zeromq - repo: generic - package: libzmq - publish: true - override: true - version: ${ZMQ_VERSION} - on: - APPVEYOR_REPO_TAG: true + - provider: GitHub + tag: $(APPVEYOR_REPO_TAG_NAME) + release: libzmq $(APPVEYOR_REPO_TAG_NAME) + description: | + Windows binaries for libzmq $(APPVEYOR_REPO_TAG_NAME), uploaded from appveyor. + Edit after appveyor is done uploading. + auth_token: + secure: vmAeVtN2qiQgFBCB2I5FDDRtADQ7GUdR9NwAJJyakbiV5OHzLHExDcC/D9Oh5r67 + draft: true + prerelease: false + force_update: true # adds files, clobbers release name and description + # the analysis build is repeated; apparently appveyor only uses the first section that matches some branch for: diff --git a/vendor/ZMQ/builds/android/README.md b/vendor/ZMQ/builds/android/README.md index 9692d5a4..ddc1abeb 100644 --- a/vendor/ZMQ/builds/android/README.md +++ b/vendor/ZMQ/builds/android/README.md @@ -7,10 +7,10 @@ You need the Android Native Development Kit (NDK) installed. See This project is tested against Android NDK version r21d. -If you installed version r21d all you have to do is to expose the NDK root +If you installed version r21e all you have to do is to expose the NDK root directory as environment variable, e.g: - export ANDROID_NDK_ROOT=$HOME/android-ndk-r21d + export ANDROID_NDK_ROOT=$HOME/android-ndk-r21e If you installed another version you have to expose the NDK root directory as well as the NDK version, e.g: diff --git a/vendor/ZMQ/builds/android/build.sh b/vendor/ZMQ/builds/android/build.sh index 5c539dd0..d6ea8c3e 100644 --- a/vendor/ZMQ/builds/android/build.sh +++ b/vendor/ZMQ/builds/android/build.sh @@ -35,8 +35,8 @@ esac # Set default values used in ci builds -export NDK_VERSION=${NDK_VERSION:-android-ndk-r21d} -# With NDK r21d, the minimum SDK version range is [16, 29]. +export NDK_VERSION=${NDK_VERSION:-android-ndk-r21e} +# With NDK r21e, the minimum SDK version range is [16, 29]. # SDK version 21 is the minimum version for 64-bit builds. export MIN_SDK_VERSION=${MIN_SDK_VERSION:-21} diff --git a/vendor/ZMQ/builds/android/ci_build.sh b/vendor/ZMQ/builds/android/ci_build.sh index fa298e69..1eaa7de2 100644 --- a/vendor/ZMQ/builds/android/ci_build.sh +++ b/vendor/ZMQ/builds/android/ci_build.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -export NDK_VERSION=android-ndk-r21d +export NDK_VERSION=android-ndk-r21e export ANDROID_NDK_ROOT="/tmp/${NDK_VERSION}" case $(uname | tr '[:upper:]' '[:lower:]') in diff --git a/vendor/ZMQ/builds/cmake/platform.hpp.in b/vendor/ZMQ/builds/cmake/platform.hpp.in index f7119df1..013dca1b 100644 --- a/vendor/ZMQ/builds/cmake/platform.hpp.in +++ b/vendor/ZMQ/builds/cmake/platform.hpp.in @@ -16,6 +16,7 @@ #cmakedefine ZMQ_POLL_BASED_ON_SELECT #cmakedefine ZMQ_POLL_BASED_ON_POLL +#cmakedefine HAVE_POSIX_MEMALIGN @HAVE_POSIX_MEMALIGN@ #cmakedefine ZMQ_CACHELINE_SIZE @ZMQ_CACHELINE_SIZE@ #cmakedefine ZMQ_FORCE_MUTEXES @@ -35,6 +36,7 @@ #cmakedefine ZMQ_HAVE_SO_PEERCRED #cmakedefine ZMQ_HAVE_LOCAL_PEERCRED +#cmakedefine ZMQ_HAVE_BUSY_POLL #cmakedefine ZMQ_HAVE_O_CLOEXEC diff --git a/vendor/ZMQ/builds/ios/build_ios.sh b/vendor/ZMQ/builds/ios/build_ios.sh index bf278f90..d45b302e 100644 --- a/vendor/ZMQ/builds/ios/build_ios.sh +++ b/vendor/ZMQ/builds/ios/build_ios.sh @@ -84,7 +84,9 @@ do --prefix=${BUILDARCHDIR} \ --disable-shared \ --enable-static \ - --host=${HOST} + --host=${HOST} \ + --disable-perf \ + --disable-curve-keygen echo "Building ${LIBNAME} for ${ARCH}..." cd ${LIBDIR} diff --git a/vendor/ZMQ/config.sh b/vendor/ZMQ/config.sh index 4648cab4..73729fed 100644 --- a/vendor/ZMQ/config.sh +++ b/vendor/ZMQ/config.sh @@ -66,4 +66,14 @@ function set_config_opts() { if [ -n "$FORCE_98" ] && [ "$FORCE_98" = "enabled" ]; then CONFIG_OPTS+=("--enable-force-CXX98-compat=yes") fi + + if [ -n "$VMCI" ] && [ "$VMCI" = "enabled" ]; then + CONFIG_OPTS+=("--with-vmci=$PWD/vmci") + # VMWare headeers are not ISO C++ compliant + CONFIG_OPTS+=("--disable-pedantic") + git clone --depth 1 https://github.com/vmware/open-vm-tools.git + mkdir -p vmci + # Linux headers are redefined, so we can't just add -I to the whole dir + cp open-vm-tools/open-vm-tools/lib/include/vmci_* vmci/ + fi } diff --git a/vendor/ZMQ/configure.ac b/vendor/ZMQ/configure.ac index 15863eeb..6af4cadc 100644 --- a/vendor/ZMQ/configure.ac +++ b/vendor/ZMQ/configure.ac @@ -65,7 +65,7 @@ ZMQ_ORIG_CXXFLAGS="${CXXFLAGS:-none}" # Checks for programs. AC_PROG_CC -AX_CHECK_COMPILE_FLAG([-std=gnu11], [CFLAGS+=" -std=gnu11"], [AC_PROG_CC_C99]) +AX_CHECK_COMPILE_FLAG([-std=c11], [CFLAGS+=" -std=c11"], [AC_PROG_CC_C99]) AC_PROG_CXX AX_CODE_COVERAGE AM_PROG_CC_C_O @@ -550,6 +550,20 @@ AS_IF([test "x$with_libsodium" = "xyes"], [ AC_ARG_ENABLE([curve], [AS_HELP_STRING([--disable-curve], [disable CURVE security [default=no]])]) +AC_ARG_ENABLE( + [libsodium_randombytes_close], + [AS_HELP_STRING( + [--disable-libsodium_randombytes_close], + [Do not call libsodium randombytes_close() when terminating contexts. + If disabled, may leave one FD open on /dev/urandom + until randombytes_close() is called explicitly, + but fixes a crash when multiple contexts are used with CURVE. + Has no effect when getrandom() is available. [default=enabled]] + )], + [], + [enable_libsodium_randombytes_close=yes] +) + if test "x$enable_curve" = "xno"; then curve_library="" AC_MSG_NOTICE([CURVE security is disabled]) @@ -558,6 +572,12 @@ elif test "x$with_libsodium" = "xyes"; then AC_MSG_NOTICE([Using libsodium for CURVE security]) AC_DEFINE(ZMQ_HAVE_CURVE, [1], [Using curve encryption]) AC_DEFINE(ZMQ_USE_LIBSODIUM, [1], [Using libsodium for curve encryption]) + if test "x$enable_libsodium_randombytes_close" = "xyes"; then + AC_DEFINE(ZMQ_LIBSODIUM_RANDOMBYTES_CLOSE, [1], [Automatically close libsodium randombytes. Not threadsafe without getrandom()]) + else + AC_MSG_NOTICE([Disabling libsodium randombytes_close(). randombytes_close() may need to be called in applcation code.]) + fi + curve_library="libsodium" enable_curve="yes" diff --git a/vendor/ZMQ/doc/zmq_poll.txt b/vendor/ZMQ/doc/zmq_poll.txt index 726064e1..ddfb3911 100644 --- a/vendor/ZMQ/doc/zmq_poll.txt +++ b/vendor/ZMQ/doc/zmq_poll.txt @@ -25,7 +25,7 @@ array. The *zmq_pollitem_t* structure is defined as follows: typedef struct { void '*socket'; - int 'fd'; + zmq_fd_t 'fd'; short 'events'; short 'revents'; } zmq_pollitem_t; diff --git a/vendor/ZMQ/doc/zmq_setsockopt.txt b/vendor/ZMQ/doc/zmq_setsockopt.txt index a010d3d8..c8c9029a 100644 --- a/vendor/ZMQ/doc/zmq_setsockopt.txt +++ b/vendor/ZMQ/doc/zmq_setsockopt.txt @@ -87,6 +87,21 @@ Default value:: not set Applicable socket types:: all, when using TCP or UDP transports. +ZMQ_BUSY_POLL: This removes delays caused by the interrupt and the resultant context switch. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Busy polling helps reduce latency in the network receive path by allowing socket layer code +to poll the receive queue of a network device, and disabling network interrupts. This removes +delays caused by the interrupt and the resultant context switch. However, it also increases +CPU utilization. Busy polling also prevents the CPU from sleeping, which can incur additional +power consumption. + +[horizontal] +Option value type:: int +Option value unit:: 0,1 +Default value:: 0 +Applicable socket types:: all + + ZMQ_CONNECT_RID: Assign the next outbound connection id ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This option name is now deprecated. Use ZMQ_CONNECT_ROUTING_ID instead. @@ -241,6 +256,21 @@ Option value unit:: N/A Default value:: NULL Applicable socket types:: ZMQ_ROUTER, ZMQ_SERVER and ZMQ_PEER + +ZMQ_HICCUP_MSG: set a hiccup message that the socket will generate when connected peer temporarly disconnect +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When set, the socket will generate a hiccup message when connect peer has been disconnected. +You may set this on DEALER, CLIENT and PEER sockets. +The combination with ZMQ_HEARTBEAT_IVL is powerful and simplify protocols, when heartbeat recognize a connection drop it +will generate a hiccup message that can match the protocol of the application. + +[horizontal] +Option value type:: binary data +Option value unit:: N/A +Default value:: NULL +Applicable socket types:: ZMQ_DEALER, ZMQ_CLIENT and ZMQ_PEER + + ZMQ_GSSAPI_PLAINTEXT: Disable GSSAPI encryption ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Defines whether communications on the socket will be encrypted, see diff --git a/vendor/ZMQ/external/wepoll/license.txt b/vendor/ZMQ/external/wepoll/license.txt index 31ad7c22..d7fc4b11 100644 --- a/vendor/ZMQ/external/wepoll/license.txt +++ b/vendor/ZMQ/external/wepoll/license.txt @@ -1,7 +1,7 @@ wepoll - epoll for Windows https://github.com/piscisaureus/wepoll -Copyright 2012-2018, Bert Belder +Copyright 2012-2020, Bert Belder All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/vendor/ZMQ/external/wepoll/version.txt b/vendor/ZMQ/external/wepoll/version.txt index 62915844..3b296081 100644 --- a/vendor/ZMQ/external/wepoll/version.txt +++ b/vendor/ZMQ/external/wepoll/version.txt @@ -1 +1 @@ -https://github.com/piscisaureus/wepoll/tree/v1.5.4 +https://github.com/piscisaureus/wepoll/tree/v1.5.8 diff --git a/vendor/ZMQ/external/wepoll/wepoll.c b/vendor/ZMQ/external/wepoll/wepoll.c index 3d301410..186d3f2d 100644 --- a/vendor/ZMQ/external/wepoll/wepoll.c +++ b/vendor/ZMQ/external/wepoll/wepoll.c @@ -2,7 +2,7 @@ * wepoll - epoll for Windows * https://github.com/piscisaureus/wepoll * - * Copyright 2012-2018, Bert Belder + * Copyright 2012-2020, Bert Belder * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,8 +35,6 @@ #include -/* clang-format off */ - enum EPOLL_EVENTS { EPOLLIN = (int) (1U << 0), EPOLLPRI = (int) (1U << 1), @@ -69,8 +67,6 @@ enum EPOLL_EVENTS { #define EPOLL_CTL_MOD 2 #define EPOLL_CTL_DEL 3 -/* clang-format on */ - typedef void* HANDLE; typedef uintptr_t SOCKET; @@ -111,40 +107,34 @@ WEPOLL_EXPORT int epoll_wait(HANDLE ephnd, } /* extern "C" */ #endif -#include +#include + #include #define WEPOLL_INTERNAL static -#define WEPOLL_INTERNAL_VAR static +#define WEPOLL_INTERNAL_EXTERN static -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#ifdef __clang__ +#if defined(__clang__) #pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonportable-system-include-path" #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__ +#elif defined(_MSC_VER) #pragma warning(push, 1) #endif -#include -#include -#include +#undef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN -#ifndef __GNUC__ +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 + +#include +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(_MSC_VER) #pragma warning(pop) #endif @@ -169,6 +159,10 @@ typedef NTSTATUS* PNTSTATUS; #define STATUS_CANCELLED ((NTSTATUS) 0xC0000120L) #endif +#ifndef STATUS_NOT_FOUND +#define STATUS_NOT_FOUND ((NTSTATUS) 0xC0000225L) +#endif + typedef struct _IO_STATUS_BLOCK { NTSTATUS Status; ULONG_PTR Information; @@ -178,11 +172,11 @@ typedef VOID(NTAPI* PIO_APC_ROUTINE)(PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG Reserved); -typedef struct _LSA_UNICODE_STRING { +typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; -} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING; +} UNICODE_STRING, *PUNICODE_STRING; #define RTL_CONSTANT_STRING(s) \ { sizeof(s) - sizeof((s)[0]), sizeof(s), s } @@ -203,83 +197,79 @@ typedef struct _OBJECT_ATTRIBUTES { #define FILE_OPEN 0x00000001UL #endif -#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, \ - 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(ULONG, WINAPI, RtlNtStatusToDosError, (NTSTATUS Status)) \ - \ - X(NTSTATUS, \ - NTAPI, \ - NtCreateKeyedEvent, \ - (PHANDLE handle, \ - ACCESS_MASK access, \ - POBJECT_ATTRIBUTES attr, \ - ULONG flags)) \ - \ - X(NTSTATUS, \ - NTAPI, \ - NtWaitForKeyedEvent, \ - (HANDLE handle, PVOID key, BOOLEAN alertable, PLARGE_INTEGER mstimeout)) \ - \ - X(NTSTATUS, \ - NTAPI, \ - NtReleaseKeyedEvent, \ - (HANDLE handle, PVOID key, BOOLEAN alertable, PLARGE_INTEGER mstimeout)) +#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, \ + NtCancelIoFileEx, \ + (HANDLE FileHandle, \ + PIO_STATUS_BLOCK IoRequestToCancel, \ + PIO_STATUS_BLOCK IoStatusBlock)) \ + \ + 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; + WEPOLL_INTERNAL_EXTERN return_type(attributes* name) parameters; NT_NTDLL_IMPORT_LIST(X) #undef X -#include -#include - -#ifndef _SSIZE_T_DEFINED -typedef intptr_t ssize_t; -#endif - -#define array_count(a) (sizeof(a) / (sizeof((a)[0]))) - -/* clang-format off */ -#define container_of(ptr, type, member) \ - ((type*) ((uintptr_t) (ptr) - offsetof(type, member))) -/* clang-format on */ - -#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 - -/* clang-format off */ #define AFD_POLL_RECEIVE 0x0001 #define AFD_POLL_RECEIVE_EXPEDITED 0x0002 #define AFD_POLL_SEND 0x0004 @@ -288,7 +278,6 @@ typedef intptr_t ssize_t; #define AFD_POLL_LOCAL_CLOSE 0x0020 #define AFD_POLL_ACCEPT 0x0080 #define AFD_POLL_CONNECT_FAIL 0x0100 -/* clang-format on */ typedef struct _AFD_POLL_HANDLE_INFO { HANDLE Handle; @@ -303,12 +292,14 @@ typedef struct _AFD_POLL_INFO { 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_create_device_handle(HANDLE iocp_handle, + HANDLE* afd_device_handle_out); -WEPOLL_INTERNAL int afd_poll(HANDLE afd_helper_handle, +WEPOLL_INTERNAL int afd_poll(HANDLE afd_device_handle, AFD_POLL_INFO* poll_info, - OVERLAPPED* overlapped); + IO_STATUS_BLOCK* io_status_block); +WEPOLL_INTERNAL int afd_cancel_poll(HANDLE afd_device_handle, + IO_STATUS_BLOCK* io_status_block); #define return_map_error(value) \ do { \ @@ -326,28 +317,26 @@ 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 = +static UNICODE_STRING afd__device_name = RTL_CONSTANT_STRING(L"\\Device\\Afd\\Wepoll"); -static OBJECT_ATTRIBUTES afd__helper_attributes = - RTL_CONSTANT_OBJECT_ATTRIBUTES(&afd__helper_name, 0); +static OBJECT_ATTRIBUTES afd__device_attributes = + RTL_CONSTANT_OBJECT_ATTRIBUTES(&afd__device_name, 0); -int afd_create_helper_handle(HANDLE iocp, HANDLE* afd_helper_handle_out) { - HANDLE afd_helper_handle; +int afd_create_device_handle(HANDLE iocp_handle, + HANDLE* afd_device_handle_out) { + HANDLE afd_device_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, + status = NtCreateFile(&afd_device_handle, SYNCHRONIZE, - &afd__helper_attributes, + &afd__device_attributes, &iosb, NULL, 0, @@ -359,50 +348,35 @@ int afd_create_helper_handle(HANDLE iocp, HANDLE* afd_helper_handle_out) { if (status != STATUS_SUCCESS) return_set_error(-1, RtlNtStatusToDosError(status)); - if (CreateIoCompletionPort(afd_helper_handle, iocp, 0, 0) == NULL) + if (CreateIoCompletionPort(afd_device_handle, iocp_handle, 0, 0) == NULL) goto error; - if (!SetFileCompletionNotificationModes(afd_helper_handle, + if (!SetFileCompletionNotificationModes(afd_device_handle, FILE_SKIP_SET_EVENT_ON_HANDLE)) goto error; - *afd_helper_handle_out = afd_helper_handle; + *afd_device_handle_out = afd_device_handle; return 0; error: - CloseHandle(afd_helper_handle); + CloseHandle(afd_device_handle); return_map_error(-1); } -int afd_poll(HANDLE afd_helper_handle, +int afd_poll(HANDLE afd_device_handle, AFD_POLL_INFO* poll_info, - OVERLAPPED* overlapped) { - IO_STATUS_BLOCK* iosb; - HANDLE event; - void* apc_context; + IO_STATUS_BLOCK* io_status_block) { NTSTATUS status; /* Blocking operation is not supported. */ - assert(overlapped != NULL); + assert(io_status_block != 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, + io_status_block->Status = STATUS_PENDING; + status = NtDeviceIoControlFile(afd_device_handle, NULL, - apc_context, - iosb, + NULL, + io_status_block, + io_status_block, IOCTL_AFD_POLL, poll_info, sizeof *poll_info, @@ -417,106 +391,75 @@ int afd_poll(HANDLE afd_helper_handle, return_set_error(-1, RtlNtStatusToDosError(status)); } +int afd_cancel_poll(HANDLE afd_device_handle, + IO_STATUS_BLOCK* io_status_block) { + NTSTATUS cancel_status; + IO_STATUS_BLOCK cancel_iosb; + + /* If the poll operation has already completed or has been cancelled earlier, + * there's nothing left for us to do. */ + if (io_status_block->Status != STATUS_PENDING) + return 0; + + cancel_status = + NtCancelIoFileEx(afd_device_handle, io_status_block, &cancel_iosb); + + /* NtCancelIoFileEx() may return STATUS_NOT_FOUND if the operation completed + * just before calling NtCancelIoFileEx(). This is not an error. */ + if (cancel_status == STATUS_SUCCESS || cancel_status == STATUS_NOT_FOUND) + return 0; + else + return_set_error(-1, RtlNtStatusToDosError(cancel_status)); +} + WEPOLL_INTERNAL int epoll_global_init(void); WEPOLL_INTERNAL int init(void); -#include - -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 queue queue_t; typedef struct sock_state sock_state_t; +typedef struct ts_tree_node ts_tree_node_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 port_state_t* port_new(HANDLE* iocp_handle_out); +WEPOLL_INTERNAL int port_close(port_state_t* port_state); +WEPOLL_INTERNAL int port_delete(port_state_t* port_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 port_wait(port_state_t* port_state, + struct epoll_event* events, + int maxevents, + int timeout); -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 int port_ctl(port_state_t* port_state, + int op, + SOCKET sock, + 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); +WEPOLL_INTERNAL int port_register_socket(port_state_t* port_state, + sock_state_t* sock_state, + SOCKET socket); +WEPOLL_INTERNAL void port_unregister_socket(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); + +WEPOLL_INTERNAL HANDLE port_get_iocp_handle(port_state_t* port_state); +WEPOLL_INTERNAL queue_t* port_get_poll_group_queue(port_state_t* port_state); + +WEPOLL_INTERNAL port_state_t* port_state_from_handle_tree_node( + ts_tree_node_t* tree_node); +WEPOLL_INTERNAL ts_tree_node_t* port_state_to_handle_tree_node( + port_state_t* port_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 @@ -547,6 +490,36 @@ 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); +#include + +/* 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 ts_tree { tree_t tree; SRWLOCK lock; @@ -572,59 +545,8 @@ WEPOLL_INTERNAL ts_tree_node_t* ts_tree_find_and_ref(ts_tree_t* ts_tree, 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; @@ -633,6 +555,7 @@ int epoll_global_init(void) { static HANDLE epoll__create(void) { port_state_t* port_state; HANDLE ephnd; + ts_tree_node_t* tree_node; if (init() < 0) return NULL; @@ -641,9 +564,8 @@ static HANDLE epoll__create(void) { if (port_state == NULL) return NULL; - if (ts_tree_add(&epoll__handle_tree, - &port_state->handle_tree_node, - (uintptr_t) ephnd) < 0) { + tree_node = port_state_to_handle_tree_node(port_state); + if (ts_tree_add(&epoll__handle_tree, tree_node, (uintptr_t) ephnd) < 0) { /* This should never happen. */ port_delete(port_state); return_set_error(NULL, ERROR_ALREADY_EXISTS); @@ -679,7 +601,7 @@ int epoll_close(HANDLE ephnd) { goto err; } - port_state = epoll__handle_tree_node_to_port(tree_node); + port_state = port_state_from_handle_tree_node(tree_node); port_close(port_state); ts_tree_node_unref_and_destroy(tree_node); @@ -705,7 +627,7 @@ int epoll_ctl(HANDLE ephnd, int op, SOCKET sock, struct epoll_event* ev) { goto err; } - port_state = epoll__handle_tree_node_to_port(tree_node); + port_state = port_state_from_handle_tree_node(tree_node); r = port_ctl(port_state, op, sock, ev); ts_tree_node_unref(tree_node); @@ -716,7 +638,7 @@ int epoll_ctl(HANDLE ephnd, int op, SOCKET sock, struct epoll_event* ev) { return 0; err: - /* On Linux, in the case of epoll_ctl_mod(), EBADF takes priority over other + /* On Linux, in the case of epoll_ctl(), EBADF takes priority over other * errors. Wepoll mimics this behavior. */ err_check_handle(ephnd); err_check_handle((HANDLE) sock); @@ -743,7 +665,7 @@ int epoll_wait(HANDLE ephnd, goto err; } - port_state = epoll__handle_tree_node_to_port(tree_node); + port_state = port_state_from_handle_tree_node(tree_node); num_events = port_wait(port_state, events, maxevents, timeout); ts_tree_node_unref(tree_node); @@ -897,6 +819,23 @@ int err_check_handle(HANDLE handle) { return 0; } +#include + +#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 + +WEPOLL_INTERNAL int ws_global_init(void); +WEPOLL_INTERNAL SOCKET ws_get_base_socket(SOCKET socket); + static bool init__done = false; static INIT_ONCE init__once = INIT_ONCE_STATIC_INIT; @@ -919,7 +858,11 @@ static BOOL CALLBACK init__once_callback(INIT_ONCE* once, int init(void) { if (!init__done && !InitOnceExecuteOnce(&init__once, init__once_callback, NULL, NULL)) - return -1; /* LastError and errno aren't touched InitOnceExecuteOnce. */ + /* `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; } @@ -962,16 +905,57 @@ int nt_global_init(void) { #include -static const size_t POLL_GROUP__MAX_GROUP_SIZE = 32; +typedef struct poll_group poll_group_t; + +typedef struct queue_node queue_node_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_device_handle(poll_group_t* poll_group); + +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_to_start(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_move_to_end(queue_t* queue, queue_node_t* node); +WEPOLL_INTERNAL void queue_remove(queue_node_t* node); + +WEPOLL_INTERNAL bool queue_is_empty(const queue_t* queue); +WEPOLL_INTERNAL bool queue_is_enqueued(const queue_node_t* node); + +#define POLL_GROUP__MAX_GROUP_SIZE 32 typedef struct poll_group { port_state_t* port_state; queue_node_t queue_node; - HANDLE afd_helper_handle; + HANDLE afd_device_handle; size_t group_size; } poll_group_t; static poll_group_t* poll_group__new(port_state_t* port_state) { + HANDLE iocp_handle = port_get_iocp_handle(port_state); + queue_t* poll_group_queue = port_get_poll_group_queue(port_state); + poll_group_t* poll_group = malloc(sizeof *poll_group); if (poll_group == NULL) return_set_error(NULL, ERROR_NOT_ENOUGH_MEMORY); @@ -981,20 +965,20 @@ static poll_group_t* poll_group__new(port_state_t* port_state) { 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) { + if (afd_create_device_handle(iocp_handle, &poll_group->afd_device_handle) < + 0) { free(poll_group); return NULL; } - queue_append(&port_state->poll_group_queue, &poll_group->queue_node); + queue_append(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); + CloseHandle(poll_group->afd_device_handle); queue_remove(&poll_group->queue_node); free(poll_group); } @@ -1003,15 +987,16 @@ 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; +HANDLE poll_group_get_afd_device_handle(poll_group_t* poll_group) { + return poll_group->afd_device_handle; } poll_group_t* poll_group_acquire(port_state_t* port_state) { - queue_t* queue = &port_state->poll_group_queue; + queue_t* poll_group_queue = port_get_poll_group_queue(port_state); poll_group_t* poll_group = - !queue_empty(queue) - ? container_of(queue_last(queue), poll_group_t, queue_node) + !queue_is_empty(poll_group_queue) + ? container_of( + queue_last(poll_group_queue), poll_group_t, queue_node) : NULL; if (poll_group == NULL || @@ -1021,25 +1006,62 @@ poll_group_t* poll_group_acquire(port_state_t* port_state) { return NULL; if (++poll_group->group_size == POLL_GROUP__MAX_GROUP_SIZE) - queue_move_first(&port_state->poll_group_queue, &poll_group->queue_node); + queue_move_to_start(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; + queue_t* poll_group_queue = port_get_poll_group_queue(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); + queue_move_to_end(poll_group_queue, &poll_group->queue_node); /* Poll groups are currently only freed when the epoll port is closed. */ } +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, + IO_STATUS_BLOCK* io_status_block, + 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); + #define PORT__MAX_ON_STACK_COMPLETIONS 256 -static port_state_t* port__alloc(void) { +typedef struct port_state { + HANDLE iocp_handle; + 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; + +static inline 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); @@ -1047,34 +1069,35 @@ static port_state_t* port__alloc(void) { return port_state; } -static void port__free(port_state_t* port) { +static inline 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) +static inline HANDLE port__create_iocp(void) { + HANDLE iocp_handle = + CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); + if (iocp_handle == NULL) return_map_error(NULL); - return iocp; + return iocp_handle; } -port_state_t* port_new(HANDLE* iocp_out) { +port_state_t* port_new(HANDLE* iocp_handle_out) { port_state_t* port_state; - HANDLE iocp; + HANDLE iocp_handle; port_state = port__alloc(); if (port_state == NULL) goto err1; - iocp = port__create_iocp(); - if (iocp == NULL) + iocp_handle = port__create_iocp(); + if (iocp_handle == NULL) goto err2; memset(port_state, 0, sizeof *port_state); - port_state->iocp = iocp; + port_state->iocp_handle = iocp_handle; tree_init(&port_state->sock_tree); queue_init(&port_state->sock_update_queue); queue_init(&port_state->sock_deleted_queue); @@ -1082,7 +1105,7 @@ port_state_t* port_new(HANDLE* iocp_out) { ts_tree_node_init(&port_state->handle_tree_node); InitializeCriticalSection(&port_state->lock); - *iocp_out = iocp; + *iocp_handle_out = iocp_handle; return port_state; err2: @@ -1091,11 +1114,11 @@ err1: return NULL; } -static int port__close_iocp(port_state_t* port_state) { - HANDLE iocp = port_state->iocp; - port_state->iocp = NULL; +static inline int port__close_iocp(port_state_t* port_state) { + HANDLE iocp_handle = port_state->iocp_handle; + port_state->iocp_handle = NULL; - if (!CloseHandle(iocp)) + if (!CloseHandle(iocp_handle)) return_map_error(-1); return 0; @@ -1116,7 +1139,7 @@ int port_delete(port_state_t* port_state) { queue_node_t* queue_node; /* At this point the IOCP port should have been closed. */ - assert(port_state->iocp == NULL); + assert(port_state->iocp_handle == NULL); while ((tree_node = tree_root(&port_state->sock_tree)) != NULL) { sock_state_t* sock_state = sock_state_from_tree_node(tree_node); @@ -1133,7 +1156,7 @@ int port_delete(port_state_t* port_state) { poll_group_delete(poll_group); } - assert(queue_empty(&port_state->sock_update_queue)); + assert(queue_is_empty(&port_state->sock_update_queue)); DeleteCriticalSection(&port_state->lock); @@ -1147,7 +1170,7 @@ static int port__update_events(port_state_t* port_state) { /* Walk the queue, submitting new poll requests for every socket that needs * it. */ - while (!queue_empty(sock_update_queue)) { + while (!queue_is_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); @@ -1160,33 +1183,34 @@ static int port__update_events(port_state_t* port_state) { return 0; } -static void port__update_events_if_polling(port_state_t* port_state) { +static inline 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) { +static inline 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; + IO_STATUS_BLOCK* io_status_block = + (IO_STATUS_BLOCK*) iocp_events[i].lpOverlapped; struct epoll_event* ev = &epoll_events[epoll_event_count]; - epoll_event_count += sock_feed_event(port_state, overlapped, ev); + epoll_event_count += sock_feed_event(port_state, io_status_block, 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) { +static inline 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) @@ -1196,7 +1220,7 @@ static int port__poll(port_state_t* port_state, LeaveCriticalSection(&port_state->lock); - BOOL r = GetQueuedCompletionStatusEx(port_state->iocp, + BOOL r = GetQueuedCompletionStatusEx(port_state->iocp_handle, iocp_events, maxevents, &completion_count, @@ -1292,9 +1316,9 @@ int port_wait(port_state_t* port_state, return -1; } -static int port__ctl_add(port_state_t* port_state, - SOCKET sock, - struct epoll_event* ev) { +static inline 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; @@ -1309,9 +1333,9 @@ static int port__ctl_add(port_state_t* port_state, return 0; } -static int port__ctl_mod(port_state_t* port_state, - SOCKET sock, - struct epoll_event* ev) { +static inline 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; @@ -1324,7 +1348,7 @@ static int port__ctl_mod(port_state_t* port_state, return 0; } -static int port__ctl_del(port_state_t* port_state, SOCKET sock) { +static inline 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; @@ -1334,10 +1358,10 @@ static int port__ctl_del(port_state_t* port_state, SOCKET sock) { return 0; } -static int port__ctl_op(port_state_t* port_state, - int op, - SOCKET sock, - struct epoll_event* ev) { +static inline 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); @@ -1363,9 +1387,9 @@ int port_ctl(port_state_t* port_state, return result; } -int port_register_socket_handle(port_state_t* port_state, - sock_state_t* sock_state, - SOCKET socket) { +int port_register_socket(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) @@ -1373,8 +1397,8 @@ int port_register_socket_handle(port_state_t* port_state, return 0; } -void port_unregister_socket_handle(port_state_t* port_state, - sock_state_t* sock_state) { +void port_unregister_socket(port_state_t* port_state, + sock_state_t* sock_state) { tree_del(&port_state->sock_tree, sock_state_to_tree_node(sock_state)); } @@ -1387,7 +1411,7 @@ sock_state_t* port_find_socket(port_state_t* port_state, SOCKET socket) { 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))) + if (queue_is_enqueued(sock_state_to_queue_node(sock_state))) return; queue_append(&port_state->sock_update_queue, sock_state_to_queue_node(sock_state)); @@ -1396,14 +1420,14 @@ void port_request_socket_update(port_state_t* port_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))) + if (!queue_is_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))) + if (queue_is_enqueued(sock_state_to_queue_node(sock_state))) return; queue_append(&port_state->sock_deleted_queue, sock_state_to_queue_node(sock_state)); @@ -1412,11 +1436,28 @@ void port_add_deleted_socket(port_state_t* port_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))) + if (!queue_is_enqueued(sock_state_to_queue_node(sock_state))) return; queue_remove(sock_state_to_queue_node(sock_state)); } +HANDLE port_get_iocp_handle(port_state_t* port_state) { + assert(port_state->iocp_handle != NULL); + return port_state->iocp_handle; +} + +queue_t* port_get_poll_group_queue(port_state_t* port_state) { + return &port_state->poll_group_queue; +} + +port_state_t* port_state_from_handle_tree_node(ts_tree_node_t* tree_node) { + return container_of(tree_node, port_state_t, handle_tree_node); +} + +ts_tree_node_t* port_state_to_handle_tree_node(port_state_t* port_state) { + return &port_state->handle_tree_node; +} + void queue_init(queue_t* queue) { queue_node_init(&queue->head); } @@ -1432,11 +1473,11 @@ static inline void queue__detach_node(queue_node_t* node) { } queue_node_t* queue_first(const queue_t* queue) { - return !queue_empty(queue) ? queue->head.next : NULL; + return !queue_is_empty(queue) ? queue->head.next : NULL; } queue_node_t* queue_last(const queue_t* queue) { - return !queue_empty(queue) ? queue->head.prev : NULL; + return !queue_is_empty(queue) ? queue->head.prev : NULL; } void queue_prepend(queue_t* queue, queue_node_t* node) { @@ -1453,12 +1494,12 @@ void queue_append(queue_t* queue, queue_node_t* node) { queue->head.prev = node; } -void queue_move_first(queue_t* queue, queue_node_t* node) { +void queue_move_to_start(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) { +void queue_move_to_end(queue_t* queue, queue_node_t* node) { queue__detach_node(node); queue_append(queue, node); } @@ -1468,27 +1509,25 @@ void queue_remove(queue_node_t* node) { queue_node_init(node); } -bool queue_empty(const queue_t* queue) { - return !queue_enqueued(&queue->head); +bool queue_is_empty(const queue_t* queue) { + return !queue_is_enqueued(&queue->head); } -bool queue_enqueued(const queue_node_t* node) { +bool queue_is_enqueued(const queue_node_t* node) { return node->prev != node; } -/* clang-format off */ -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; -/* clang-format on */ +#define REFLOCK__REF ((long) 0x00000001UL) +#define REFLOCK__REF_MASK ((long) 0x0fffffffUL) +#define REFLOCK__DESTROY ((long) 0x10000000UL) +#define REFLOCK__DESTROY_MASK ((long) 0xf0000000UL) +#define REFLOCK__POISON ((long) 0x300dead0UL) static HANDLE reflock__keyed_event = NULL; int reflock_global_init(void) { - NTSTATUS status = - NtCreateKeyedEvent(&reflock__keyed_event, ~(ACCESS_MASK) 0, NULL, 0); + NTSTATUS status = NtCreateKeyedEvent( + &reflock__keyed_event, KEYEDEVENT_ALL_ACCESS, NULL, 0); if (status != STATUS_SUCCESS) return_set_error(-1, RtlNtStatusToDosError(status)); return 0; @@ -1514,22 +1553,20 @@ static void reflock__await_event(void* address) { 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); - assert((state & REFLOCK__DESTROY_MASK) == 0); /* Overflow or destroyed. */ } void reflock_unref(reflock_t* reflock) { long state = InterlockedAdd(&reflock->state, -REFLOCK__REF); - long ref_count = state & REFLOCK__REF_MASK; - long destroy = state & REFLOCK__DESTROY_MASK; - unused_var(ref_count); - unused_var(destroy); + /* 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); - else - assert(destroy == 0 || ref_count > 0); } void reflock_unref_and_destroy(reflock_t* reflock) { @@ -1537,8 +1574,8 @@ void reflock_unref_and_destroy(reflock_t* reflock) { InterlockedAdd(&reflock->state, REFLOCK__DESTROY - REFLOCK__REF); long ref_count = state & REFLOCK__REF_MASK; - assert((state & REFLOCK__DESTROY_MASK) == - REFLOCK__DESTROY); /* Underflow or already destroyed. */ + /* 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); @@ -1547,9 +1584,9 @@ void reflock_unref_and_destroy(reflock_t* reflock) { assert(state == REFLOCK__DESTROY); } -static const uint32_t SOCK__KNOWN_EPOLL_EVENTS = - EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | - EPOLLRDBAND | EPOLLWRNORM | EPOLLWRBAND | EPOLLMSG | EPOLLRDHUP; +#define SOCK__KNOWN_EPOLL_EVENTS \ + (EPOLLIN | EPOLLPRI | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | \ + EPOLLRDBAND | EPOLLWRNORM | EPOLLWRBAND | EPOLLMSG | EPOLLRDHUP) typedef enum sock__poll_status { SOCK__POLL_IDLE = 0, @@ -1558,7 +1595,7 @@ typedef enum sock__poll_status { } sock__poll_status_t; typedef struct sock_state { - OVERLAPPED overlapped; + IO_STATUS_BLOCK io_status_block; AFD_POLL_INFO poll_info; queue_node_t queue_node; tree_node_t tree_node; @@ -1579,20 +1616,16 @@ static inline sock_state_t* sock__alloc(void) { } static inline void sock__free(sock_state_t* sock_state) { + assert(sock_state != NULL); 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); +static inline int sock__cancel_poll(sock_state_t* sock_state) { 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); + if (afd_cancel_poll(poll_group_get_afd_device_handle(sock_state->poll_group), + &sock_state->io_status_block) < 0) + return -1; sock_state->poll_status = SOCK__POLL_CANCELLED; sock_state->pending_events = 0; @@ -1627,7 +1660,7 @@ sock_state_t* sock_new(port_state_t* port_state, SOCKET socket) { 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) + if (port_register_socket(port_state, sock_state, socket) < 0) goto err2; return sock_state; @@ -1648,7 +1681,7 @@ static int sock__delete(port_state_t* port_state, sock__cancel_poll(sock_state); port_cancel_socket_update(port_state, sock_state); - port_unregister_socket_handle(port_state, sock_state); + port_unregister_socket(port_state, sock_state); sock_state->delete_pending = true; } @@ -1682,7 +1715,7 @@ int sock_set_event(port_state_t* port_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. */ + * socket for which the EPOLLONESHOT flag was set. */ uint32_t events = ev->events | EPOLLERR | EPOLLHUP; sock_state->user_events = events; @@ -1769,11 +1802,9 @@ int sock_update(port_state_t* port_state, sock_state_t* sock_state) { 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), + if (afd_poll(poll_group_get_afd_device_handle(sock_state->poll_group), &sock_state->poll_info, - &sock_state->overlapped) < 0) { + &sock_state->io_status_block) < 0) { switch (GetLastError()) { case ERROR_IO_PENDING: /* Overlapped poll operation in progress; this is expected. */ @@ -1801,10 +1832,10 @@ int sock_update(port_state_t* port_state, sock_state_t* sock_state) { } int sock_feed_event(port_state_t* port_state, - OVERLAPPED* overlapped, + IO_STATUS_BLOCK* io_status_block, struct epoll_event* ev) { sock_state_t* sock_state = - container_of(overlapped, sock_state_t, overlapped); + container_of(io_status_block, sock_state_t, io_status_block); AFD_POLL_INFO* poll_info = &sock_state->poll_info; uint32_t epoll_events = 0; @@ -1815,10 +1846,10 @@ int sock_feed_event(port_state_t* port_state, /* 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) { + } else if (io_status_block->Status == STATUS_CANCELLED) { /* The poll request was cancelled by CancelIoEx. */ - } else if (!NT_SUCCESS(overlapped->Internal)) { + } else if (!NT_SUCCESS(io_status_block->Status)) { /* The overlapped request itself failed in an unexpected way. */ epoll_events = EPOLLERR; @@ -1855,6 +1886,10 @@ int sock_feed_event(port_state_t* port_state, return 1; } +sock_state_t* sock_state_from_queue_node(queue_node_t* queue_node) { + return container_of(queue_node, sock_state_t, queue_node); +} + queue_node_t* sock_state_to_queue_node(sock_state_t* sock_state) { return &sock_state->queue_node; } @@ -1867,10 +1902,6 @@ 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); @@ -1983,23 +2014,23 @@ static inline void tree__rotate_right(tree_t* tree, tree_node_t* 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); \ +#define TREE__REBALANCE_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) { @@ -2027,9 +2058,9 @@ int tree_add(tree_t* tree, tree_node_t* node, uintptr_t key) { for (; parent && parent->red; parent = node->parent) { if (parent == parent->parent->left) { - TREE__FIXUP_AFTER_INSERT(left, right) + TREE__REBALANCE_AFTER_INSERT(left, right) } else { - TREE__FIXUP_AFTER_INSERT(right, left) + TREE__REBALANCE_AFTER_INSERT(right, left) } } tree->root->red = false; @@ -2037,7 +2068,7 @@ int tree_add(tree_t* tree, tree_node_t* node, uintptr_t key) { return 0; } -#define TREE__FIXUP_AFTER_REMOVE(cis, trans) \ +#define TREE__REBALANCE_AFTER_REMOVE(cis, trans) \ tree_node_t* sibling = parent->trans; \ \ if (sibling->red) { \ @@ -2123,9 +2154,9 @@ void tree_del(tree_t* tree, tree_node_t* node) { if (node == tree->root) break; if (node == parent->left) { - TREE__FIXUP_AFTER_REMOVE(left, right) + TREE__REBALANCE_AFTER_REMOVE(left, right) } else { - TREE__FIXUP_AFTER_REMOVE(right, left) + TREE__REBALANCE_AFTER_REMOVE(right, left) } node = parent; parent = parent->parent; @@ -2152,6 +2183,10 @@ tree_node_t* tree_root(const tree_t* tree) { return tree->root; } +#ifndef SIO_BSP_HANDLE_POLL +#define SIO_BSP_HANDLE_POLL 0x4800001D +#endif + #ifndef SIO_BASE_HANDLE #define SIO_BASE_HANDLE 0x48000022 #endif @@ -2167,20 +2202,52 @@ int ws_global_init(void) { return 0; } -SOCKET ws_get_base_socket(SOCKET socket) { - SOCKET base_socket; +static inline SOCKET ws__ioctl_get_bsp_socket(SOCKET socket, DWORD ioctl) { + SOCKET bsp_socket; DWORD bytes; if (WSAIoctl(socket, - SIO_BASE_HANDLE, + ioctl, NULL, 0, - &base_socket, - sizeof base_socket, + &bsp_socket, + sizeof bsp_socket, &bytes, NULL, - NULL) == SOCKET_ERROR) - return_map_error(INVALID_SOCKET); - - return base_socket; + NULL) != SOCKET_ERROR) + return bsp_socket; + else + return INVALID_SOCKET; +} + +SOCKET ws_get_base_socket(SOCKET socket) { + SOCKET base_socket; + DWORD error; + + for (;;) { + base_socket = ws__ioctl_get_bsp_socket(socket, SIO_BASE_HANDLE); + if (base_socket != INVALID_SOCKET) + return base_socket; + + error = GetLastError(); + if (error == WSAENOTSOCK) + return_set_error(INVALID_SOCKET, error); + + /* Even though Microsoft documentation clearly states that LSPs should + * never intercept the `SIO_BASE_HANDLE` ioctl [1], Komodia based LSPs do + * so anyway, breaking it, with the apparent intention of preventing LSP + * bypass [2]. Fortunately they don't handle `SIO_BSP_HANDLE_POLL`, which + * will at least let us obtain the socket associated with the next winsock + * protocol chain entry. If this succeeds, loop around and call + * `SIO_BASE_HANDLE` again with the returned BSP socket, to make sure that + * we unwrap all layers and retrieve the actual base socket. + * [1] https://docs.microsoft.com/en-us/windows/win32/winsock/winsock-ioctls + * [2] https://www.komodia.com/newwiki/index.php?title=Komodia%27s_Redirector_bug_fixes#Version_2.2.2.6 + */ + base_socket = ws__ioctl_get_bsp_socket(socket, SIO_BSP_HANDLE_POLL); + if (base_socket != INVALID_SOCKET && base_socket != socket) + socket = base_socket; + else + return_set_error(INVALID_SOCKET, error); + } } diff --git a/vendor/ZMQ/external/wepoll/wepoll.h b/vendor/ZMQ/external/wepoll/wepoll.h index 7b522984..daf6bdb0 100644 --- a/vendor/ZMQ/external/wepoll/wepoll.h +++ b/vendor/ZMQ/external/wepoll/wepoll.h @@ -2,7 +2,7 @@ * wepoll - epoll for Windows * https://github.com/piscisaureus/wepoll * - * Copyright 2012-2018, Bert Belder + * Copyright 2012-2020, Bert Belder * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,8 +38,6 @@ #include -/* clang-format off */ - enum EPOLL_EVENTS { EPOLLIN = (int) (1U << 0), EPOLLPRI = (int) (1U << 1), @@ -72,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; diff --git a/vendor/ZMQ/include/zmq.h b/vendor/ZMQ/include/zmq.h index 683cb6a1..90b5567f 100644 --- a/vendor/ZMQ/include/zmq.h +++ b/vendor/ZMQ/include/zmq.h @@ -57,21 +57,6 @@ extern "C" { #endif #include #include -#if defined _WIN32 -// Set target version to Windows Server 2008, Windows Vista or higher. -// Windows XP (0x0501) is supported but without client & server socket types. -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 -#endif - -#ifdef __MINGW32__ -// Require Windows XP or higher with MinGW for getaddrinfo(). -#if (_WIN32_WINNT >= 0x0501) -#else -#error You need at least Windows XP target -#endif -#endif -#endif /* Handle DSO symbol visibility */ #if defined _WIN32 @@ -683,11 +668,13 @@ ZMQ_EXPORT void zmq_threadclose (void *thread_); #define ZMQ_HELLO_MSG 110 #define ZMQ_DISCONNECT_MSG 111 #define ZMQ_PRIORITY 112 +#define ZMQ_BUSY_POLL 113 +#define ZMQ_HICCUP_MSG 114 /* DRAFT ZMQ_RECONNECT_STOP options */ #define ZMQ_RECONNECT_STOP_CONN_REFUSED 0x1 #define ZMQ_RECONNECT_STOP_HANDSHAKE_FAILED 0x2 -#define ZMQ_RECONNECT_STOP_AFTER_DISCONNECT 0x3 +#define ZMQ_RECONNECT_STOP_AFTER_DISCONNECT 0x4 /* DRAFT Context options */ #define ZMQ_ZERO_COPY_RECV 10 diff --git a/vendor/ZMQ/packaging/redhat/zeromq.spec b/vendor/ZMQ/packaging/redhat/zeromq.spec index 67963c0e..d9375ac6 100644 --- a/vendor/ZMQ/packaging/redhat/zeromq.spec +++ b/vendor/ZMQ/packaging/redhat/zeromq.spec @@ -13,8 +13,8 @@ Name: zeromq Version: 4.3.5 Release: 1%{?dist} Summary: The ZeroMQ messaging library -Group: Applications/Internet -License: LGPLv3+ +Group: Development/Libraries/C and C++ +License: LGPL-3.0-or-later URL: http://www.zeromq.org/ Source: http://download.zeromq.org/%{name}-%{version}.tar.gz Prefix: %{_prefix} @@ -228,11 +228,12 @@ autoreconf -fi %{__make} %{?_smp_mflags} +%check +%{__make} check VERBOSE=1 + %install [ "%{buildroot}" != "/" ] && %{__rm} -rf %{buildroot} - # Install the package to build area -%{__make} check VERBOSE=1 %makeinstall %post diff --git a/vendor/ZMQ/src/client.cpp b/vendor/ZMQ/src/client.cpp index f26b1386..ef3fc1cc 100644 --- a/vendor/ZMQ/src/client.cpp +++ b/vendor/ZMQ/src/client.cpp @@ -38,6 +38,7 @@ zmq::client_t::client_t (class ctx_t *parent_, uint32_t tid_, int sid_) : { options.type = ZMQ_CLIENT; options.can_send_hello_msg = true; + options.can_recv_hiccup_msg = true; } zmq::client_t::~client_t () diff --git a/vendor/ZMQ/src/clock.cpp b/vendor/ZMQ/src/clock.cpp index 79522ad3..63c0100a 100644 --- a/vendor/ZMQ/src/clock.cpp +++ b/vendor/ZMQ/src/clock.cpp @@ -126,9 +126,11 @@ static f_compatible_get_tick_count64 my_get_tick_count64 = init_compatible_get_tick_count64 (); #endif +#ifndef ZMQ_HAVE_WINDOWS const uint64_t usecs_per_msec = 1000; -const uint64_t usecs_per_sec = 1000000; const uint64_t nsecs_per_usec = 1000; +#endif +const uint64_t usecs_per_sec = 1000000; zmq::clock_t::clock_t () : _last_tsc (rdtsc ()), @@ -193,6 +195,7 @@ uint64_t zmq::clock_t::now_us () #else + LIBZMQ_UNUSED (nsecs_per_usec); // Use POSIX gettimeofday function to get precise time. struct timeval tv; int rc = gettimeofday (&tv, NULL); diff --git a/vendor/ZMQ/src/command.hpp b/vendor/ZMQ/src/command.hpp index c92f7a7c..234c858a 100644 --- a/vendor/ZMQ/src/command.hpp +++ b/vendor/ZMQ/src/command.hpp @@ -33,6 +33,7 @@ #include #include "stdint.hpp" #include "endpoint.hpp" +#include "platform.hpp" namespace zmq { @@ -44,12 +45,7 @@ class socket_base_t; // This structure defines the commands that can be sent between threads. -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4324) // C4324: alignment padding warnings -__declspec(align (64)) -#endif - struct command_t +struct command_t { // Object to process the command. zmq::object_t *destination; @@ -216,9 +212,12 @@ __declspec(align (64)) } args; #ifdef _MSC_VER }; -#pragma warning(pop) #else -} __attribute__ ((aligned (64))); +} +#ifdef HAVE_POSIX_MEMALIGN +__attribute__ ((aligned (ZMQ_CACHELINE_SIZE))) +#endif +; #endif } diff --git a/vendor/ZMQ/src/curve_client_tools.hpp b/vendor/ZMQ/src/curve_client_tools.hpp index 3c098ba9..7e0d25b3 100644 --- a/vendor/ZMQ/src/curve_client_tools.hpp +++ b/vendor/ZMQ/src/curve_client_tools.hpp @@ -180,6 +180,12 @@ struct curve_client_tools_t // Create Box [C + vouch + metadata](C'->S') std::fill (initiate_plaintext.begin (), initiate_plaintext.begin () + crypto_box_ZEROBYTES, 0); + + // False positives due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99578 +#if __GNUC__ >= 11 +#pragma GCC diagnostic ignored "-Warray-bounds" +#pragma GCC diagnostic ignored "-Wstringop-overflow=" +#endif memcpy (&initiate_plaintext[crypto_box_ZEROBYTES], public_key_, 32); memcpy (&initiate_plaintext[crypto_box_ZEROBYTES + 32], vouch_nonce + 8, 16); @@ -189,6 +195,10 @@ struct curve_client_tools_t memcpy (&initiate_plaintext[crypto_box_ZEROBYTES + 48 + 80], metadata_plaintext_, metadata_length_); } +#if __GNUC__ >= 11 +#pragma GCC diagnostic pop +#pragma GCC diagnostic pop +#endif memcpy (initiate_nonce, "CurveZMQINITIATE", 16); put_uint64 (initiate_nonce + 16, cn_nonce_); diff --git a/vendor/ZMQ/src/dealer.cpp b/vendor/ZMQ/src/dealer.cpp index bf1c41da..c5584974 100644 --- a/vendor/ZMQ/src/dealer.cpp +++ b/vendor/ZMQ/src/dealer.cpp @@ -39,6 +39,7 @@ zmq::dealer_t::dealer_t (class ctx_t *parent_, uint32_t tid_, int sid_) : { options.type = ZMQ_DEALER; options.can_send_hello_msg = true; + options.can_recv_hiccup_msg = true; } zmq::dealer_t::~dealer_t () diff --git a/vendor/ZMQ/src/dgram.cpp b/vendor/ZMQ/src/dgram.cpp index 879ff4da..437b4819 100644 --- a/vendor/ZMQ/src/dgram.cpp +++ b/vendor/ZMQ/src/dgram.cpp @@ -39,7 +39,6 @@ zmq::dgram_t::dgram_t (class ctx_t *parent_, uint32_t tid_, int sid_) : socket_base_t (parent_, tid_, sid_), _pipe (NULL), - _last_in (NULL), _more_out (false) { options.type = ZMQ_DGRAM; @@ -71,9 +70,6 @@ void zmq::dgram_t::xattach_pipe (pipe_t *pipe_, void zmq::dgram_t::xpipe_terminated (pipe_t *pipe_) { if (pipe_ == _pipe) { - if (_last_in == _pipe) { - _last_in = NULL; - } _pipe = NULL; } } @@ -147,7 +143,6 @@ int zmq::dgram_t::xrecv (msg_t *msg_) errno = EAGAIN; return -1; } - _last_in = _pipe; return 0; } diff --git a/vendor/ZMQ/src/dgram.hpp b/vendor/ZMQ/src/dgram.hpp index 51385aad..d84dac69 100644 --- a/vendor/ZMQ/src/dgram.hpp +++ b/vendor/ZMQ/src/dgram.hpp @@ -62,8 +62,6 @@ class dgram_t ZMQ_FINAL : public socket_base_t private: zmq::pipe_t *_pipe; - zmq::pipe_t *_last_in; - // If true, more outgoing message parts are expected. bool _more_out; diff --git a/vendor/ZMQ/src/dist.cpp b/vendor/ZMQ/src/dist.cpp index 7795bb36..a205f70f 100644 --- a/vendor/ZMQ/src/dist.cpp +++ b/vendor/ZMQ/src/dist.cpp @@ -64,6 +64,18 @@ void zmq::dist_t::attach (pipe_t *pipe_) } } +bool zmq::dist_t::has_pipe (pipe_t *pipe_) +{ + std::size_t claimed_index = _pipes.index (pipe_); + + // If pipe claims to be outside the available index space it can't be in the distributor. + if (claimed_index >= _pipes.size ()) { + return false; + } + + return _pipes[claimed_index] == pipe_; +} + void zmq::dist_t::match (pipe_t *pipe_) { // If pipe is already matching do nothing. diff --git a/vendor/ZMQ/src/dist.hpp b/vendor/ZMQ/src/dist.hpp index f772a716..a5926cfb 100644 --- a/vendor/ZMQ/src/dist.hpp +++ b/vendor/ZMQ/src/dist.hpp @@ -51,6 +51,9 @@ class dist_t // Adds the pipe to the distributor object. void attach (zmq::pipe_t *pipe_); + // Checks if this pipe is present in the distributor. + bool has_pipe (zmq::pipe_t *pipe_); + // Activates pipe that have previously reached high watermark. void activated (zmq::pipe_t *pipe_); diff --git a/vendor/ZMQ/src/epoll.cpp b/vendor/ZMQ/src/epoll.cpp index fc641b74..da95bd0a 100644 --- a/vendor/ZMQ/src/epoll.cpp +++ b/vendor/ZMQ/src/epoll.cpp @@ -131,7 +131,7 @@ void zmq::epoll_t::reset_pollin (handle_t handle_) { check_thread (); poll_entry_t *pe = static_cast (handle_); - pe->ev.events &= ~(static_cast (EPOLLIN)); + pe->ev.events &= ~(static_cast (EPOLLIN)); const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); errno_assert (rc != -1); } @@ -149,7 +149,7 @@ void zmq::epoll_t::reset_pollout (handle_t handle_) { check_thread (); poll_entry_t *pe = static_cast (handle_); - pe->ev.events &= ~(static_cast (EPOLLOUT)); + pe->ev.events &= ~(static_cast (EPOLLOUT)); const int rc = epoll_ctl (_epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); errno_assert (rc != -1); } diff --git a/vendor/ZMQ/src/fq.cpp b/vendor/ZMQ/src/fq.cpp index 2a700977..d3dfeab6 100644 --- a/vendor/ZMQ/src/fq.cpp +++ b/vendor/ZMQ/src/fq.cpp @@ -33,7 +33,7 @@ #include "err.hpp" #include "msg.hpp" -zmq::fq_t::fq_t () : _active (0), _last_in (NULL), _current (0), _more (false) +zmq::fq_t::fq_t () : _active (0), _current (0), _more (false) { } @@ -62,10 +62,6 @@ void zmq::fq_t::pipe_terminated (pipe_t *pipe_) _current = 0; } _pipes.erase (pipe_); - - if (_last_in == pipe_) { - _last_in = NULL; - } } void zmq::fq_t::activated (pipe_t *pipe_) @@ -100,7 +96,6 @@ int zmq::fq_t::recvpipe (msg_t *msg_, pipe_t **pipe_) *pipe_ = _pipes[_current]; _more = (msg_->flags () & msg_t::more) != 0; if (!_more) { - _last_in = _pipes[_current]; _current = (_current + 1) % _active; } return 0; diff --git a/vendor/ZMQ/src/fq.hpp b/vendor/ZMQ/src/fq.hpp index 26724101..ed019126 100644 --- a/vendor/ZMQ/src/fq.hpp +++ b/vendor/ZMQ/src/fq.hpp @@ -65,11 +65,6 @@ class fq_t // beginning of the pipes array. pipes_t::size_type _active; - // Pointer to the last pipe we received message from. - // NULL when no message has been received or the pipe - // has terminated. - pipe_t *_last_in; - // Index of the next bound pipe to read a message from. pipes_t::size_type _current; diff --git a/vendor/ZMQ/src/ip.cpp b/vendor/ZMQ/src/ip.cpp index d150da76..8d1ea13b 100644 --- a/vendor/ZMQ/src/ip.cpp +++ b/vendor/ZMQ/src/ip.cpp @@ -868,24 +868,48 @@ void zmq::assert_success_or_recoverable (zmq::fd_t s_, int rc_) } #ifdef ZMQ_HAVE_IPC + +#if defined ZMQ_HAVE_WINDOWS +char *widechar_to_utf8 (const wchar_t *widestring) +{ + int nch, n; + char *utf8 = 0; + nch = WideCharToMultiByte (CP_UTF8, 0, widestring, -1, 0, 0, NULL, NULL); + if (nch > 0) { + utf8 = (char *) malloc ((nch + 1) * sizeof (char)); + n = WideCharToMultiByte (CP_UTF8, 0, widestring, -1, utf8, nch, NULL, + NULL); + utf8[nch] = 0; + } + return utf8; +} +#endif + int zmq::create_ipc_wildcard_address (std::string &path_, std::string &file_) { #if defined ZMQ_HAVE_WINDOWS - char buffer[MAX_PATH]; + wchar_t buffer[MAX_PATH]; { - const errno_t rc = tmpnam_s (buffer); + const errno_t rc = _wtmpnam_s (buffer); errno_assert (rc == 0); } // TODO or use CreateDirectoryA and specify permissions? - const int rc = _mkdir (buffer); + const int rc = _wmkdir (buffer); if (rc != 0) { return -1; } - path_.assign (buffer); + char *tmp = widechar_to_utf8 (buffer); + if (tmp == 0) { + return -1; + } + + path_.assign (tmp); file_ = path_ + "/socket"; + + free (tmp); #else std::string tmp_path; diff --git a/vendor/ZMQ/src/mutex.hpp b/vendor/ZMQ/src/mutex.hpp index 8ff02269..25ad8451 100644 --- a/vendor/ZMQ/src/mutex.hpp +++ b/vendor/ZMQ/src/mutex.hpp @@ -35,7 +35,7 @@ // Mutex class encapsulates OS mutex in a platform-independent way. -#ifdef ZMQ_HAVE_WINDOWS +#if defined(ZMQ_HAVE_WINDOWS) && !defined(ZMQ_USE_CV_IMPL_PTHREADS) #include "windows.hpp" diff --git a/vendor/ZMQ/src/options.cpp b/vendor/ZMQ/src/options.cpp index 1b7d2175..f0d5e7c6 100644 --- a/vendor/ZMQ/src/options.cpp +++ b/vendor/ZMQ/src/options.cpp @@ -254,7 +254,10 @@ zmq::options_t::options_t () : hello_msg (), can_send_hello_msg (false), disconnect_msg (), - can_recv_disconnect_msg (false) + can_recv_disconnect_msg (false), + hiccup_msg (), + can_recv_hiccup_msg (false), + busy_poll (0) { memset (curve_public_key, 0, CURVE_KEYSIZE); memset (curve_secret_key, 0, CURVE_KEYSIZE); @@ -802,6 +805,12 @@ int zmq::options_t::setsockopt (int option_, } break; + case ZMQ_BUSY_POLL: + if (is_int) { + busy_poll = value; + return 0; + } + break; #ifdef ZMQ_HAVE_WSS case ZMQ_WSS_KEY_PEM: // TODO: check if valid certificate @@ -852,6 +861,18 @@ int zmq::options_t::setsockopt (int option_, } break; + case ZMQ_HICCUP_MSG: + if (optvallen_ > 0) { + unsigned char *bytes = (unsigned char *) optval_; + hiccup_msg = + std::vector (bytes, bytes + optvallen_); + } else { + hiccup_msg = std::vector (); + } + + return 0; + + #endif default: @@ -1285,6 +1306,12 @@ int zmq::options_t::getsockopt (int option_, return 0; } break; + + case ZMQ_BUSY_POLL: + if (is_int) { + *value = busy_poll; + } + break; #endif diff --git a/vendor/ZMQ/src/options.hpp b/vendor/ZMQ/src/options.hpp index 6aa8c321..bd238e50 100644 --- a/vendor/ZMQ/src/options.hpp +++ b/vendor/ZMQ/src/options.hpp @@ -308,6 +308,13 @@ struct options_t // Disconnect msg std::vector disconnect_msg; bool can_recv_disconnect_msg; + + // Hiccup msg + std::vector hiccup_msg; + bool can_recv_hiccup_msg; + + // This option removes several delays caused by scheduling, interrupts and context switching. + int busy_poll; }; inline bool get_effective_conflate_option (const options_t &options) diff --git a/vendor/ZMQ/src/pair.cpp b/vendor/ZMQ/src/pair.cpp index f3bfe34b..d1fb80b1 100644 --- a/vendor/ZMQ/src/pair.cpp +++ b/vendor/ZMQ/src/pair.cpp @@ -36,8 +36,7 @@ zmq::pair_t::pair_t (class ctx_t *parent_, uint32_t tid_, int sid_) : socket_base_t (parent_, tid_, sid_), - _pipe (NULL), - _last_in (NULL) + _pipe (NULL) { options.type = ZMQ_PAIR; } @@ -67,9 +66,6 @@ void zmq::pair_t::xattach_pipe (pipe_t *pipe_, void zmq::pair_t::xpipe_terminated (pipe_t *pipe_) { if (pipe_ == _pipe) { - if (_last_in == _pipe) { - _last_in = NULL; - } _pipe = NULL; } } @@ -117,7 +113,6 @@ int zmq::pair_t::xrecv (msg_t *msg_) errno = EAGAIN; return -1; } - _last_in = _pipe; return 0; } diff --git a/vendor/ZMQ/src/pair.hpp b/vendor/ZMQ/src/pair.hpp index 2d7bc071..74996d3b 100644 --- a/vendor/ZMQ/src/pair.hpp +++ b/vendor/ZMQ/src/pair.hpp @@ -62,8 +62,6 @@ class pair_t ZMQ_FINAL : public socket_base_t private: zmq::pipe_t *_pipe; - zmq::pipe_t *_last_in; - ZMQ_NON_COPYABLE_NOR_MOVABLE (pair_t) }; } diff --git a/vendor/ZMQ/src/peer.cpp b/vendor/ZMQ/src/peer.cpp index 4d7e7461..3e3f27ed 100644 --- a/vendor/ZMQ/src/peer.cpp +++ b/vendor/ZMQ/src/peer.cpp @@ -42,6 +42,7 @@ zmq::peer_t::peer_t (class ctx_t *parent_, uint32_t tid_, int sid_) : options.type = ZMQ_PEER; options.can_send_hello_msg = true; options.can_recv_disconnect_msg = true; + options.can_recv_hiccup_msg = true; } uint32_t zmq::peer_t::connect_peer (const char *endpoint_uri_) diff --git a/vendor/ZMQ/src/pipe.cpp b/vendor/ZMQ/src/pipe.cpp index b391b6ea..31e553b3 100644 --- a/vendor/ZMQ/src/pipe.cpp +++ b/vendor/ZMQ/src/pipe.cpp @@ -597,7 +597,7 @@ void zmq::pipe_t::process_pipe_peer_stats (uint64_t queue_count_, void zmq::pipe_t::send_disconnect_msg () { - if (_disconnect_msg.size () > 0) { + if (_disconnect_msg.size () > 0 && _out_pipe) { // Rollback any incomplete message in the pipe, and push the disconnect message. rollback (); @@ -615,3 +615,15 @@ void zmq::pipe_t::set_disconnect_msg ( _disconnect_msg.init_buffer (&disconnect_[0], disconnect_.size ()); errno_assert (rc == 0); } + +void zmq::pipe_t::send_hiccup_msg (const std::vector &hiccup_) +{ + if (!hiccup_.empty () && _out_pipe) { + msg_t msg; + const int rc = msg.init_buffer (&hiccup_[0], hiccup_.size ()); + errno_assert (rc == 0); + + _out_pipe->write (msg, false); + flush (); + } +} diff --git a/vendor/ZMQ/src/pipe.hpp b/vendor/ZMQ/src/pipe.hpp index 8136b860..f1cbcffa 100644 --- a/vendor/ZMQ/src/pipe.hpp +++ b/vendor/ZMQ/src/pipe.hpp @@ -150,6 +150,8 @@ class pipe_t ZMQ_FINAL : public object_t, void send_disconnect_msg (); void set_disconnect_msg (const std::vector &disconnect_); + void send_hiccup_msg (const std::vector &hiccup_); + private: // Type of the underlying lock-free pipe. typedef ypipe_base_t upipe_t; diff --git a/vendor/ZMQ/src/polling_util.hpp b/vendor/ZMQ/src/polling_util.hpp index 2af8c456..f0ec420d 100644 --- a/vendor/ZMQ/src/polling_util.hpp +++ b/vendor/ZMQ/src/polling_util.hpp @@ -46,7 +46,7 @@ template class fast_vector_t explicit fast_vector_t (const size_t nitems_) { if (nitems_ > S) { - _buf = static_cast (malloc (nitems_ * sizeof (T))); + _buf = new (std::nothrow) T[nitems_]; // TODO since this function is called by a client, we could return errno == ENOMEM here alloc_assert (_buf); } else { @@ -59,7 +59,7 @@ template class fast_vector_t ~fast_vector_t () { if (_buf != _static_buf) - free (_buf); + delete[] _buf; } private: @@ -109,9 +109,9 @@ timeout_t compute_timeout (bool first_pass_, long timeout_, uint64_t now_, uint64_t end_); #elif defined ZMQ_POLL_BASED_ON_SELECT +#if defined ZMQ_HAVE_WINDOWS inline size_t valid_pollset_bytes (const fd_set &pollset_) { -#if defined ZMQ_HAVE_WINDOWS // On Windows we don't need to copy the whole fd_set. // SOCKETS are continuous from the beginning of fd_array in fd_set. // We just need to copy fd_count elements of fd_array. @@ -119,10 +119,14 @@ inline size_t valid_pollset_bytes (const fd_set &pollset_) return reinterpret_cast ( &pollset_.fd_array[pollset_.fd_count]) - reinterpret_cast (&pollset_); -#else - return sizeof (fd_set); -#endif } +#else +inline size_t valid_pollset_bytes (const fd_set & /*pollset_*/) +{ + return sizeof (fd_set); +} +#endif + #if defined ZMQ_HAVE_WINDOWS // struct fd_set { diff --git a/vendor/ZMQ/src/radix_tree.cpp b/vendor/ZMQ/src/radix_tree.cpp index 04374eec..2a38ebf0 100644 --- a/vendor/ZMQ/src/radix_tree.cpp +++ b/vendor/ZMQ/src/radix_tree.cpp @@ -558,7 +558,7 @@ visit_keys (node_t node_, for (size_t i = 0, edgecount = node_.edgecount (); i < edgecount; ++i) { visit_keys (node_.node_at (i), buffer_, func_, arg_); } - buffer_.resize (buffer_.size () - prefix_length); + buffer_.resize (static_cast (buffer_.size () - prefix_length)); } void zmq::radix_tree_t::apply ( diff --git a/vendor/ZMQ/src/random.cpp b/vendor/ZMQ/src/random.cpp index 17c3537d..4700aa58 100644 --- a/vendor/ZMQ/src/random.cpp +++ b/vendor/ZMQ/src/random.cpp @@ -151,8 +151,13 @@ static void manage_random (bool init_) if (init_) { int rc = sodium_init (); zmq_assert (rc != -1); +#if defined(ZMQ_LIBSODIUM_RANDOMBYTES_CLOSE) } else { + // randombytes_close either a no-op or not threadsafe + // doing this without refcounting can cause crashes + // if called while a context is active randombytes_close (); +#endif } #else LIBZMQ_UNUSED (init_); diff --git a/vendor/ZMQ/src/session_base.cpp b/vendor/ZMQ/src/session_base.cpp index 551ae318..f6452146 100644 --- a/vendor/ZMQ/src/session_base.cpp +++ b/vendor/ZMQ/src/session_base.cpp @@ -460,14 +460,18 @@ void zmq::session_base_t::engine_error (bool handshaked_, if (_pipe) { clean_pipes (); -#ifdef ZMQ_BUILD_DRAFT_API // Only send disconnect message if socket was accepted and handshake was completed if (!_active && handshaked_ && options.can_recv_disconnect_msg && !options.disconnect_msg.empty ()) { _pipe->set_disconnect_msg (options.disconnect_msg); _pipe->send_disconnect_msg (); } -#endif + + // Only send hiccup message if socket was connected and handshake was completed + if (_active && handshaked_ && options.can_recv_hiccup_msg + && !options.hiccup_msg.empty ()) { + _pipe->send_hiccup_msg (options.hiccup_msg); + } } zmq_assert (reason_ == i_engine::connection_error diff --git a/vendor/ZMQ/src/tcp.cpp b/vendor/ZMQ/src/tcp.cpp index c37bed02..e75c482b 100644 --- a/vendor/ZMQ/src/tcp.cpp +++ b/vendor/ZMQ/src/tcp.cpp @@ -341,6 +341,21 @@ void zmq::tcp_tune_loopback_fast_path (const fd_t socket_) #endif } +void zmq::tune_tcp_busy_poll (fd_t socket_, int busy_poll_) +{ +#if defined(ZMQ_HAVE_BUSY_POLL) + if (busy_poll_ > 0) { + const int rc = + setsockopt (socket_, SOL_SOCKET, SO_BUSY_POLL, + reinterpret_cast (&busy_poll_), sizeof (int)); + assert_success_or_recoverable (socket_, rc); + } +#else + LIBZMQ_UNUSED (socket_); + LIBZMQ_UNUSED (busy_poll_); +#endif +} + zmq::fd_t zmq::tcp_open_socket (const char *address_, const zmq::options_t &options_, bool local_, @@ -398,6 +413,9 @@ zmq::fd_t zmq::tcp_open_socket (const char *address_, if (options_.rcvbuf >= 0) set_tcp_receive_buffer (s, options_.rcvbuf); + // This option removes several delays caused by scheduling, interrupts and context switching. + if (options_.busy_poll) + tune_tcp_busy_poll (s, options_.busy_poll); return s; setsockopt_error: diff --git a/vendor/ZMQ/src/tcp.hpp b/vendor/ZMQ/src/tcp.hpp index fc355a63..0b5e9759 100644 --- a/vendor/ZMQ/src/tcp.hpp +++ b/vendor/ZMQ/src/tcp.hpp @@ -68,6 +68,8 @@ int tcp_read (fd_t s_, void *data_, size_t size_); void tcp_tune_loopback_fast_path (fd_t socket_); +void tune_tcp_busy_poll (fd_t socket_, int busy_poll_); + // Resolves the given address_ string, opens a socket and sets socket options // according to the passed options_. On success, returns the socket // descriptor and assigns the resolved address to out_tcp_addr_. In case of diff --git a/vendor/ZMQ/src/vmci.cpp b/vendor/ZMQ/src/vmci.cpp index 4edd096b..b8807e35 100644 --- a/vendor/ZMQ/src/vmci.cpp +++ b/vendor/ZMQ/src/vmci.cpp @@ -28,7 +28,9 @@ */ #include "precompiled.hpp" +#include "ip.hpp" #include "vmci.hpp" +#include "vmci_address.hpp" #if defined ZMQ_HAVE_VMCI @@ -97,4 +99,23 @@ void zmq::tune_vmci_connect_timeout (ctx_t *context_, #endif } +zmq::fd_t zmq::vmci_open_socket (const char *address_, + const zmq::options_t &options_, + zmq::vmci_address_t *out_vmci_addr_) +{ + // Convert the textual address into address structure. + int rc = out_vmci_addr_->resolve (address_); + if (rc != 0) + return retired_fd; + + // Create the socket. + fd_t s = open_socket (out_vmci_addr_->family (), SOCK_STREAM, 0); + + if (s == retired_fd) { + return retired_fd; + } + + return s; +} + #endif diff --git a/vendor/ZMQ/src/vmci.hpp b/vendor/ZMQ/src/vmci.hpp index 16a0c734..a79b4bf3 100644 --- a/vendor/ZMQ/src/vmci.hpp +++ b/vendor/ZMQ/src/vmci.hpp @@ -59,6 +59,10 @@ void tune_vmci_connect_timeout (ctx_t *context_, fd_t sockfd_, struct timeval timeout_); #endif + +fd_t vmci_open_socket (const char *address_, + const options_t &options_, + vmci_address_t *out_vmci_addr_); } #endif diff --git a/vendor/ZMQ/src/vmci_address.cpp b/vendor/ZMQ/src/vmci_address.cpp index cf97d99c..7af36a02 100644 --- a/vendor/ZMQ/src/vmci_address.cpp +++ b/vendor/ZMQ/src/vmci_address.cpp @@ -39,6 +39,11 @@ #include "err.hpp" +zmq::vmci_address_t::vmci_address_t () +{ + memset (&address, 0, sizeof address); +} + zmq::vmci_address_t::vmci_address_t (ctx_t *parent_) : parent (parent_) { memset (&address, 0, sizeof address); @@ -56,10 +61,6 @@ zmq::vmci_address_t::vmci_address_t (const sockaddr *sa, memcpy (&address, sa, sa_len); } -zmq::vmci_address_t::~vmci_address_t () -{ -} - int zmq::vmci_address_t::resolve (const char *path_) { // Find the ':' at end that separates address from the port number. @@ -125,7 +126,7 @@ int zmq::vmci_address_t::resolve (const char *path_) return 0; } -int zmq::vmci_address_t::to_string (std::string &addr_) +int zmq::vmci_address_t::to_string (std::string &addr_) const { if (address.svm_family != parent->get_vmci_socket_family ()) { addr_.clear (); @@ -164,4 +165,13 @@ socklen_t zmq::vmci_address_t::addrlen () const return static_cast (sizeof address); } +#if defined ZMQ_HAVE_WINDOWS +unsigned short zmq::vmci_address_t::family () const +#else +sa_family_t zmq::vmci_address_t::family () const +#endif +{ + return parent->get_vmci_socket_family (); +} + #endif diff --git a/vendor/ZMQ/src/vmci_address.hpp b/vendor/ZMQ/src/vmci_address.hpp index 16dab4a3..ae6b39fb 100644 --- a/vendor/ZMQ/src/vmci_address.hpp +++ b/vendor/ZMQ/src/vmci_address.hpp @@ -43,16 +43,21 @@ namespace zmq class vmci_address_t { public: + vmci_address_t (); vmci_address_t (ctx_t *parent_); vmci_address_t (const sockaddr *sa, socklen_t sa_len, ctx_t *parent_); - ~vmci_address_t (); // This function sets up the address for VMCI transport. int resolve (const char *path_); // The opposite to resolve() - int to_string (std::string &addr_); + int to_string (std::string &addr_) const; +#if defined ZMQ_HAVE_WINDOWS + unsigned short family () const; +#else + sa_family_t family () const; +#endif const sockaddr *addr () const; socklen_t addrlen () const; @@ -60,8 +65,6 @@ class vmci_address_t struct sockaddr_vm address; ctx_t *parent; - vmci_address_t (); - ZMQ_NON_COPYABLE_NOR_MOVABLE (vmci_address_t) }; } diff --git a/vendor/ZMQ/src/vmci_connecter.cpp b/vendor/ZMQ/src/vmci_connecter.cpp index 5768c78e..2e0e4b4f 100644 --- a/vendor/ZMQ/src/vmci_connecter.cpp +++ b/vendor/ZMQ/src/vmci_connecter.cpp @@ -35,69 +35,41 @@ #include -#include "stream_engine.hpp" #include "io_thread.hpp" #include "platform.hpp" #include "random.hpp" #include "err.hpp" #include "ip.hpp" #include "address.hpp" -#include "session_base.hpp" #include "vmci_address.hpp" #include "vmci.hpp" +#include "session_base.hpp" zmq::vmci_connecter_t::vmci_connecter_t (class io_thread_t *io_thread_, class session_base_t *session_, const options_t &options_, - const address_t *addr_, + address_t *addr_, bool delayed_start_) : - own_t (io_thread_, options_), - io_object_t (io_thread_), - addr (addr_), - s (retired_fd), - handle_valid (false), - delayed_start (delayed_start_), - timer_started (false), - session (session_), - current_reconnect_ivl (options.reconnect_ivl) + stream_connecter_base_t ( + io_thread_, session_, options_, addr_, delayed_start_), + _connect_timer_started (false) { - zmq_assert (addr); - zmq_assert (addr->protocol == "vmci"); - addr->to_string (endpoint); - socket = session->get_socket (); + zmq_assert (_addr->protocol == protocol_name::vmci); } zmq::vmci_connecter_t::~vmci_connecter_t () { - zmq_assert (!timer_started); - zmq_assert (!handle_valid); - zmq_assert (s == retired_fd); -} - -void zmq::vmci_connecter_t::process_plug () -{ - if (delayed_start) - add_reconnect_timer (); - else - start_connecting (); + zmq_assert (!_connect_timer_started); } void zmq::vmci_connecter_t::process_term (int linger_) { - if (timer_started) { - cancel_timer (reconnect_timer_id); - timer_started = false; + if (_connect_timer_started) { + cancel_timer (connect_timer_id); + _connect_timer_started = false; } - if (handle_valid) { - rm_fd (handle); - handle_valid = false; - } - - if (s != retired_fd) - close (); - - own_t::process_term (linger_); + stream_connecter_base_t::process_term (linger_); } void zmq::vmci_connecter_t::in_event () @@ -110,9 +82,26 @@ void zmq::vmci_connecter_t::in_event () void zmq::vmci_connecter_t::out_event () { - fd_t fd = connect (); - rm_fd (handle); - handle_valid = false; + if (_connect_timer_started) { + cancel_timer (connect_timer_id); + _connect_timer_started = false; + } + + // TODO this is still very similar to (t)ipc_connecter_t, maybe the + // differences can be factored out + + rm_handle (); + + const fd_t fd = connect (); + + if (fd == retired_fd + && ((options.reconnect_stop & ZMQ_RECONNECT_STOP_CONN_REFUSED) + && errno == ECONNREFUSED)) { + send_conn_failed (_session); + close (); + terminate (); + return; + } // Handle the error condition by attempt to reconnect. if (fd == retired_fd) { @@ -135,148 +124,154 @@ void zmq::vmci_connecter_t::out_event () #endif } - // Create the engine object for this connection. - stream_engine_t *engine = new (std::nothrow) stream_engine_t ( - fd, options, make_unconnected_bind_endpoint_pair (endpoint)); - alloc_assert (engine); + create_engine ( + fd, zmq::vmci_connecter_t::get_socket_name (fd, socket_end_local)); +} - // Attach the engine to the corresponding session object. - send_attach (session, engine); +std::string +zmq::vmci_connecter_t::get_socket_name (zmq::fd_t fd_, + socket_end_t socket_end_) const +{ + struct sockaddr_storage ss; + const zmq_socklen_t sl = get_socket_address (fd_, socket_end_, &ss); + if (sl == 0) { + return std::string (); + } - // Shut the connecter down. - terminate (); - - socket->event_connected (make_unconnected_bind_endpoint_pair (endpoint), - fd); + const vmci_address_t addr (reinterpret_cast (&ss), sl, + this->get_ctx ()); + std::string address_string; + addr.to_string (address_string); + return address_string; } void zmq::vmci_connecter_t::timer_event (int id_) { - zmq_assert (id_ == reconnect_timer_id); - timer_started = false; - start_connecting (); + if (id_ == connect_timer_id) { + _connect_timer_started = false; + rm_handle (); + close (); + add_reconnect_timer (); + } else + stream_connecter_base_t::timer_event (id_); } void zmq::vmci_connecter_t::start_connecting () { // Open the connecting socket. - int rc = open (); + const int rc = open (); // Connect may succeed in synchronous manner. if (rc == 0) { - handle = add_fd (s); - handle_valid = true; + _handle = add_fd (_s); out_event (); } + // Connection establishment may be delayed. Poll for its completion. + else if (rc == -1 && errno == EINPROGRESS) { + _handle = add_fd (_s); + set_pollout (_handle); + _socket->event_connect_delayed ( + make_unconnected_connect_endpoint_pair (_endpoint), zmq_errno ()); + + // add userspace connect timeout + add_connect_timer (); + } + // Handle any other error condition by eventual reconnect. else { - if (s != retired_fd) + if (_s != retired_fd) close (); add_reconnect_timer (); } } -void zmq::vmci_connecter_t::add_reconnect_timer () +void zmq::vmci_connecter_t::add_connect_timer () { - if (options.reconnect_ivl > 0) { - int rc_ivl = get_new_reconnect_ivl (); - add_timer (rc_ivl, reconnect_timer_id); - socket->event_connect_retried ( - make_unconnected_bind_endpoint_pair (endpoint), rc_ivl); - timer_started = true; + if (options.connect_timeout > 0) { + add_timer (options.connect_timeout, connect_timer_id); + _connect_timer_started = true; } } -int zmq::vmci_connecter_t::get_new_reconnect_ivl () -{ - // The new interval is the current interval + random value. - int this_interval = - current_reconnect_ivl + (generate_random () % options.reconnect_ivl); - - // Only change the current reconnect interval if the maximum reconnect - // interval was set and if it's larger than the reconnect interval. - if (options.reconnect_ivl_max > 0 - && options.reconnect_ivl_max > options.reconnect_ivl) { - // Calculate the next interval - current_reconnect_ivl = current_reconnect_ivl * 2; - if (current_reconnect_ivl >= options.reconnect_ivl_max) { - current_reconnect_ivl = options.reconnect_ivl_max; - } - } - return this_interval; -} - int zmq::vmci_connecter_t::open () { - zmq_assert (s == retired_fd); + zmq_assert (_s == retired_fd); - int family = this->get_ctx ()->get_vmci_socket_family (); - if (family == -1) - return -1; + // Resolve the address + if (_addr->resolved.vmci_addr != NULL) { + LIBZMQ_DELETE (_addr->resolved.vmci_addr); + } - // Create the socket. - s = open_socket (family, SOCK_STREAM, 0); -#ifdef ZMQ_HAVE_WINDOWS - if (s == INVALID_SOCKET) { - errno = wsa_error_to_errno (WSAGetLastError ()); + _addr->resolved.vmci_addr = + new (std::nothrow) vmci_address_t (this->get_ctx ()); + alloc_assert (_addr->resolved.vmci_addr); + _s = vmci_open_socket (_addr->address.c_str (), options, + _addr->resolved.vmci_addr); + if (_s == retired_fd) { + // TODO we should emit some event in this case! + + LIBZMQ_DELETE (_addr->resolved.vmci_addr); return -1; } -#else - if (s == -1) - return -1; -#endif + zmq_assert (_addr->resolved.vmci_addr != NULL); + + // Set the socket to non-blocking mode so that we get async connect(). + unblock_socket (_s); + + const vmci_address_t *const vmci_addr = _addr->resolved.vmci_addr; + + int rc; // Connect to the remote peer. - int rc = ::connect (s, addr->resolved.vmci_addr->addr (), - addr->resolved.vmci_addr->addrlen ()); - - // Connect was successful immediately. - if (rc == 0) - return 0; - - // Forward the error. - return -1; -} - -void zmq::vmci_connecter_t::close () -{ - zmq_assert (s != retired_fd); -#ifdef ZMQ_HAVE_WINDOWS - const int rc = closesocket (s); - wsa_assert (rc != SOCKET_ERROR); +#if defined ZMQ_HAVE_VXWORKS + rc = ::connect (_s, (sockaddr *) vmci_addr->addr (), vmci_addr->addrlen ()); #else - const int rc = ::close (s); - errno_assert (rc == 0); + rc = ::connect (_s, vmci_addr->addr (), vmci_addr->addrlen ()); #endif - socket->event_closed (make_unconnected_bind_endpoint_pair (endpoint), s); - s = retired_fd; + // Connect was successful immediately. + if (rc == 0) { + return 0; + } + + // Translate error codes indicating asynchronous connect has been + // launched to a uniform EINPROGRESS. +#ifdef ZMQ_HAVE_WINDOWS + const int last_error = WSAGetLastError (); + if (last_error == WSAEINPROGRESS || last_error == WSAEWOULDBLOCK) + errno = EINPROGRESS; + else + errno = wsa_error_to_errno (last_error); +#else + if (errno == EINTR) + errno = EINPROGRESS; +#endif + return -1; } zmq::fd_t zmq::vmci_connecter_t::connect () { - // Following code should handle both Berkeley-derived socket - // implementations and Solaris. + // Async connect has finished. Check whether an error occurred int err = 0; -#if defined ZMQ_HAVE_HPUX - int len = sizeof (err); +#if defined ZMQ_HAVE_HPUX || defined ZMQ_HAVE_VXWORKS + int len = sizeof err; #else - socklen_t len = sizeof (err); + socklen_t len = sizeof err; #endif - int rc = getsockopt (s, SOL_SOCKET, SO_ERROR, (char *) &err, &len); + + const int rc = getsockopt (_s, SOL_SOCKET, SO_ERROR, + reinterpret_cast (&err), &len); // Assert if the error was caused by 0MQ bug. // Networking problems are OK. No need to assert. #ifdef ZMQ_HAVE_WINDOWS zmq_assert (rc == 0); if (err != 0) { - if (err != WSAECONNREFUSED && err != WSAETIMEDOUT - && err != WSAECONNABORTED && err != WSAEHOSTUNREACH - && err != WSAENETUNREACH && err != WSAENETDOWN && err != WSAEACCES - && err != WSAEINVAL && err != WSAEADDRINUSE - && err != WSAECONNRESET) { + if (err == WSAEBADF || err == WSAENOPROTOOPT || err == WSAENOTSOCK + || err == WSAENOBUFS) { wsa_assert_no (err); } + errno = wsa_error_to_errno (err); return retired_fd; } #else @@ -286,16 +281,20 @@ zmq::fd_t zmq::vmci_connecter_t::connect () err = errno; if (err != 0) { errno = err; - errno_assert (errno == ECONNREFUSED || errno == ECONNRESET - || errno == ETIMEDOUT || errno == EHOSTUNREACH - || errno == ENETUNREACH || errno == ENETDOWN - || errno == EINVAL); +#if !defined(TARGET_OS_IPHONE) || !TARGET_OS_IPHONE + errno_assert (errno != EBADF && errno != ENOPROTOOPT + && errno != ENOTSOCK && errno != ENOBUFS); +#else + errno_assert (errno != ENOPROTOOPT && errno != ENOTSOCK + && errno != ENOBUFS); +#endif return retired_fd; } #endif - fd_t result = s; - s = retired_fd; + // Return the newly connected socket. + const fd_t result = _s; + _s = retired_fd; return result; } diff --git a/vendor/ZMQ/src/vmci_connecter.hpp b/vendor/ZMQ/src/vmci_connecter.hpp index d0ebaac9..64a176a9 100644 --- a/vendor/ZMQ/src/vmci_connecter.hpp +++ b/vendor/ZMQ/src/vmci_connecter.hpp @@ -38,6 +38,7 @@ #include "own.hpp" #include "stdint.hpp" #include "io_object.hpp" +#include "stream_connecter_base.hpp" namespace zmq { @@ -45,8 +46,7 @@ class io_thread_t; class session_base_t; struct address_t; -// TODO consider refactoring this to derive from stream_connecter_base_t -class vmci_connecter_t ZMQ_FINAL : public own_t, public io_object_t +class vmci_connecter_t ZMQ_FINAL : public stream_connecter_base_t { public: // If 'delayed_start' is true connecter first waits for a while, @@ -54,19 +54,21 @@ class vmci_connecter_t ZMQ_FINAL : public own_t, public io_object_t vmci_connecter_t (zmq::io_thread_t *io_thread_, zmq::session_base_t *session_, const options_t &options_, - const address_t *addr_, + address_t *addr_, bool delayed_start_); ~vmci_connecter_t (); + protected: + std::string get_socket_name (fd_t fd_, socket_end_t socket_end_) const; + private: - // ID of the timer used to delay the reconnection. + // ID of the timer used to check the connect timeout, must be different from stream_connecter_base_t::reconnect_timer_id. enum { - reconnect_timer_id = 1 + connect_timer_id = 2 }; // Handlers for incoming commands. - void process_plug (); void process_term (int linger_); // Handlers for I/O events. @@ -77,8 +79,8 @@ class vmci_connecter_t ZMQ_FINAL : public own_t, public io_object_t // Internal function to start the actual connection establishment. void start_connecting (); - // Internal function to add a reconnect timer - void add_reconnect_timer (); + // Internal function to add a connect timer + void add_connect_timer (); // Internal function to return a reconnect backoff delay. // Will modify the current_reconnect_ivl used for next call @@ -90,43 +92,12 @@ class vmci_connecter_t ZMQ_FINAL : public own_t, public io_object_t // EAGAIN errno if async connect was launched. int open (); - // Close the connecting socket. - void close (); - // Get the file descriptor of newly created connection. Returns // retired_fd if the connection was unsuccessful. fd_t connect (); - // Address to connect to. Owned by session_base_t. - const address_t *addr; - - // Underlying socket. - fd_t s; - - // Handle corresponding to the listening socket. - handle_t handle; - - // If true file descriptor is registered with the poller and 'handle' - // contains valid value. - bool handle_valid; - - // If true, connecter is waiting a while before trying to connect. - const bool delayed_start; - // True iff a timer has been started. - bool timer_started; - - // Reference to the session we belong to. - zmq::session_base_t *session; - - // Current reconnect ivl, updated for backoff strategy - int current_reconnect_ivl; - - // String representation of endpoint to connect to - std::string endpoint; - - // Socket - zmq::socket_base_t *socket; + bool _connect_timer_started; ZMQ_NON_COPYABLE_NOR_MOVABLE (vmci_connecter_t) }; diff --git a/vendor/ZMQ/src/vmci_listener.cpp b/vendor/ZMQ/src/vmci_listener.cpp index 0ac6db34..c6b10dfb 100644 --- a/vendor/ZMQ/src/vmci_listener.cpp +++ b/vendor/ZMQ/src/vmci_listener.cpp @@ -35,7 +35,7 @@ #include -#include "stream_engine.hpp" +//#include "stream_engine.hpp" #include "vmci_address.hpp" #include "io_thread.hpp" #include "session_base.hpp" @@ -55,40 +55,18 @@ zmq::vmci_listener_t::vmci_listener_t (io_thread_t *io_thread_, socket_base_t *socket_, const options_t &options_) : - own_t (io_thread_, options_), - io_object_t (io_thread_), - s (retired_fd), - socket (socket_) + stream_listener_base_t (io_thread_, socket_, options_) { } -zmq::vmci_listener_t::~vmci_listener_t () -{ - zmq_assert (s == retired_fd); -} - -void zmq::vmci_listener_t::process_plug () -{ - // Start polling for incoming connections. - handle = add_fd (s); - set_pollin (handle); -} - -void zmq::vmci_listener_t::process_term (int linger_) -{ - rm_fd (handle); - close (); - own_t::process_term (linger_); -} - void zmq::vmci_listener_t::in_event () { fd_t fd = accept (); // If connection was reset by the peer in the meantime, just ignore it. if (fd == retired_fd) { - socket->event_accept_failed ( - make_unconnected_bind_endpoint_pair (endpoint), zmq_errno ()); + _socket->event_accept_failed ( + make_unconnected_bind_endpoint_pair (_endpoint), zmq_errno ()); return; } @@ -107,41 +85,24 @@ void zmq::vmci_listener_t::in_event () } // Create the engine object for this connection. - stream_engine_t *engine = new (std::nothrow) stream_engine_t ( - fd, options, make_unconnected_bind_endpoint_pair (endpoint)); - alloc_assert (engine); - - // Choose I/O thread to run connecter in. Given that we are already - // running in an I/O thread, there must be at least one available. - io_thread_t *io_thread = choose_io_thread (options.affinity); - zmq_assert (io_thread); - - // Create and launch a session object. - session_base_t *session = - session_base_t::create (io_thread, false, socket, options, NULL); - errno_assert (session); - session->inc_seqnum (); - launch_child (session); - send_attach (session, engine, false); - socket->event_accepted (make_unconnected_bind_endpoint_pair (endpoint), fd); + create_engine (fd); } -int zmq::vmci_listener_t::get_local_address (std::string &addr_) +std::string +zmq::vmci_listener_t::get_socket_name (zmq::fd_t fd_, + socket_end_t socket_end_) const { struct sockaddr_storage ss; -#ifdef ZMQ_HAVE_HPUX - int sl = sizeof (ss); -#else - socklen_t sl = sizeof (ss); -#endif - int rc = getsockname (s, (sockaddr *) &ss, &sl); - if (rc != 0) { - addr_.clear (); - return rc; + const zmq_socklen_t sl = get_socket_address (fd_, socket_end_, &ss); + if (sl == 0) { + return std::string (); } - vmci_address_t addr ((struct sockaddr *) &ss, sl, this->get_ctx ()); - return addr.to_string (addr_); + const vmci_address_t addr (reinterpret_cast (&ss), sl, + this->get_ctx ()); + std::string address_string; + addr.to_string (address_string); + return address_string; } int zmq::vmci_listener_t::set_local_address (const char *addr_) @@ -156,7 +117,7 @@ int zmq::vmci_listener_t::set_local_address (const char *addr_) return -1; // Create a listening socket. - s = + _s = open_socket (this->get_ctx ()->get_vmci_socket_family (), SOCK_STREAM, 0); #ifdef ZMQ_HAVE_WINDOWS if (s == INVALID_SOCKET) { @@ -165,18 +126,18 @@ int zmq::vmci_listener_t::set_local_address (const char *addr_) } #if !defined _WIN32_WCE // On Windows, preventing sockets to be inherited by child processes. - BOOL brc = SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0); + BOOL brc = SetHandleInformation ((HANDLE) _s, HANDLE_FLAG_INHERIT, 0); win_assert (brc); #endif #else - if (s == -1) + if (_s == -1) return -1; #endif - address.to_string (endpoint); + address.to_string (_endpoint); // Bind the socket. - rc = bind (s, address.addr (), address.addrlen ()); + rc = bind (_s, address.addr (), address.addrlen ()); #ifdef ZMQ_HAVE_WINDOWS if (rc == SOCKET_ERROR) { errno = wsa_error_to_errno (WSAGetLastError ()); @@ -188,7 +149,7 @@ int zmq::vmci_listener_t::set_local_address (const char *addr_) #endif // Listen for incoming connections. - rc = listen (s, options.backlog); + rc = listen (_s, options.backlog); #ifdef ZMQ_HAVE_WINDOWS if (rc == SOCKET_ERROR) { errno = wsa_error_to_errno (WSAGetLastError ()); @@ -199,7 +160,8 @@ int zmq::vmci_listener_t::set_local_address (const char *addr_) goto error; #endif - socket->event_listening (make_unconnected_bind_endpoint_pair (endpoint), s); + _socket->event_listening (make_unconnected_bind_endpoint_pair (_endpoint), + _s); return 0; error: @@ -209,27 +171,13 @@ error: return -1; } -void zmq::vmci_listener_t::close () -{ - zmq_assert (s != retired_fd); -#ifdef ZMQ_HAVE_WINDOWS - int rc = closesocket (s); - wsa_assert (rc != SOCKET_ERROR); -#else - int rc = ::close (s); - errno_assert (rc == 0); -#endif - socket->event_closed (make_unconnected_bind_endpoint_pair (endpoint), s); - s = retired_fd; -} - zmq::fd_t zmq::vmci_listener_t::accept () { // Accept one connection and deal with different failure modes. // The situation where connection cannot be accepted due to insufficient // resources is considered valid and treated by ignoring the connection. - zmq_assert (s != retired_fd); - fd_t sock = ::accept (s, NULL, NULL); + zmq_assert (_s != retired_fd); + fd_t sock = ::accept (_s, NULL, NULL); #ifdef ZMQ_HAVE_WINDOWS if (sock == INVALID_SOCKET) { diff --git a/vendor/ZMQ/src/vmci_listener.hpp b/vendor/ZMQ/src/vmci_listener.hpp index 4ab11842..abce0793 100644 --- a/vendor/ZMQ/src/vmci_listener.hpp +++ b/vendor/ZMQ/src/vmci_listener.hpp @@ -37,57 +37,37 @@ #include #include "fd.hpp" -#include "own.hpp" -#include "stdint.hpp" -#include "io_object.hpp" +#include "vmci_address.hpp" +#include "stream_listener_base.hpp" namespace zmq { -class io_thread_t; -class socket_base_t; - -// TODO consider refactoring this to derive from stream_listener_base_t -class vmci_listener_t ZMQ_FINAL : public own_t, public io_object_t +class vmci_listener_t ZMQ_FINAL : public stream_listener_base_t { public: vmci_listener_t (zmq::io_thread_t *io_thread_, zmq::socket_base_t *socket_, const options_t &options_); - ~vmci_listener_t (); // Set address to listen on. int set_local_address (const char *addr_); - // Get the bound address for use with wildcards - int get_local_address (std::string &addr_); + protected: + std::string get_socket_name (fd_t fd_, socket_end_t socket_end_) const; private: - // Handlers for incoming commands. - void process_plug (); - void process_term (int linger_); - // Handlers for I/O events. void in_event (); - // Close the listening socket. - void close (); - // Accept the new connection. Returns the file descriptor of the // newly created connection. The function may return retired_fd // if the connection was dropped while waiting in the listen backlog. fd_t accept (); - // Underlying socket. - fd_t s; + int create_socket (const char *addr_); - // Handle corresponding to the listening socket. - handle_t handle; - - // Socket the listerner belongs to. - zmq::socket_base_t *socket; - - // String representation of endpoint to bind to - std::string endpoint; + // Address to listen on. + vmci_address_t _address; ZMQ_NON_COPYABLE_NOR_MOVABLE (vmci_listener_t) }; diff --git a/vendor/ZMQ/src/ws_engine.cpp b/vendor/ZMQ/src/ws_engine.cpp index cb5510e1..d9debddf 100644 --- a/vendor/ZMQ/src/ws_engine.cpp +++ b/vendor/ZMQ/src/ws_engine.cpp @@ -453,20 +453,20 @@ bool zmq::ws_engine_t::server_handshake () if (strcasecmp ("upgrade", _header_name) == 0) _header_upgrade_websocket = strcasecmp ("websocket", _header_value) == 0; - else if (strcasecmp ("connection", _header_name) == 0){ - char *element = strtok (_header_value, ","); - while (element != NULL){ + else if (strcasecmp ("connection", _header_name) == 0) { + char *rest = NULL; + char *element = strtok_r (_header_value, ",", &rest); + while (element != NULL) { while (*element == ' ') element++; - if (strcasecmp ("upgrade", element) == 0){ + if (strcasecmp ("upgrade", element) == 0) { _header_connection_upgrade = true; break; } - element = strtok (NULL, ","); + element = strtok_r (NULL, ",", &rest); } - } - else if (strcasecmp ("Sec-WebSocket-Key", _header_name) - == 0) + } else if (strcasecmp ("Sec-WebSocket-Key", _header_name) + == 0) strcpy_s (_websocket_key, _header_value); else if (strcasecmp ("Sec-WebSocket-Protocol", _header_name) == 0) { @@ -474,7 +474,7 @@ bool zmq::ws_engine_t::server_handshake () // Sec-WebSocket-Protocol can appear multiple times or be a comma separated list // if _websocket_protocol is already set we skip the check if (_websocket_protocol[0] == '\0') { - char *rest = 0; + char *rest = NULL; char *p = strtok_r (_header_value, ",", &rest); while (p != NULL) { if (*p == ' ') diff --git a/vendor/ZMQ/src/xpub.cpp b/vendor/ZMQ/src/xpub.cpp index e806b8e4..a71543a3 100644 --- a/vendor/ZMQ/src/xpub.cpp +++ b/vendor/ZMQ/src/xpub.cpp @@ -272,6 +272,12 @@ void zmq::xpub_t::xpipe_terminated (pipe_t *pipe_) // care of by the manual call above. subscriptions is the real mtrie, // so the pipe must be removed from there or it will be left over. _subscriptions.rm (pipe_, stub, static_cast (NULL), false); + + // In case the pipe is currently set as last we must clear it to prevent + // subscriptions from being re-added. + if (pipe_ == _last_pipe) { + _last_pipe = NULL; + } } else { // Remove the pipe from the trie. If there are topics that nobody // is interested in anymore, send corresponding unsubscriptions @@ -348,6 +354,12 @@ int zmq::xpub_t::xrecv (msg_t *msg_) if (_manual && !_pending_pipes.empty ()) { _last_pipe = _pending_pipes.front (); _pending_pipes.pop_front (); + + // If the distributor doesn't know about this pipe it must have already + // been terminated and thus we can't allow manual subscriptions. + if (_last_pipe != NULL && !_dist.has_pipe (_last_pipe)) { + _last_pipe = NULL; + } } int rc = msg_->close (); diff --git a/vendor/ZMQ/src/yqueue.hpp b/vendor/ZMQ/src/yqueue.hpp index 6ba634a3..0780ea7a 100644 --- a/vendor/ZMQ/src/yqueue.hpp +++ b/vendor/ZMQ/src/yqueue.hpp @@ -35,6 +35,7 @@ #include "err.hpp" #include "atomic_ptr.hpp" +#include "platform.hpp" namespace zmq { @@ -50,7 +51,7 @@ namespace zmq // T is the type of the object in the queue. // N is granularity of the queue (how many pushes have to be done till // actual memory allocation is required). -#ifdef HAVE_POSIX_MEMALIGN +#if defined HAVE_POSIX_MEMALIGN // ALIGN is the memory alignment size to use in the case where we have // posix_memalign available. Default value is 64, this alignment will // prevent two queue chunks from occupying the same CPU cache line on @@ -181,7 +182,7 @@ template class yqueue_t static inline chunk_t *allocate_chunk () { -#ifdef HAVE_POSIX_MEMALIGN +#if defined HAVE_POSIX_MEMALIGN void *pv; if (posix_memalign (&pv, ALIGN, sizeof (chunk_t)) == 0) return (chunk_t *) pv; diff --git a/vendor/ZMQ/src/zmq_draft.h b/vendor/ZMQ/src/zmq_draft.h index b384d035..59be7932 100644 --- a/vendor/ZMQ/src/zmq_draft.h +++ b/vendor/ZMQ/src/zmq_draft.h @@ -69,11 +69,13 @@ #define ZMQ_HELLO_MSG 110 #define ZMQ_DISCONNECT_MSG 111 #define ZMQ_PRIORITY 112 +#define ZMQ_BUSY_POLL 113 +#define ZMQ_HICCUP_MSG 114 /* DRAFT ZMQ_RECONNECT_STOP options */ #define ZMQ_RECONNECT_STOP_CONN_REFUSED 0x1 #define ZMQ_RECONNECT_STOP_HANDSHAKE_FAILED 0x2 -#define ZMQ_RECONNECT_STOP_AFTER_DISCONNECT 0x3 +#define ZMQ_RECONNECT_STOP_AFTER_DISCONNECT 0x4 /* DRAFT Context options */ #define ZMQ_ZERO_COPY_RECV 10 diff --git a/vendor/ZMQ/tests/CMakeLists.txt b/vendor/ZMQ/tests/CMakeLists.txt index 08056143..77b4e37b 100644 --- a/vendor/ZMQ/tests/CMakeLists.txt +++ b/vendor/ZMQ/tests/CMakeLists.txt @@ -161,7 +161,11 @@ if(ENABLE_DRAFTS) test_channel test_hello_msg test_disconnect_msg + test_hiccup_msg ) + if(ZMQ_HAVE_BUSY_POLL) + list(APPEND tests test_busy_poll) + endif() endif() if(ZMQ_HAVE_WS) diff --git a/vendor/ZMQ/tests/test_busy_poll.cpp b/vendor/ZMQ/tests/test_busy_poll.cpp new file mode 100644 index 00000000..e0405542 --- /dev/null +++ b/vendor/ZMQ/tests/test_busy_poll.cpp @@ -0,0 +1,58 @@ +/* + Copyright (c) 2007-2021 Contributors as noted in the AUTHORS file + + This file is part of libzmq, the ZeroMQ core engine in C++. + + libzmq is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + As a special exception, the Contributors give you permission to link + this library with independent modules to produce an executable, + regardless of the license terms of these independent modules, and to + copy and distribute the resulting executable under terms of your choice, + provided that you also meet, for each linked independent module, the + terms and conditions of the license of that module. An independent + module is a module which is not derived from or based on this library. + If you modify this library, you must extend this exception to your + version of the library. + + libzmq is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "testutil.hpp" +#include "testutil_unity.hpp" + +SETUP_TEARDOWN_TESTCONTEXT + +void test_busy_poll () +{ + // Create a socket + void *socket = test_context_socket (ZMQ_DEALER); + + // set socket ZMQ_BUSY_POLL options + int busy_poll = 1; + TEST_ASSERT_SUCCESS_ERRNO ( + zmq_setsockopt (socket, ZMQ_BUSY_POLL, &busy_poll, sizeof (int))); + + // bind socket + TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (socket, "tcp://127.0.0.1:*")); + + // Clean up. + test_context_socket_close (socket); +} + +int main () +{ + setup_test_environment (); + UNITY_BEGIN (); + RUN_TEST (test_busy_poll); + return UNITY_END (); +} diff --git a/vendor/ZMQ/tests/test_hiccup_msg.cpp b/vendor/ZMQ/tests/test_hiccup_msg.cpp new file mode 100644 index 00000000..c7784b20 --- /dev/null +++ b/vendor/ZMQ/tests/test_hiccup_msg.cpp @@ -0,0 +1,76 @@ +/* + Copyright (c) 2007-2021 Contributors as noted in the AUTHORS file + + This file is part of libzmq, the ZeroMQ core engine in C++. + + libzmq is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + As a special exception, the Contributors give you permission to link + this library with independent modules to produce an executable, + regardless of the license terms of these independent modules, and to + copy and distribute the resulting executable under terms of your choice, + provided that you also meet, for each linked independent module, the + terms and conditions of the license of that module. An independent + module is a module which is not derived from or based on this library. + If you modify this library, you must extend this exception to your + version of the library. + + libzmq is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#include "testutil.hpp" +#include "testutil_unity.hpp" + +SETUP_TEARDOWN_TESTCONTEXT + +void test () +{ + char address[MAX_SOCKET_STRING]; + size_t addr_length = sizeof (address); + + // Create a server + void *server = test_context_socket (ZMQ_SERVER); + + // bind server + TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (server, "tcp://127.0.0.1:*")); + TEST_ASSERT_SUCCESS_ERRNO ( + zmq_getsockopt (server, ZMQ_LAST_ENDPOINT, address, &addr_length)); + + // Create a client + void *client = test_context_socket (ZMQ_CLIENT); + TEST_ASSERT_SUCCESS_ERRNO ( + zmq_setsockopt (client, ZMQ_HELLO_MSG, "HELLO", 5)); + TEST_ASSERT_SUCCESS_ERRNO ( + zmq_setsockopt (client, ZMQ_HICCUP_MSG, "HICCUP", 6)); + TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (client, address)); + + // Receive the hello message from client + recv_string_expect_success (server, "HELLO", 0); + + // Kill the server + test_context_socket_close (server); + + // Receive the hiccup message + recv_string_expect_success (client, "HICCUP", 0); + + // Clean up. + test_context_socket_close (client); +} + +int main () +{ + setup_test_environment (); + + UNITY_BEGIN (); + RUN_TEST (test); + return UNITY_END (); +} diff --git a/vendor/ZMQ/tests/test_inproc_connect.cpp b/vendor/ZMQ/tests/test_inproc_connect.cpp index de1923fb..7a3acbda 100644 --- a/vendor/ZMQ/tests/test_inproc_connect.cpp +++ b/vendor/ZMQ/tests/test_inproc_connect.cpp @@ -54,8 +54,9 @@ static void simult_conn (void *endpt_) // Connect // do not use test_context_socket here, as it is not thread-safe - void *connect_socket = zmq_socket (get_test_context (), ZMQ_SUB); + void *connect_socket = zmq_socket (get_test_context (), ZMQ_PAIR); TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (connect_socket, endpt)); + recv_string_expect_success (connect_socket, "foobar", 0); // Cleanup TEST_ASSERT_SUCCESS_ERRNO (zmq_close (connect_socket)); @@ -68,8 +69,9 @@ static void simult_bind (void *endpt_) // Bind // do not use test_context_socket here, as it is not thread-safe - void *bind_socket = zmq_socket (get_test_context (), ZMQ_PUB); + void *bind_socket = zmq_socket (get_test_context (), ZMQ_PAIR); TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (bind_socket, endpt)); + send_string_expect_success (bind_socket, "foobar", 0); // Cleanup TEST_ASSERT_SUCCESS_ERRNO (zmq_close (bind_socket)); diff --git a/vendor/ZMQ/tests/test_pair_vmci.cpp b/vendor/ZMQ/tests/test_pair_vmci.cpp index 721c1ba5..2e20cc0c 100644 --- a/vendor/ZMQ/tests/test_pair_vmci.cpp +++ b/vendor/ZMQ/tests/test_pair_vmci.cpp @@ -48,10 +48,10 @@ void test_pair_vmci () void *sc = test_context_socket (ZMQ_PAIR); TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (sc, endpoint.c_str ())); - bounce (sb, sc); + expect_bounce_fail (sb, sc); - test_context_socket_close (sc); - test_context_socket_close (sb); + test_context_socket_close_zero_linger (sc); + test_context_socket_close_zero_linger (sb); } int main (void) diff --git a/vendor/ZMQ/tests/test_reqrep_vmci.cpp b/vendor/ZMQ/tests/test_reqrep_vmci.cpp index 0672f68d..24dd72b1 100644 --- a/vendor/ZMQ/tests/test_reqrep_vmci.cpp +++ b/vendor/ZMQ/tests/test_reqrep_vmci.cpp @@ -42,16 +42,16 @@ void test_reqrep_vmci () s << "vmci://" << VMCISock_GetLocalCID () << ":" << 5560; std::string endpoint = s.str (); - void *sb = test_context_socket (ZMQ_REP); + void *sb = test_context_socket (ZMQ_DEALER); TEST_ASSERT_SUCCESS_ERRNO (zmq_bind (sb, endpoint.c_str ())); - void *sc = test_context_socket (ZMQ_REQ); + void *sc = test_context_socket (ZMQ_DEALER); TEST_ASSERT_SUCCESS_ERRNO (zmq_connect (sc, endpoint.c_str ())); - bounce (sb, sc); + expect_bounce_fail (sb, sc); - test_context_socket_close (sc); - test_context_socket_close (sb); + test_context_socket_close_zero_linger (sc); + test_context_socket_close_zero_linger (sb); } int main (void) diff --git a/vendor/ZMQ/tests/testutil.hpp b/vendor/ZMQ/tests/testutil.hpp index c6f5e4de..da51c461 100644 --- a/vendor/ZMQ/tests/testutil.hpp +++ b/vendor/ZMQ/tests/testutil.hpp @@ -41,7 +41,12 @@ // For AF_INET and IPPROTO_TCP #if defined _WIN32 #include "../src/windows.hpp" +#if defined(__MINGW32__) +#include +#endif #else +#include +#include #include #include #include