uvco/CMakeLists.txt
Kimapr 4d00b8bfe1
Some checks failed
Actions CI + Docs / uvco (clang++) (push) Has been cancelled
Actions CI + Docs / uvco (g++-14) (push) Has been cancelled
Allow coroutines to inhibit their own destruction
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.
2026-03-19 14:56:18 +05:00

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
)