Constructing and holding a CancellationBlock object prevents the coroutine that constructed it, as well as the coroutine that created that coroutine and so on from being destructed. StreamBase::write now uses this mechanism to avoid use-after-free when the write is async and the promise is destroyed before it finishes. Other uvco functions might need to be evaluated to see if they would need CancellationBlock. This requires adding a reference counting mechanism to the coroutines, which might add some overhead but hopefully not too much. Since type-erased coroutine handles cannot be used to access the reference counter, every place that uses those had to be patched to use the new CoroutineHandle type, and awaiters patched to be templated for every concrete promise type (which CoroutineHandle uses to extract a pointer to the reference counter). Another new primitive: detach(Promise<void>) allows running a task in the background just like TaskSet, but unlike TaskSet the task keeps itself alive on its own independently, and cleans up when it's done.
371 lines
10 KiB
CMake
371 lines
10 KiB
CMake
cmake_minimum_required(VERSION 3.27)
|
|
project(uv-co)
|
|
|
|
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
|
# This can cause trouble if compiling libraries for system-wide use.
|
|
# Generally you must use the same compiler for application code as was used
|
|
# to build the uvco code.
|
|
if(NOT DEFINED ENABLE_LTO)
|
|
set(ENABLE_LTO 1)
|
|
endif()
|
|
if(NOT DEFINED ENABLE_ASAN)
|
|
set(ENABLE_ASAN 0)
|
|
endif()
|
|
if(NOT DEFINED ENABLE_COVERAGE)
|
|
set(ENABLE_COVERAGE 0)
|
|
endif()
|
|
elseif(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|
if(NOT DEFINED ENABLE_LTO)
|
|
set(ENABLE_LTO 0)
|
|
endif()
|
|
if(NOT DEFINED ENABLE_ASAN)
|
|
set(ENABLE_ASAN 1)
|
|
endif()
|
|
if(NOT DEFINED ENABLE_COVERAGE)
|
|
set(ENABLE_COVERAGE 1)
|
|
endif()
|
|
else()
|
|
if(NOT DEFINED ENABLE_LTO)
|
|
set(ENABLE_LTO 0)
|
|
endif()
|
|
if(NOT DEFINED ENABLE_ASAN)
|
|
set(ENABLE_ASAN 0)
|
|
endif()
|
|
if(NOT DEFINED ENABLE_COVERAGE)
|
|
set(ENABLE_COVERAGE 0)
|
|
endif()
|
|
endif()
|
|
|
|
MESSAGE(STATUS "LTO ${ENABLE_LTO} ASAN ${ENABLE_ASAN} COVERAGE ${ENABLE_COVERAGE}")
|
|
|
|
include(CTest)
|
|
include(CMakePackageConfigHelpers)
|
|
|
|
find_package(PkgConfig)
|
|
pkg_check_modules(FMT REQUIRED fmt)
|
|
pkg_check_modules(UV REQUIRED libuv)
|
|
|
|
# Allow using a custom libuv installation.
|
|
link_directories(BEFORE /usr/local/lib64 /usr/local/lib)
|
|
|
|
find_package(Boost 1.80
|
|
CONFIG
|
|
COMPONENTS
|
|
log
|
|
log_setup
|
|
program_options
|
|
REQUIRED)
|
|
find_package(benchmark)
|
|
find_package(CURL)
|
|
find_package(libpqxx)
|
|
find_package(GTest)
|
|
|
|
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
|
|
set(CMAKE_CXX_STANDARD 23)
|
|
add_compile_options(
|
|
-pipe
|
|
-fno-omit-frame-pointer
|
|
-Wall
|
|
-Wextra
|
|
-Wpedantic
|
|
)
|
|
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
add_compile_options(-fcolor-diagnostics)
|
|
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
|
add_compile_options(-fdiagnostics-color=always)
|
|
endif()
|
|
|
|
if(ENABLE_ASAN)
|
|
add_compile_options(-fsanitize=address,undefined)
|
|
add_link_options(-fsanitize=address,undefined)
|
|
endif()
|
|
|
|
if(ENABLE_COVERAGE)
|
|
add_compile_options(--coverage)
|
|
add_link_options(--coverage -lgcov)
|
|
endif()
|
|
|
|
if (ENABLE_LTO)
|
|
add_compile_options(-flto)
|
|
add_link_options(-flto)
|
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
add_link_options(-fuse-ld=lld)
|
|
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
|
# Works by default, usually.
|
|
endif()
|
|
endif()
|
|
|
|
# Core utilities not relying on uv-co-promise or uv-co-lib.
|
|
add_library(uv-co-base STATIC
|
|
uvco/exception.cc
|
|
uvco/internal/internal_utils.cc
|
|
uvco/loop/loop.cc
|
|
uvco/loop/scheduler.cc
|
|
)
|
|
target_sources(uv-co-base INTERFACE
|
|
FILE_SET HEADERS
|
|
TYPE HEADERS
|
|
FILES
|
|
uvco/exception.h
|
|
uvco/internal/internal_utils.h
|
|
uvco/loop/loop.h
|
|
uvco/loop/scheduler.h
|
|
)
|
|
target_include_directories(uv-co-base PRIVATE ${FMT_INCLUDEDIR} .)
|
|
install(TARGETS uv-co-base EXPORT uvco FILE_SET HEADERS DESTINATION include)
|
|
|
|
add_library(uv-co-promise STATIC
|
|
uvco/promise/multipromise.cc
|
|
uvco/promise/promise.cc
|
|
uvco/promise/promise_core.cc
|
|
)
|
|
target_sources(uv-co-promise INTERFACE
|
|
FILE_SET HEADERS
|
|
TYPE HEADERS
|
|
FILES
|
|
uvco/promise/multipromise.h
|
|
uvco/promise/promise.h
|
|
uvco/promise/promise_core.h
|
|
uvco/promise/promise_refcount.h
|
|
uvco/promise/select.h
|
|
)
|
|
target_link_libraries(uv-co-promise PRIVATE uv-co-base)
|
|
target_include_directories(uv-co-promise PRIVATE .)
|
|
install(TARGETS uv-co-promise EXPORT uvco FILE_SET HEADERS DESTINATION include)
|
|
|
|
add_library(uv-co-lib STATIC
|
|
uvco/async_work.cc
|
|
uvco/channel.cc
|
|
uvco/close.cc
|
|
uvco/combinators.cc
|
|
uvco/fs.cc
|
|
uvco/name_resolution.cc
|
|
uvco/pipe.cc
|
|
uvco/run.cc
|
|
uvco/stream.cc
|
|
uvco/stream_server_base_impl.cc
|
|
uvco/tcp.cc
|
|
uvco/tcp_stream.cc
|
|
uvco/timer.cc
|
|
uvco/udp.cc
|
|
uvco/uds.cc
|
|
uvco/uds_stream.cc
|
|
)
|
|
|
|
target_sources(uv-co-lib INTERFACE
|
|
FILE_SET HEADERS
|
|
TYPE HEADERS
|
|
FILES
|
|
uvco/async_work.h
|
|
uvco/bounded_queue.h
|
|
uvco/cancellation_block.h
|
|
uvco/channel.h
|
|
uvco/close.h
|
|
uvco/combinators.h
|
|
uvco/fs.h
|
|
uvco/name_resolution.h
|
|
uvco/pipe.h
|
|
uvco/run.h
|
|
uvco/stream.h
|
|
uvco/stream_server_base.h
|
|
uvco/stream_server_base_impl.h
|
|
uvco/tcp.h
|
|
uvco/tcp_stream.h
|
|
uvco/timer.h
|
|
uvco/udp.h
|
|
uvco/uds.h
|
|
uvco/uds_stream.h
|
|
)
|
|
|
|
target_link_libraries(uv-co-lib
|
|
PUBLIC
|
|
uv-co-base
|
|
uv-co-promise
|
|
PRIVATE
|
|
${UV_LIBRARIES}
|
|
${FMT_LIBRARIES}
|
|
)
|
|
|
|
target_include_directories(uv-co-lib PRIVATE ${UV_INCLUDEDIR} ${FMT_INCLUDEDIR} .)
|
|
install(TARGETS uv-co-lib EXPORT uvco FILE_SET HEADERS DESTINATION include)
|
|
|
|
# Package configuration
|
|
|
|
install(EXPORT uvco FILE UvcoTargets.cmake DESTINATION lib/cmake/uvco NAMESPACE Uvco:: )
|
|
export(EXPORT uvco FILE UvcoTargets.cmake NAMESPACE Uvco::)
|
|
configure_package_config_file(Config.cmake.in UvcoConfig.cmake INSTALL_DESTINATION lib/cmake/uvco)
|
|
install(FILES
|
|
"${CMAKE_CURRENT_BINARY_DIR}/UvcoConfig.cmake"
|
|
DESTINATION lib/cmake/uvco
|
|
)
|
|
|
|
# Optional integrations
|
|
|
|
if(CURL_FOUND)
|
|
add_library(uv-co-curl STATIC
|
|
uvco/integrations/curl/curl.cc
|
|
)
|
|
target_sources(uv-co-curl INTERFACE
|
|
FILE_SET HEADERS
|
|
TYPE HEADERS
|
|
BASE_DIRS uvco/
|
|
FILES
|
|
uvco/integrations/curl/curl.h
|
|
)
|
|
target_link_libraries(uv-co-curl PUBLIC uv-co-lib CURL::libcurl)
|
|
target_include_directories(uv-co-curl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
|
install(TARGETS uv-co-curl)
|
|
install(TARGETS uv-co-curl FILE_SET HEADERS DESTINATION include/uvco)
|
|
set(GENPKGCONFIG_EXTRA_REQUIRES "${GENPKGCONFIG_EXTRA_REQUIRES}, libcurl")
|
|
set(GENPKGCONFIG_EXTRA_LIBS "${GENPKGCONFIG_EXTRA_LIBS} -luv-co-curl")
|
|
endif()
|
|
|
|
if(libpqxx_FOUND)
|
|
add_library(uv-co-pqxx STATIC
|
|
uvco/integrations/pqxx/pqxx.cc
|
|
)
|
|
target_sources(uv-co-pqxx INTERFACE
|
|
FILE_SET HEADERS
|
|
TYPE HEADERS
|
|
BASE_DIRS uvco/
|
|
FILES
|
|
uvco/integrations/pqxx/pqxx.h
|
|
)
|
|
target_link_libraries(uv-co-pqxx PUBLIC uv-co-lib libpqxx::pqxx)
|
|
target_include_directories(uv-co-pqxx PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
|
install(TARGETS uv-co-pqxx)
|
|
install(TARGETS uv-co-pqxx FILE_SET HEADERS DESTINATION include/uvco)
|
|
set(GENPKGCONFIG_EXTRA_REQUIRES "${GENPKGCONFIG_EXTRA_REQUIRES}, libpqxx")
|
|
set(GENPKGCONFIG_EXTRA_LIBS "${GENPKGCONFIG_EXTRA_LIBS} -luv-co-pqxx")
|
|
endif()
|
|
|
|
# Unit tests
|
|
|
|
if(GTest_FOUND)
|
|
enable_testing()
|
|
|
|
add_library(test-util STATIC test/test_util.cc)
|
|
target_include_directories(test-util PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
target_link_libraries(test-util PRIVATE uv-co-lib)
|
|
|
|
add_library(uv-co-example-http STATIC
|
|
uvco/examples/http_server.cc
|
|
)
|
|
|
|
target_sources(uv-co-example-http INTERFACE
|
|
FILE_SET HEADERS
|
|
TYPE HEADERS
|
|
FILES
|
|
uvco/examples/http_server.h
|
|
)
|
|
|
|
target_link_libraries(uv-co-example-http
|
|
PUBLIC
|
|
uv-co-lib
|
|
)
|
|
|
|
target_include_directories(uv-co-example-http PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
|
|
install(TARGETS uv-co-example-http EXPORT uvco FILE_SET HEADERS DESTINATION include)
|
|
|
|
add_executable(http-server benchmark/http_server.cc)
|
|
target_link_libraries(http-server
|
|
PRIVATE
|
|
uv-co-example-http
|
|
Boost::program_options
|
|
)
|
|
|
|
foreach(TESTCASE IN ITEMS async_work channel combinators fs generator loop name_resolution promise select stream timer udp uds tcp misc)
|
|
add_executable(${TESTCASE}-test test/${TESTCASE}_test.cc)
|
|
target_link_libraries(${TESTCASE}-test PRIVATE uv-co-lib test-util GTest::gtest_main)
|
|
add_test(NAME ${TESTCASE}-test COMMAND ${TESTCASE}-test)
|
|
|
|
if(ENABLE_COVERAGE)
|
|
target_compile_options(${TESTCASE}-test PRIVATE "--coverage")
|
|
target_link_libraries(${TESTCASE}-test PRIVATE gcov)
|
|
endif()
|
|
endforeach()
|
|
|
|
add_executable(http_server-test test/http_server_test.cc)
|
|
target_link_libraries(http_server-test
|
|
PRIVATE
|
|
uv-co-example-http
|
|
test-util
|
|
GTest::gtest_main
|
|
)
|
|
add_test(NAME http_server-test COMMAND http_server-test)
|
|
|
|
if(ENABLE_COVERAGE)
|
|
target_compile_options(http_server-test PRIVATE "--coverage")
|
|
target_link_libraries(http_server-test PRIVATE gcov)
|
|
endif()
|
|
|
|
foreach(TESTBINARY IN ITEMS tcp-broadcaster multicast http10 memcached-impl)
|
|
add_executable(test-${TESTBINARY} test/${TESTBINARY}.exe.cc)
|
|
target_link_libraries(test-${TESTBINARY} PRIVATE uv-co-lib Boost::program_options)
|
|
endforeach()
|
|
|
|
target_link_libraries(test-memcached-impl PRIVATE Boost::log Boost::log_setup)
|
|
|
|
if(CURL_FOUND)
|
|
add_executable(curl-test test/curl_test.cc)
|
|
target_link_libraries(curl-test PRIVATE uv-co-curl test-util GTest::gtest_main)
|
|
add_test(NAME curl-test COMMAND curl-test)
|
|
endif()
|
|
if(libpqxx_FOUND)
|
|
add_executable(pqxx-test test/pqxx_test.cc)
|
|
target_link_libraries(pqxx-test PRIVATE uv-co-pqxx test-util GTest::gtest_main)
|
|
add_test(NAME pqxx-test COMMAND pqxx-test)
|
|
endif()
|
|
|
|
else()
|
|
message(WARNING "GTest not found, skipping tests")
|
|
endif()
|
|
|
|
# Benchmarks
|
|
|
|
if (benchmark_FOUND)
|
|
foreach(BENCHMARKCASE IN ITEMS stream promise)
|
|
add_executable(${BENCHMARKCASE}-benchmark benchmark/${BENCHMARKCASE}_bench.cc)
|
|
target_link_libraries(${BENCHMARKCASE}-benchmark
|
|
PRIVATE
|
|
uv-co-lib
|
|
benchmark::benchmark
|
|
benchmark::benchmark_main
|
|
GTest::gtest
|
|
)
|
|
endforeach()
|
|
else()
|
|
message(WARNING "Google Benchmark not found, skipping benchmarks")
|
|
endif()
|
|
|
|
# Coverage
|
|
|
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
find_program(LLVM_COV_BINARY NAMES llvm-cov llvm-cov-18)
|
|
set(GCOV_BINARY "${LLVM_COV_BINARY} gcov")
|
|
else()
|
|
set(GCOV_BINARY "gcov")
|
|
endif()
|
|
|
|
# Pkg-config
|
|
|
|
configure_file(uvco.pc.in
|
|
uvco.pc @ONLY)
|
|
|
|
install(
|
|
FILES ${CMAKE_CURRENT_BINARY_DIR}/uvco.pc
|
|
DESTINATION lib/pkgconfig
|
|
)
|
|
|
|
add_custom_target(coverage
|
|
COMMAND mkdir -p coverage
|
|
COMMAND gcovr --html-nested -o coverage/uvco.html --gcov-executable ${GCOV_BINARY} -r .. --merge-mode-functions=separate
|
|
COMMAND find . -name *.gcda -delete
|
|
)
|
|
add_custom_target(grcov
|
|
COMMAND mkdir -p grcov
|
|
COMMAND grcov . -s .. --binary-path . -t html --branch --ignore-not-existing -o grcov --llvm
|
|
COMMAND find . -name *.gcda -delete
|
|
)
|