From a047831b5536bc467bba01c1c013085966709afd Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Wed, 4 Mar 2026 10:00:50 -0700 Subject: [PATCH] Fix Drone CI: capy cloning, build warnings, and cmake-mainproject install - Clone capy in drone.sh/drone.bat; disable coverage in .drone.star - Suppress GCC 14 false-positive -Wmaybe-uninitialized in destroy() - Exclude tls_stream_stress from non-TLS builds (CMake filter + separate per-backend B2 targets to avoid no propagation) - Use BOOST_INCLUDE_LIBRARIES in cmake_test for cmake-subdirectory - Fix missing install target: guard on BOOST_COROSIO_IS_ROOT instead of boost_capy_FOUND (never set when capy comes from boost root subdirectory) - Scale test failsafes when running under valgrind - Timer wait fast-path must clear token_ (like the normal path's std::move) so that cancel_at_awaitable's post-completion request_stop() doesn't cause await_resume() to return a spurious canceled Another valgrind fix --- .drone.star | 1 + .drone/drone.bat | 28 +++++++ .drone/drone.sh | 8 ++ cmake/CorosioBuild.cmake | 82 +++++++++++-------- .../boost/corosio/detail/timer_service.hpp | 9 ++ include/boost/corosio/io/io_timer.hpp | 2 + test/cmake_test/CMakeLists.txt | 32 +------- test/unit/CMakeLists.txt | 5 ++ test/unit/Jamfile | 16 +++- test/unit/test_utils.hpp | 24 ++++-- 10 files changed, 134 insertions(+), 73 deletions(-) diff --git a/.drone.star b/.drone.star index bdd9adc2..6827a3c5 100644 --- a/.drone.star +++ b/.drone.star @@ -29,6 +29,7 @@ def main(ctx): ], '>=20', docs=False, + coverage=False, cache_dir='cache') # macOS: generate() skips apple-clang when cxx_range='>=20' because diff --git a/.drone/drone.bat b/.drone/drone.bat index 0b1cbaf0..0b61c2ed 100644 --- a/.drone/drone.bat +++ b/.drone/drone.bat @@ -34,6 +34,13 @@ if "%BOOST_BRANCH%" == "" ( call ci\common_install.bat +REM Clone the capy dependency into the superproject. +SET CAPY_BRANCH=develop +SET CAPY_TARGET=!BOOST_CI_TARGET_BRANCH! +if NOT "!DRONE_TARGET_BRANCH!" == "" SET CAPY_TARGET=!DRONE_TARGET_BRANCH! +if "!CAPY_TARGET!" == "master" SET CAPY_BRANCH=master +git clone -b !CAPY_BRANCH! https://github.com/cppalliance/capy.git !BOOST_ROOT!\libs\capy --depth 1 + echo '==================================> COMPILE' set B2_TARGETS=libs/!SELF!/test @@ -55,6 +62,13 @@ SET BOOST_CI_SRC_FOLDER=%cd% call ci\common_install.bat +REM Clone the capy dependency into the superproject. +SET CAPY_BRANCH=develop +SET CAPY_TARGET=!BOOST_CI_TARGET_BRANCH! +if NOT "!DRONE_TARGET_BRANCH!" == "" SET CAPY_TARGET=!DRONE_TARGET_BRANCH! +if "!CAPY_TARGET!" == "master" SET CAPY_BRANCH=master +git clone -b !CAPY_BRANCH! https://github.com/cppalliance/capy.git !BOOST_ROOT!\libs\capy --depth 1 + echo '==================================> COMPILE' if "!CMAKE_NO_TESTS!" == "" ( @@ -124,6 +138,13 @@ SET BOOST_CI_SRC_FOLDER=%cd% call ci\common_install.bat +REM Clone the capy dependency into the superproject. +SET CAPY_BRANCH=develop +SET CAPY_TARGET=!BOOST_CI_TARGET_BRANCH! +if NOT "!DRONE_TARGET_BRANCH!" == "" SET CAPY_TARGET=!DRONE_TARGET_BRANCH! +if "!CAPY_TARGET!" == "master" SET CAPY_BRANCH=master +git clone -b !CAPY_BRANCH! https://github.com/cppalliance/capy.git !BOOST_ROOT!\libs\capy --depth 1 + echo '==================================> COMPILE' if "!CMAKE_NO_TESTS!" == "" ( @@ -160,6 +181,13 @@ SET BOOST_CI_SRC_FOLDER=%cd% call ci\common_install.bat +REM Clone the capy dependency into the superproject. +SET CAPY_BRANCH=develop +SET CAPY_TARGET=!BOOST_CI_TARGET_BRANCH! +if NOT "!DRONE_TARGET_BRANCH!" == "" SET CAPY_TARGET=!DRONE_TARGET_BRANCH! +if "!CAPY_TARGET!" == "master" SET CAPY_BRANCH=master +git clone -b !CAPY_BRANCH! https://github.com/cppalliance/capy.git !BOOST_ROOT!\libs\capy --depth 1 + echo '==================================> COMPILE' if "!CMAKE_NO_TESTS!" == "" ( diff --git a/.drone/drone.sh b/.drone/drone.sh index 1d358bc8..bd213561 100755 --- a/.drone/drone.sh +++ b/.drone/drone.sh @@ -34,6 +34,14 @@ common_install () { export BOOST_CI_SRC_FOLDER=$(pwd) . ./ci/common_install.sh + + # Clone the capy dependency into the superproject. + CAPY_BRANCH=develop + CAPY_TARGET="${DRONE_TARGET_BRANCH:-$TRAVIS_BRANCH}" + if [ "$CAPY_TARGET" = "master" ]; then + CAPY_BRANCH=master + fi + git clone -b "$CAPY_BRANCH" https://github.com/cppalliance/capy.git "$BOOST_ROOT/libs/capy" --depth 1 } if [[ $(uname) == "Linux" && "$B2_ASAN" == "1" ]]; then diff --git a/cmake/CorosioBuild.cmake b/cmake/CorosioBuild.cmake index ec5d5bf0..98c28e57 100644 --- a/cmake/CorosioBuild.cmake +++ b/cmake/CorosioBuild.cmake @@ -12,8 +12,7 @@ # Resolve all build dependencies: sibling Boost libraries when inside a # boost tree, Capy via find_package / FetchContent, and Threads. # -# Must be a macro so find_package results (e.g. boost_capy_FOUND) propagate -# to the caller's scope for install logic. +# Must be a macro so find_package results propagate to the caller's scope. macro(corosio_resolve_deps) # Sibling Boost libraries when building standalone inside a boost tree. # The Boost::asio reference must stay out of CMakeLists.txt because the @@ -187,9 +186,8 @@ function(corosio_install) TARGETS ${_corosio_install_targets} VERSION ${BOOST_SUPERPROJECT_VERSION} HEADER_DIRECTORY include) - elseif(boost_capy_FOUND) + elseif(BOOST_COROSIO_IS_ROOT) include(GNUInstallDirs) - include(CMakePackageConfigHelpers) # Set INSTALL_INTERFACE for standalone installs (boost_install handles # this for superproject builds, including versioned-layout paths) @@ -198,36 +196,52 @@ function(corosio_install) $) endforeach() - set(BOOST_COROSIO_INSTALL_CMAKEDIR - ${CMAKE_INSTALL_LIBDIR}/cmake/boost_corosio) - - install(TARGETS ${_corosio_install_targets} - EXPORT boost_corosio-targets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - install(DIRECTORY include/ - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - install(EXPORT boost_corosio-targets - NAMESPACE Boost:: - DESTINATION ${BOOST_COROSIO_INSTALL_CMAKEDIR}) - - configure_package_config_file( - cmake/boost_corosio-config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/boost_corosio-config.cmake - INSTALL_DESTINATION ${BOOST_COROSIO_INSTALL_CMAKEDIR}) - write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/boost_corosio-config-version.cmake - COMPATIBILITY SameMajorVersion) - - set(_corosio_config_files - ${CMAKE_CURRENT_BINARY_DIR}/boost_corosio-config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/boost_corosio-config-version.cmake) - if(WolfSSL_FOUND) - list(APPEND _corosio_config_files - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindWolfSSL.cmake) + if(boost_capy_FOUND) + # Capy from find_package (imported target): full install with + # CMake package config and export sets. + include(CMakePackageConfigHelpers) + + set(BOOST_COROSIO_INSTALL_CMAKEDIR + ${CMAKE_INSTALL_LIBDIR}/cmake/boost_corosio) + + install(TARGETS ${_corosio_install_targets} + EXPORT boost_corosio-targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(DIRECTORY include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + install(EXPORT boost_corosio-targets + NAMESPACE Boost:: + DESTINATION ${BOOST_COROSIO_INSTALL_CMAKEDIR}) + + configure_package_config_file( + cmake/boost_corosio-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/boost_corosio-config.cmake + INSTALL_DESTINATION ${BOOST_COROSIO_INSTALL_CMAKEDIR}) + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/boost_corosio-config-version.cmake + COMPATIBILITY SameMajorVersion) + + set(_corosio_config_files + ${CMAKE_CURRENT_BINARY_DIR}/boost_corosio-config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/boost_corosio-config-version.cmake) + if(WolfSSL_FOUND) + list(APPEND _corosio_config_files + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindWolfSSL.cmake) + endif() + install(FILES ${_corosio_config_files} + DESTINATION ${BOOST_COROSIO_INSTALL_CMAKEDIR}) + else() + # Capy from source tree (boost root or FetchContent): export sets + # can't work because capy isn't an imported target. Install the + # library and headers only. + install(TARGETS ${_corosio_install_targets} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(DIRECTORY include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) endif() - install(FILES ${_corosio_config_files} - DESTINATION ${BOOST_COROSIO_INSTALL_CMAKEDIR}) endif() endfunction() diff --git a/include/boost/corosio/detail/timer_service.hpp b/include/boost/corosio/detail/timer_service.hpp index fd061d47..7855f7c6 100644 --- a/include/boost/corosio/detail/timer_service.hpp +++ b/include/boost/corosio/detail/timer_service.hpp @@ -760,6 +760,12 @@ waiter_node::completion_op::operator()() sched.work_finished(); } +// GCC 14 false-positive: inlining ~optional through +// delete loses track that stop_cb_ was already .reset() above. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif inline void waiter_node::completion_op::destroy() { @@ -783,6 +789,9 @@ waiter_node::completion_op::destroy() if (h) h.destroy(); } +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif inline std::coroutine_handle<> timer_service::implementation::wait( diff --git a/include/boost/corosio/io/io_timer.hpp b/include/boost/corosio/io/io_timer.hpp index 4597a4ed..e9f9331e 100644 --- a/include/boost/corosio/io/io_timer.hpp +++ b/include/boost/corosio/io/io_timer.hpp @@ -72,6 +72,8 @@ class BOOST_COROSIO_DECL io_timer : public io_object impl.expiry_ <= clock_type::now())) { ec_ = {}; + token_ = {}; // match normal path so await_resume + // returns ec_, not a stale stop check auto d = env->executor; d.post(h); return std::noop_coroutine(); diff --git a/test/cmake_test/CMakeLists.txt b/test/cmake_test/CMakeLists.txt index 38b4a6fa..54b72927 100644 --- a/test/cmake_test/CMakeLists.txt +++ b/test/cmake_test/CMakeLists.txt @@ -15,36 +15,8 @@ if(BOOST_CI_INSTALL_TEST) find_package(Boost CONFIG REQUIRED COMPONENTS corosio) else() set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - - set(deps - # Low-level dependencies (order matters - these must come first) - assert - config - core - mp11 - predef - static_assert - throw_exception - type_traits - winapi - - # Mid-level dependencies - align - compat - optional - variant2 - - # Primary dependencies (corosio) - system - capy - ) - - foreach(dep IN LISTS deps) - add_subdirectory(../../../${dep} boostorg/${dep} EXCLUDE_FROM_ALL) - endforeach() - - # Add corosio last, after all its dependencies - add_subdirectory(../.. boostorg/corosio) + set(BOOST_INCLUDE_LIBRARIES corosio) + add_subdirectory(../../../.. deps/boost EXCLUDE_FROM_ALL) endif() add_executable(main main.cpp) diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 77fdfe73..ae137e30 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -8,6 +8,11 @@ # file(GLOB_RECURSE PFILES CONFIGURE_DEPENDS *.cpp *.hpp) + +if (NOT OpenSSL_FOUND AND NOT WolfSSL_FOUND) + list(FILTER PFILES EXCLUDE REGEX "tls_stream_stress\\.cpp$") +endif() + list(APPEND PFILES CMakeLists.txt Jamfile) diff --git a/test/unit/Jamfile b/test/unit/Jamfile index 8ff0fe5c..f16ab3cb 100644 --- a/test/unit/Jamfile +++ b/test/unit/Jamfile @@ -20,7 +20,7 @@ project boost/corosio/test/unit ; # Non-TLS tests -for local f in [ glob *.cpp : openssl_stream.cpp wolfssl_stream.cpp cross_ssl_stream.cpp tls_stream.cpp ] [ glob test/*.cpp ] +for local f in [ glob *.cpp : openssl_stream.cpp wolfssl_stream.cpp cross_ssl_stream.cpp tls_stream.cpp tls_stream_stress.cpp ] [ glob test/*.cpp ] { run $(f) ; } @@ -50,3 +50,17 @@ run cross_ssl_stream.cpp # TLS stream base tests (no specific TLS backend required) run tls_stream.cpp ; + +# TLS stress tests - separate targets so each builds when its backend is found +# (linking both would cause no propagation to skip if either is missing) +run tls_stream_stress.cpp + : : : + /boost/corosio//boost_corosio_openssl + : tls_stream_stress_openssl + ; + +run tls_stream_stress.cpp + : : : + /boost/corosio//boost_corosio_wolfssl + : tls_stream_stress_wolfssl + ; diff --git a/test/unit/test_utils.hpp b/test/unit/test_utils.hpp index 29c578b7..848d4ea8 100644 --- a/test/unit/test_utils.hpp +++ b/test/unit/test_utils.hpp @@ -28,6 +28,14 @@ #include #include +// Valgrind slows execution ~10-20x; scale failsafe timeouts to avoid +// false failures when BOOST_NO_STRESS_TEST is defined. +#ifdef BOOST_NO_STRESS_TEST +inline constexpr int failsafe_scale = 20; +#else +inline constexpr int failsafe_scale = 1; +#endif + namespace boost::corosio::test { // @@ -845,7 +853,7 @@ run_tls_test_fail( // Timer to unblock stuck handshakes (failsafe only) timer timeout(ioc); - timeout.expires_after(std::chrono::milliseconds(200)); + timeout.expires_after(std::chrono::milliseconds(200 * failsafe_scale)); // Store lambdas in named variables before invoking - anonymous lambda + immediate // invocation pattern [...](){}() can cause capture corruption with run_async @@ -995,7 +1003,7 @@ run_tls_shutdown_test( // Failsafe timer in case of bugs timer failsafe(ioc); - failsafe.expires_after(std::chrono::milliseconds(200)); + failsafe.expires_after(std::chrono::milliseconds(200 * failsafe_scale)); auto client_shutdown = [&client, &done, &failsafe]() -> capy::task<> { auto [ec] = co_await client.shutdown(); @@ -1110,7 +1118,7 @@ run_tls_truncation_test( // Timeout to prevent deadlock timer timeout(ioc); // IOCP peer-close propagation can be bursty under TLS backends. - timeout.expires_after(std::chrono::milliseconds(750)); + timeout.expires_after(std::chrono::milliseconds(750 * failsafe_scale)); auto client_close = [&s1, &s2]() -> capy::task<> { // Cancel and close underlying socket without TLS shutdown (IOCP needs cancel) @@ -1388,7 +1396,7 @@ run_connection_reset_test( // Timeout protection timer timeout(ioc); - timeout.expires_after(std::chrono::milliseconds(200)); + timeout.expires_after(std::chrono::milliseconds(200 * failsafe_scale)); auto client_task = [&client, &client_failed, &timeout]() -> capy::task<> { auto [ec] = co_await client.handshake( @@ -1471,7 +1479,7 @@ run_stop_token_handshake_test( // Failsafe timeout to prevent infinite hang if cancellation doesn't work // 2000ms allows headroom for CI with coverage instrumentation timer failsafe(ioc); - failsafe.expires_after(std::chrono::milliseconds(2000)); + failsafe.expires_after(std::chrono::milliseconds(2000 * failsafe_scale)); // Client handshake - will be cancelled while waiting for ServerHello auto client_task = [&client, &client_got_error, @@ -1573,7 +1581,7 @@ run_stop_token_read_test( // Failsafe timeout - 2000ms allows headroom for CI with coverage instrumentation timer failsafe(ioc); - failsafe.expires_after(std::chrono::milliseconds(2000)); + failsafe.expires_after(std::chrono::milliseconds(2000 * failsafe_scale)); auto client_read = [&client, &read_got_error, &failsafe]() -> capy::task<> { char buf[32]; @@ -1676,7 +1684,7 @@ run_stop_token_write_test( // Failsafe timeout - 2000ms allows headroom for CI with coverage instrumentation timer failsafe(ioc); - failsafe.expires_after(std::chrono::milliseconds(2000)); + failsafe.expires_after(std::chrono::milliseconds(2000 * failsafe_scale)); auto client_write = [&client, &large_buf, &write_got_error, &failsafe]() -> capy::task<> { @@ -1767,7 +1775,7 @@ run_socket_cancel_test( // Failsafe timeout - 2000ms allows headroom for CI with coverage instrumentation timer failsafe(ioc); - failsafe.expires_after(std::chrono::milliseconds(2000)); + failsafe.expires_after(std::chrono::milliseconds(2000 * failsafe_scale)); // Client starts handshake - will be cancelled auto client_task = [&client, &client_got_error,