From b0e53c19c31cf6b92d341d9271dd66426e09f7e4 Mon Sep 17 00:00:00 2001 From: Zifeng Deng Date: Sun, 31 Dec 2023 22:14:39 +0800 Subject: [PATCH] build(all): extern liburingcxx; use configure, no `-D` --- .gitignore | 2 +- CMakeLists.txt | 4 + cmake/CompileOption.cmake | 15 +- cmake/Configure.cmake | 5 + cmake/Install.cmake | 4 - cmake/Platform.cmake | 13 - cmake/check/check_compile.cmake | 17 +- cmake/templates/Config.cmake.in | 12 - example/iota.cpp | 4 +- extern/liburingcxx/.clang-format | 99 ++++ extern/liburingcxx/.gitignore | 72 +++ extern/liburingcxx/CMakeLists.txt | 39 ++ extern/liburingcxx/LICENSE | 201 +++++++ extern/liburingcxx/README.md | 21 + extern/liburingcxx/cmake/configure.cmake | 45 ++ extern/liburingcxx/cmake/install.cmake | 44 ++ extern/liburingcxx/cmake/option.cmake | 15 + .../cmake/template/Config.cmake.in | 7 + .../cmake/template/cmake_uninstall.cmake.in | 28 + extern/liburingcxx/example/CMakeLists.txt | 4 + extern/liburingcxx/example/cat.cpp | 124 ++++ extern/liburingcxx/example/httpd.cpp | 535 ++++++++++++++++++ extern/liburingcxx/format_count.sh | 6 + extern/liburingcxx/include/uring/.gitignore | 1 + .../include}/uring/arch/aarch64/lib.h | 0 .../include}/uring/arch/aarch64/syscall.h | 0 .../include}/uring/arch/generic/lib.h | 0 .../include}/uring/arch/generic/syscall.h | 0 .../include}/uring/arch/syscall-defs.h | 0 .../liburingcxx/include}/uring/arch/x86/lib.h | 0 .../include}/uring/arch/x86/syscall.h | 0 .../liburingcxx/include}/uring/barrier.h | 0 .../liburingcxx/include}/uring/buf_ring.hpp | 0 .../liburingcxx/include}/uring/compat.hpp | 14 +- .../liburingcxx/include/uring/config.hpp.in | 12 + .../liburingcxx/include}/uring/cq_entry.hpp | 0 .../liburingcxx/include}/uring/detail/cq.hpp | 0 .../include}/uring/detail/int_flags.h | 0 .../liburingcxx/include}/uring/detail/sq.hpp | 0 .../liburingcxx/include}/uring/io_uring.h | 0 .../liburingcxx/include}/uring/sq_entry.hpp | 9 +- .../liburingcxx/include}/uring/syscall.hpp | 0 .../liburingcxx/include}/uring/uring.hpp | 0 .../include}/uring/uring_define.hpp | 0 .../include}/uring/utility/io_helper.hpp | 0 .../include}/uring/utility/kernel_version.hpp | 6 +- extern/liburingcxx/test/CMakeLists.txt | 3 + extern/liburingcxx/test/type_check.cpp | 56 ++ include/co_context/all.hpp | 2 +- include/co_context/co/when_all.hpp | 5 +- include/co_context/config.hpp | 148 ----- include/co_context/config/.gitignore | 1 + include/co_context/config/config.hpp.in | 8 + include/co_context/config/io_context.hpp | 80 +++ include/co_context/config/log.hpp | 22 + include/co_context/config/net.hpp | 7 + include/co_context/config/uring.hpp | 41 ++ include/co_context/detail/io_context_meta.hpp | 2 +- include/co_context/detail/lazy_io_awaiter.hpp | 55 +- include/co_context/detail/spinlock.hpp | 2 +- include/co_context/detail/spsc_cursor.hpp | 1 - include/co_context/detail/task_info.hpp | 2 +- include/co_context/detail/thread_meta.hpp | 2 +- include/co_context/detail/uring_type.hpp | 2 +- include/co_context/detail/worker_meta.hpp | 2 +- include/co_context/generator.hpp | 4 +- include/co_context/io_context.hpp | 2 +- include/co_context/log/log.hpp | 2 +- include/co_context/net/inet_address.hpp | 1 - include/co_context/task.hpp | 4 +- include/co_context/utility/time_cast.hpp | 2 +- lib/co_context/detail/worker_meta.cpp | 2 +- lib/co_context/io_context.cpp | 4 +- lib/co_context/net/inet_address.cpp | 1 + test/generator_test.cpp | 7 +- 75 files changed, 1549 insertions(+), 279 deletions(-) create mode 100644 cmake/Configure.cmake create mode 100755 extern/liburingcxx/.clang-format create mode 100755 extern/liburingcxx/.gitignore create mode 100644 extern/liburingcxx/CMakeLists.txt create mode 100644 extern/liburingcxx/LICENSE create mode 100644 extern/liburingcxx/README.md create mode 100644 extern/liburingcxx/cmake/configure.cmake create mode 100644 extern/liburingcxx/cmake/install.cmake create mode 100644 extern/liburingcxx/cmake/option.cmake create mode 100644 extern/liburingcxx/cmake/template/Config.cmake.in create mode 100644 extern/liburingcxx/cmake/template/cmake_uninstall.cmake.in create mode 100755 extern/liburingcxx/example/CMakeLists.txt create mode 100644 extern/liburingcxx/example/cat.cpp create mode 100644 extern/liburingcxx/example/httpd.cpp create mode 100755 extern/liburingcxx/format_count.sh create mode 100644 extern/liburingcxx/include/uring/.gitignore rename {include => extern/liburingcxx/include}/uring/arch/aarch64/lib.h (100%) rename {include => extern/liburingcxx/include}/uring/arch/aarch64/syscall.h (100%) rename {include => extern/liburingcxx/include}/uring/arch/generic/lib.h (100%) rename {include => extern/liburingcxx/include}/uring/arch/generic/syscall.h (100%) rename {include => extern/liburingcxx/include}/uring/arch/syscall-defs.h (100%) rename {include => extern/liburingcxx/include}/uring/arch/x86/lib.h (100%) rename {include => extern/liburingcxx/include}/uring/arch/x86/syscall.h (100%) rename {include => extern/liburingcxx/include}/uring/barrier.h (100%) rename {include => extern/liburingcxx/include}/uring/buf_ring.hpp (100%) rename {include => extern/liburingcxx/include}/uring/compat.hpp (67%) create mode 100644 extern/liburingcxx/include/uring/config.hpp.in rename {include => extern/liburingcxx/include}/uring/cq_entry.hpp (100%) rename {include => extern/liburingcxx/include}/uring/detail/cq.hpp (100%) rename {include => extern/liburingcxx/include}/uring/detail/int_flags.h (100%) rename {include => extern/liburingcxx/include}/uring/detail/sq.hpp (100%) rename {include => extern/liburingcxx/include}/uring/io_uring.h (100%) rename {include => extern/liburingcxx/include}/uring/sq_entry.hpp (99%) rename {include => extern/liburingcxx/include}/uring/syscall.hpp (100%) rename {include => extern/liburingcxx/include}/uring/uring.hpp (100%) rename {include => extern/liburingcxx/include}/uring/uring_define.hpp (100%) rename {include => extern/liburingcxx/include}/uring/utility/io_helper.hpp (100%) rename {include => extern/liburingcxx/include}/uring/utility/kernel_version.hpp (77%) create mode 100755 extern/liburingcxx/test/CMakeLists.txt create mode 100644 extern/liburingcxx/test/type_check.cpp delete mode 100644 include/co_context/config.hpp create mode 100644 include/co_context/config/.gitignore create mode 100644 include/co_context/config/config.hpp.in create mode 100644 include/co_context/config/io_context.hpp create mode 100644 include/co_context/config/log.hpp create mode 100644 include/co_context/config/net.hpp create mode 100644 include/co_context/config/uring.hpp diff --git a/.gitignore b/.gitignore index 2b7a840..adecfe0 100644 --- a/.gitignore +++ b/.gitignore @@ -74,7 +74,7 @@ draft/ *.rdb *.svg /perf/ -perf.data* +/perf.* *.data *.perf *.folded diff --git a/CMakeLists.txt b/CMakeLists.txt index ee98b50..05b35aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,11 @@ project(co_context include(cmake/Policy.cmake NO_POLICY_SCOPE) +add_subdirectory(extern/liburingcxx) + add_library(co_context STATIC) add_library(co_context::co_context ALIAS co_context) +target_link_libraries(co_context PUBLIC liburingcxx::liburingcxx) target_include_directories(co_context PUBLIC "$" @@ -19,6 +22,7 @@ include(cmake/CompileOption.cmake) include(cmake/Platform.cmake) include(cmake/Develop.cmake) include(cmake/Extra.cmake) +include(cmake/Configure.cmake) include(cmake/Install.cmake) add_subdirectory(lib) diff --git a/cmake/CompileOption.cmake b/cmake/CompileOption.cmake index 1c1ab80..ca80296 100644 --- a/cmake/CompileOption.cmake +++ b/cmake/CompileOption.cmake @@ -36,11 +36,10 @@ else() endif() if (mi_version) - target_compile_definitions(co_context PRIVATE CO_CONTEXT_USE_MIMALLOC) - set(USE_MIMALLOC ON) + set(CO_CONTEXT_USE_MIMALLOC ON) message(NOTICE "mimalloc ${mi_version} enabled") else() - set(USE_MIMALLOC OFF) + set(CO_CONTEXT_USE_MIMALLOC OFF) message(WARNING "mimalloc disabled") endif() @@ -48,14 +47,6 @@ set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) target_link_libraries(co_context PUBLIC Threads::Threads) -if (USE_MIMALLOC) +if (CO_CONTEXT_USE_MIMALLOC) target_link_libraries(co_context PUBLIC mimalloc) endif() - -if (co_context_no_generator) # set by check/check_compile.cmake - target_compile_definitions(co_context PUBLIC CO_CONTEXT_NO_GENERATOR) -endif() - -if (NOT co_context_has_kernel_rwf_t) # set by check/check_compile.cmake - target_compile_definitions(co_context PUBLIC CO_CONTEXT_OFFER_KERNEL_RWF_T) -endif() diff --git a/cmake/Configure.cmake b/cmake/Configure.cmake new file mode 100644 index 0000000..5b2ec4b --- /dev/null +++ b/cmake/Configure.cmake @@ -0,0 +1,5 @@ +configure_file( + ${PROJECT_SOURCE_DIR}/include/co_context/config/config.hpp.in + ${PROJECT_SOURCE_DIR}/include/co_context/config/config.hpp + @ONLY +) diff --git a/cmake/Install.cmake b/cmake/Install.cmake index cc314c5..19769d2 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -12,10 +12,6 @@ install(DIRECTORY "${co_context_SOURCE_DIR}/include/co_context" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) -install(DIRECTORY "${co_context_SOURCE_DIR}/include/uring" - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) - install(EXPORT co_context_targets FILE co_context_targets.cmake NAMESPACE co_context:: diff --git a/cmake/Platform.cmake b/cmake/Platform.cmake index bf7f136..a4fad01 100644 --- a/cmake/Platform.cmake +++ b/cmake/Platform.cmake @@ -1,19 +1,6 @@ # ---------------------------------------------------------------------------- # Linux # ---------------------------------------------------------------------------- -# Get the linux kernel version to liburingcxx -message(STATUS "target_kernel_version = ${CMAKE_SYSTEM_VERSION}") -set(kernel_version ${CMAKE_SYSTEM_VERSION}) -string(REGEX MATCH "^([0-9]+)\.([0-9]+)" kernel_version ${kernel_version}) -string(REGEX MATCH "^([0-9]+)" kernel_version_major ${kernel_version}) -string(REGEX REPLACE "^([0-9]+)\\." "" kernel_version_minor ${kernel_version}) -message(STATUS "kernel_version_major = ${kernel_version_major}") -message(STATUS "kernel_version_minor = ${kernel_version_minor}") -target_compile_definitions(co_context - PUBLIC "$" - PUBLIC "$" -) -unset(kernel_version) # ---------------------------------------------------------------------------- # Gcc diff --git a/cmake/check/check_compile.cmake b/cmake/check/check_compile.cmake index 72aa013..a0c04de 100644 --- a/cmake/check/check_compile.cmake +++ b/cmake/check/check_compile.cmake @@ -10,7 +10,7 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") endif() if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "11.3.0") message(NOTICE "co_context::generator will be disabled for insufficient gcc version (requires gcc 11.3 or above).") - set(co_context_no_generator ON) + set(CO_CONTEXT_NO_GENERATOR ON) endif() endif() @@ -20,19 +20,6 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") endif() if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "17.0.0") message(NOTICE "co_context::generator will be disabled for insufficient clang version (requires clang 17 or above).") - set(co_context_no_generator ON) + set(CO_CONTEXT_NO_GENERATOR ON) endif() endif() - -check_cxx_source_runs( - [====[ - #include - int main(int argc, char **argv) - { - __kernel_rwf_t x; - x = 0; - return x; - } - ]====] - co_context_has_kernel_rwf_t -) diff --git a/cmake/templates/Config.cmake.in b/cmake/templates/Config.cmake.in index ec2f0dc..c8bc311 100644 --- a/cmake/templates/Config.cmake.in +++ b/cmake/templates/Config.cmake.in @@ -4,18 +4,6 @@ include(CMakeFindDependencyMacro) find_dependency(Threads REQUIRED) find_dependency(mimalloc QUIET) -# Get the linux kernel version to liburingcxx -message(STATUS "target_kernel_version = ${CMAKE_SYSTEM_VERSION}") -set(kernel_version ${CMAKE_SYSTEM_VERSION}) -string(REGEX MATCH "^([0-9]+)\.([0-9]+)" kernel_version ${kernel_version}) -string(REGEX MATCH "^([0-9]+)" kernel_version_major ${kernel_version}) -string(REGEX REPLACE "^([0-9]+)\\." "" kernel_version_minor ${kernel_version}) -message(STATUS "kernel_version_major = ${kernel_version_major}") -message(STATUS "kernel_version_minor = ${kernel_version_minor}") -add_compile_definitions(LIBURINGCXX_KERNEL_VERSION_MAJOR=${kernel_version_major}) -add_compile_definitions(LIBURINGCXX_KERNEL_VERSION_MINOR=${kernel_version_minor}) -unset(kernel_version) - include("${CMAKE_CURRENT_LIST_DIR}/co_context_targets.cmake") check_required_components(co_context) diff --git a/example/iota.cpp b/example/iota.cpp index f42e107..41e0d90 100644 --- a/example/iota.cpp +++ b/example/iota.cpp @@ -1,4 +1,4 @@ -#ifndef CO_CONTEXT_NO_GENERATOR +#if !CO_CONTEXT_NO_GENERATOR #include @@ -22,7 +22,7 @@ int main() { return 0; } -#else // ifndef CO_CONTEXT_NO_GENERATOR +#else // if !CO_CONTEXT_NO_GENERATOR #include diff --git a/extern/liburingcxx/.clang-format b/extern/liburingcxx/.clang-format new file mode 100755 index 0000000..cffcb05 --- /dev/null +++ b/extern/liburingcxx/.clang-format @@ -0,0 +1,99 @@ +--- +BasedOnStyle: LLVM +IndentWidth: 4 +TabWidth: 4 +Language: Cpp +AlignAfterOpenBracket: BlockIndent # BlockIndent +AlignArrayOfStructures: Right +AlignConsecutiveBitFields: "Consecutive" +AlignConsecutiveMacros: "Consecutive" +AlignEscapedNewlines: Left +AlignOperands: "Align" +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AlignConsecutiveShortCaseStatements: # clang-format 17 + Enabled: true + AcrossEmptyLines: true + AcrossComments: true + AlignCaseColons: false +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLambdasOnASingleLine: Inline +AllowShortLoopsOnASingleLine: false +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: false +BreakAfterAttributes: Leave # clang-format 16 +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Attach +BreakBeforeConceptDeclarations: true +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeComma +BreakInheritanceList: BeforeComma +BreakStringLiterals: false +ColumnLimit: 80 +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +EmptyLineBeforeAccessModifier: Always +FixNamespaceComments: true +IndentCaseLabels: true +IndentExternBlock: Indent +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequires: true # clang-format 14 +IndentWrappedFunctionNames: false +InsertBraces: true # clang-format 15 +InsertNewlineAtEOF: true # clang-format 16 +IntegerLiteralSeparator: # clang-format 16 + Binary: 8 + Decimal: 0 + Hex: 2 + HexMinDigits: 8 +LambdaBodyIndentation: Signature +LineEnding: LF # clang-format 16 +KeepEmptyLinesAtEOF: false # clang-format 17 +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: Inner +PackConstructorInitializers: CurrentLine # clang-format 14 +PointerAlignment: Right +QualifierAlignment: Left +ReflowComments: true +RequiresClausePosition: OwnLine # clang-format 15 +RequiresExpressionIndentation: OuterScope # clang-format 16 +SeparateDefinitionBlocks: Always +ShortNamespaceLines: 5 +SortIncludes: "CaseSensitive" +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: "Never" +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Latest +UseCRLF: false +UseTab: Never +... diff --git a/extern/liburingcxx/.gitignore b/extern/liburingcxx/.gitignore new file mode 100755 index 0000000..7ddd4c8 --- /dev/null +++ b/extern/liburingcxx/.gitignore @@ -0,0 +1,72 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# CMake +build/ +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# Visual Studio Code +.vscode/ + +# Local History for Visual Studio Code +.history/ + +# clangd +.cache/ + +# Built Visual Studio Code Extensions +*.vsix + +draft/ + +perf.data* +*.data + +# log for perf +log +tmp +*.rdb +*.svg + +# public/index.html for httpd +public/ \ No newline at end of file diff --git a/extern/liburingcxx/CMakeLists.txt b/extern/liburingcxx/CMakeLists.txt new file mode 100644 index 0000000..b364b81 --- /dev/null +++ b/extern/liburingcxx/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.10.0) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +project(liburingcxx VERSION 0.9.0 LANGUAGES CXX) + +if (NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + message(WARNING "io_uring is only supported by Linux, but the target OS is ${CMAKE_SYSTEM_NAME}.") +endif() + +add_library(liburingcxx INTERFACE) +add_library(liburingcxx::liburingcxx ALIAS liburingcxx) + +target_include_directories( + liburingcxx + INTERFACE + "$" + "$" +) + +include(./cmake/option.cmake) +include(./cmake/configure.cmake) +include(./cmake/install.cmake) + +if (LIBURINGCXX_BUILD_EXAMPLE OR LIBURINGCXX_BUILD_TEST) + if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) + message("liburingcxx: Setting default CMAKE_BUILD_TYPE to Release.") + endif() + + if(LIBURINGCXX_BUILD_TEST) + add_subdirectory(./test) + endif() + + if(LIBURINGCXX_BUILD_EXAMPLE) + add_subdirectory(./example) + endif() +endif() diff --git a/extern/liburingcxx/LICENSE b/extern/liburingcxx/LICENSE new file mode 100644 index 0000000..f49a4e1 --- /dev/null +++ b/extern/liburingcxx/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/extern/liburingcxx/README.md b/extern/liburingcxx/README.md new file mode 100644 index 0000000..7504e9d --- /dev/null +++ b/extern/liburingcxx/README.md @@ -0,0 +1,21 @@ +# liburingcxx + +A header-only C++ implementation of axboe/liburing. Reordered submissions are supported. +Higher performance is achieved by compile-time configure. + +axboe/liburing 的 header-only C++ 实现,与原版不同,支持了 sqe 的乱序申请和提交,以更灵活地支持多线程应用;使用编译期配置,以提高运行性能。 + +# Installation(Optional) + +You may install this library to enable `find_package(liburingcxx)` for other CMake projects. + +```bash +cmake -B build . +sudo cmake --build build --target install +``` + +# Usage + +With CMake: +1. do `find_package(liburingcxx)` or `add_subdirectory(liburingcxx)`; +2. link `liburingcxx::liburingcxx` to your target, e.g. `target_link_libraries(my_target PUBLIC liburingcxx::liburingcxx)`. diff --git a/extern/liburingcxx/cmake/configure.cmake b/extern/liburingcxx/cmake/configure.cmake new file mode 100644 index 0000000..b6e3e1d --- /dev/null +++ b/extern/liburingcxx/cmake/configure.cmake @@ -0,0 +1,45 @@ +include(CheckCXXSourceRuns) + +message(STATUS "target_kernel_version = ${CMAKE_SYSTEM_VERSION}") +set(kernel_version ${CMAKE_SYSTEM_VERSION}) +string(REGEX MATCH "^([0-9]+)\.([0-9]+)" kernel_version ${kernel_version}) +string(REGEX MATCH "^([0-9]+)" LIBURINGCXX_KERNEL_VERSION_MAJOR ${kernel_version}) +string(REGEX REPLACE "^([0-9]+)\\." "" LIBURINGCXX_KERNEL_VERSION_MINOR ${kernel_version}) +message(STATUS "LIBURINGCXX_KERNEL_VERSION_MAJOR = ${LIBURINGCXX_KERNEL_VERSION_MAJOR}") +message(STATUS "LIBURINGCXX_KERNEL_VERSION_MINOR = ${LIBURINGCXX_KERNEL_VERSION_MINOR}") +unset(kernel_version) + +check_cxx_source_runs( + [====[ + #include + int main(int argc, char **argv) + { + __kernel_rwf_t x; + x = 0; + return x; + } + ]====] + LIBURINGCXX_HAS_KERNEL_RWF_T +) + +check_cxx_source_runs( + [====[ + #include + int main(){ return 0; } + ]====] + LIBURINGCXX_HAS_TIME_TYPES +) + +check_cxx_source_runs( + [====[ + #include + int main(){ return 0; } + ]====] + LIBURINGCXX_HAS_OPENAT2 +) + +configure_file( + ${PROJECT_SOURCE_DIR}/include/uring/config.hpp.in + ${PROJECT_SOURCE_DIR}/include/uring/config.hpp + @ONLY +) diff --git a/extern/liburingcxx/cmake/install.cmake b/extern/liburingcxx/cmake/install.cmake new file mode 100644 index 0000000..fe2d575 --- /dev/null +++ b/extern/liburingcxx/cmake/install.cmake @@ -0,0 +1,44 @@ +include(GNUInstallDirs) + +install(TARGETS liburingcxx + EXPORT liburingcxx_targets + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +install(DIRECTORY "${liburingcxx_SOURCE_DIR}/include/uring" + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +install(EXPORT liburingcxx_targets + FILE liburingcxx_targets.cmake + NAMESPACE liburingcxx:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/liburingcxx +) + +include(CMakePackageConfigHelpers) + +configure_package_config_file(${liburingcxx_SOURCE_DIR}/cmake/template/Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/liburingcxx-config.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/liburingcxx +) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/liburingcxx-config-version.cmake" + COMPATIBILITY SameMinorVersion) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/liburingcxx-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/liburingcxx-config-version.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/liburingcxx +) + +# uninstall target +if(NOT TARGET uninstall) + configure_file( + "${liburingcxx_SOURCE_DIR}/cmake/template/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) +endif() diff --git a/extern/liburingcxx/cmake/option.cmake b/extern/liburingcxx/cmake/option.cmake new file mode 100644 index 0000000..e3f4778 --- /dev/null +++ b/extern/liburingcxx/cmake/option.cmake @@ -0,0 +1,15 @@ +if(CMAKE_VERSION VERSION_LESS 3.21) + get_property(not_top DIRECTORY PROPERTY PARENT_DIRECTORY) + if(NOT not_top) + set(PROJECT_IS_TOP_LEVEL true) + endif() + unset(not_top) +endif() +include(CMakeDependentOption) + +if(PROJECT_IS_TOP_LEVEL) + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +endif() + +cmake_dependent_option(LIBURINGCXX_BUILD_EXAMPLE "Build examples of liburingcxx" ON "PROJECT_IS_TOP_LEVEL" OFF) +cmake_dependent_option(LIBURINGCXX_BUILD_TEST "Build tests of liburingcxx" ON "PROJECT_IS_TOP_LEVEL" OFF) diff --git a/extern/liburingcxx/cmake/template/Config.cmake.in b/extern/liburingcxx/cmake/template/Config.cmake.in new file mode 100644 index 0000000..904f54d --- /dev/null +++ b/extern/liburingcxx/cmake/template/Config.cmake.in @@ -0,0 +1,7 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +include("${CMAKE_CURRENT_LIST_DIR}/liburingcxx_targets.cmake") + +check_required_components(liburingcxx) diff --git a/extern/liburingcxx/cmake/template/cmake_uninstall.cmake.in b/extern/liburingcxx/cmake/template/cmake_uninstall.cmake.in new file mode 100644 index 0000000..0e4f71d --- /dev/null +++ b/extern/liburingcxx/cmake/template/cmake_uninstall.cmake.in @@ -0,0 +1,28 @@ +# ----------------------------------------------- +# File that provides "make uninstall" target +# We use the file 'install_manifest.txt' +# +# Details: https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake +# ----------------------------------------------- + +if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_BINARY_DIR@/install_manifest.txt\"") +endif() + +file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling: $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + execute_process( + COMMAND "@CMAKE_COMMAND@" "-E" "remove" "\"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RESULT_VARIABLE rm_retval + ) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif() + else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif() +endforeach() diff --git a/extern/liburingcxx/example/CMakeLists.txt b/extern/liburingcxx/example/CMakeLists.txt new file mode 100755 index 0000000..9439254 --- /dev/null +++ b/extern/liburingcxx/example/CMakeLists.txt @@ -0,0 +1,4 @@ +link_libraries(liburingcxx) + +add_executable(cat cat.cpp) +add_executable(httpd httpd.cpp) diff --git a/extern/liburingcxx/example/cat.cpp b/extern/liburingcxx/example/cat.cpp new file mode 100644 index 0000000..ecc2ebd --- /dev/null +++ b/extern/liburingcxx/example/cat.cpp @@ -0,0 +1,124 @@ +/* + * A tester using liburingcxx. + * + * Copyright (C) 2022 Zifeng Deng + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include + +#include + +#include +#include + +constexpr unsigned BLOCK_SZ = 1024; + +struct file_info { + size_t size; + iovec iovecs[]; +}; + +constexpr int count_blocks(size_t size, unsigned block_sz) noexcept { + return size / block_sz + ((size / block_sz * block_sz) != size); +} + +void output(std::string_view s) noexcept { + for (char c : s) { + putchar(c); + } +} + +using uring = liburingcxx::uring<0>; + +void submit_read_request(uring &ring, const std::filesystem::path path) { + // open the file + int file_fd = open(path.c_str(), O_RDONLY); + if (file_fd < 0) { + throw std::system_error{errno, std::system_category(), "open"}; + } + + // calculate the file size then malloc iovecs + const size_t file_size = std::filesystem::file_size(path); + const unsigned blocks = count_blocks(file_size, BLOCK_SZ); + file_info *fi = (file_info *)malloc(sizeof(*fi) + (blocks * sizeof(iovec))); + fi->size = file_size; + + // malloc buffers (to later let the ring fill them asynchronously) + for (size_t rest = file_size, i = 0; rest != 0; ++i) { + size_t to_read = std::min(rest, BLOCK_SZ); + fi->iovecs[i].iov_len = to_read; + if (posix_memalign(&fi->iovecs[i].iov_base, BLOCK_SZ, BLOCK_SZ) != 0) { + throw std::system_error{ + errno, std::system_category(), "posix_memalign" + }; + } + rest -= to_read; + } + + // submit the read request + liburingcxx::sq_entry &sqe = *ring.get_sq_entry(); + sqe.prep_readv(file_fd, std::span{fi->iovecs, blocks}, 0) + .set_data(reinterpret_cast(fi)); + + // Must be called after any request (except for polling mode) + ring.append_sq_entry(&sqe); + ring.submit(); +} + +void wait_result_and_print(uring &ring) { + // get a result from the ring + const liburingcxx::cq_entry *cqe; + [[maybe_unused]] int err = ring.wait_cq_entry(cqe); + + // get the according data + file_info *fi = reinterpret_cast(cqe->user_data); + + // print the data to console + const int blocks = count_blocks(fi->size, BLOCK_SZ); + for (int i = 0; i < blocks; ++i) { + output({(char *)fi->iovecs[i].iov_base, fi->iovecs[i].iov_len}); + } + + // Must be called after consuming a cqe + ring.seen_cq_entry(cqe); +} + +int main(int argc, char *argv[]) { + using std::cout, std::endl, std::cerr; + + if (argc < 2) { + fprintf(stderr, "Usage: %s [file name] <[file name] ...>\n", argv[0]); + return 1; + } + + uring ring; + ring.init(8); + + for (int i = 1; i < argc; ++i) { + try { + // put read request into the ring + submit_read_request(ring, argv[i]); + // get string from the ring, and output to console + wait_result_and_print(ring); + } catch (const std::system_error &e) { + cerr << e.what() << "\n" << e.code() << "\n"; + } catch (const std::exception &e) { + cerr << e.what() << '\n'; + } + } + + return 0; +} diff --git a/extern/liburingcxx/example/httpd.cpp b/extern/liburingcxx/example/httpd.cpp new file mode 100644 index 0000000..65c724f --- /dev/null +++ b/extern/liburingcxx/example/httpd.cpp @@ -0,0 +1,535 @@ +#include "uring/uring.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define SERVER_STRING "Server: zerohttpd/0.1\r\n" +#define DEFAULT_SERVER_PORT 8000 +#define QUEUE_DEPTH 256 +#define READ_SZ 8192 + +#define EVENT_TYPE_ACCEPT 0 +#define EVENT_TYPE_READ 1 +#define EVENT_TYPE_WRITE 2 + +#define MIN_KERNEL_VERSION 5 +#define MIN_MAJOR_VERSION 5 + +struct request { + int event_type; + unsigned int iovec_count; + int client_socket; + struct iovec iov[]; +}; + +using uring = liburingcxx::uring<0>; + +uring ring; + +const char *unimplemented_content = + "HTTP/1.0 400 Bad Request\r\n" + "Content-type: text/html\r\n" + "\r\n" + "" + "" + "ZeroHTTPd: Unimplemented" + "" + "" + "

Bad Request (Unimplemented)

" + "

Your client sent a request ZeroHTTPd did not understand and it is probably not your fault.

" + "" + ""; + +const char *http_404_content = + "HTTP/1.0 404 Not Found\r\n" + "Content-type: text/html\r\n" + "\r\n" + "" + "" + "ZeroHTTPd: Not Found" + "" + "" + "

Not Found (404)

" + "

Your client is asking for an object that was not found on this server.

" + "" + ""; + +/* + One function that prints the system call and the error details + and then exits with error code 1. Non-zero meaning things didn't go well. + */ +void fatal_error(const char *syscall) { + perror(syscall); + exit(1); +} + +int check_kernel_version() { + struct utsname buffer; + char *p; + long ver[16]; + int i = 0; + + if (uname(&buffer) != 0) { + perror("uname"); + exit(EXIT_FAILURE); + } + + p = buffer.release; + + while (*p) { + if (isdigit(*p)) { + ver[i] = strtol(p, &p, 10); + i++; + } else { + p++; + } + } + printf( + "Minimum kernel version required is: %d.%d\n", MIN_KERNEL_VERSION, + MIN_MAJOR_VERSION + ); + if (ver[0] > MIN_KERNEL_VERSION + || (ver[0] == MIN_KERNEL_VERSION && ver[1] >= MIN_MAJOR_VERSION)) { + printf("Your kernel version is: %ld.%ld\n", ver[0], ver[1]); + return 0; + } + fprintf(stderr, "Error: your kernel version is: %ld.%ld\n", ver[0], ver[1]); + return 1; +} + +void check_for_index_file() { + struct stat st; + int ret = stat("public/index.html", &st); + if (ret < 0) { + fprintf( + stderr, "ZeroHTTPd needs the \"public\" directory to be " + "present in the current directory.\n" + ); + fatal_error("stat: public/index.html"); + } +} + +/* + * Utility function to convert a string to lower case. + * */ + +void strtolower(char *str) { + for (; *str; ++str) { + *str = (char)tolower(*str); + } +} + +/* + * Helper function for cleaner looking code. + * */ + +void *zh_malloc(size_t size) { + void *buf = malloc(size); + if (!buf) { + fprintf(stderr, "Fatal error: unable to allocate memory.\n"); + exit(1); + } + return buf; +} + +/* + * This function is responsible for setting up the main listening socket used by + * the web server. + * */ + +int setup_listening_socket(int port) { + int sock; + struct sockaddr_in srv_addr; + + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock == -1) { + fatal_error("socket()"); + } + + int enable = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { + fatal_error("setsockopt(SO_REUSEADDR)"); + } + + std::memset(&srv_addr, 0, sizeof(srv_addr)); + srv_addr.sin_family = AF_INET; + srv_addr.sin_port = htons(port); + srv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + + /* We bind to a port and turn this socket into a listening + * socket. + * */ + if (bind(sock, (const struct sockaddr *)&srv_addr, sizeof(srv_addr)) < 0) { + fatal_error("bind()"); + } + + if (listen(sock, 10) < 0) { + fatal_error("listen()"); + } + + return (sock); +} + +int add_accept_request( + int server_socket, + struct sockaddr_in *client_addr, + socklen_t *client_addr_len +) { + auto &sqe = *ring.get_sq_entry(); + sqe.prep_accept( + server_socket, reinterpret_cast(client_addr), + client_addr_len, 0 + ); + + struct request *req = (request *)malloc(sizeof(*req)); + req->event_type = EVENT_TYPE_ACCEPT; + sqe.set_data((uint64_t)req); + ring.append_sq_entry(&sqe); + ring.submit(); + + return 0; +} + +int add_read_request(int client_socket) { + auto &sqe = *ring.get_sq_entry(); + struct request *req = + (request *)malloc(sizeof(*req) + sizeof(struct iovec)); + req->iov[0].iov_base = malloc(READ_SZ); + req->iov[0].iov_len = READ_SZ; + req->event_type = EVENT_TYPE_READ; + req->client_socket = client_socket; + std::memset(req->iov[0].iov_base, 0, READ_SZ); + /* Linux kernel 5.5 has support for readv, but not for recv() or read() */ + sqe.prep_readv(client_socket, {req->iov, 1}, 0); + sqe.set_data((uint64_t)req); + ring.append_sq_entry(&sqe); + ring.submit(); + return 0; +} + +int add_write_request(struct request *req) { + auto &sqe = *ring.get_sq_entry(); + req->event_type = EVENT_TYPE_WRITE; + sqe.prep_writev(req->client_socket, {req->iov, req->iovec_count}, 0); + sqe.set_data((uint64_t)req); + ring.append_sq_entry(&sqe); + ring.submit(); + return 0; +} + +void _send_static_string_content(const char *str, int client_socket) { + struct request *req = + (request *)zh_malloc(sizeof(*req) + sizeof(struct iovec)); + unsigned long slen = strlen(str); + req->iovec_count = 1; + req->client_socket = client_socket; + req->iov[0].iov_base = zh_malloc(slen); + req->iov[0].iov_len = slen; + memcpy(req->iov[0].iov_base, str, slen); + add_write_request(req); +} + +/* + * When ZeroHTTPd encounters any other HTTP method other than GET or POST, this + * function is used to inform the client. + * */ + +void handle_unimplemented_method(int client_socket) { + _send_static_string_content(unimplemented_content, client_socket); +} + +/* + * This function is used to send a "HTTP Not Found" code and message to the + * client in case the file requested is not found. + * */ + +void handle_http_404(int client_socket) { + _send_static_string_content(http_404_content, client_socket); +} + +/* + * Once a static file is identified to be served, this function is used to read + * the file and write it over the client socket using Linux's sendfile() system + * call. This saves us the hassle of transferring file buffers from kernel to + * user space and back. + * */ + +void copy_file_contents(char *file_path, off_t file_size, struct iovec *iov) { + int fd; + + char *buf = reinterpret_cast(zh_malloc(file_size)); + fd = open(file_path, O_RDONLY); + if (fd < 0) { + fatal_error("read"); + } + + /* We should really check for short reads here */ + int ret = read(fd, buf, file_size); + if (ret < file_size) { + fprintf(stderr, "Encountered a short read.\n"); + } + close(fd); + + iov->iov_base = buf; + iov->iov_len = file_size; +} + +/* + * Simple function to get the file extension of the file that we are about to + * serve. + * */ + +const char *get_filename_ext(const char *filename) { + const char *dot = strrchr(filename, '.'); + if (!dot || dot == filename) { + return ""; + } + return dot + 1; +} + +/* + * Sends the HTTP 200 OK header, the server string, for a few types of files, it + * can also send the content type based on the file extension. It also sends the + * content length header. Finally it send a '\r\n' in a line by itself + * signalling the end of headers and the beginning of any content. + * */ + +void send_headers(const char *path, off_t len, struct iovec *iov) { + char small_case_path[1024]; + char send_buffer[1024]; + strcpy(small_case_path, path); + strtolower(small_case_path); + + const char *str = "HTTP/1.0 200 OK\r\n"; + unsigned long slen = strlen(str); + iov[0].iov_base = zh_malloc(slen); + iov[0].iov_len = slen; + memcpy(iov[0].iov_base, str, slen); + + slen = strlen(SERVER_STRING); + iov[1].iov_base = zh_malloc(slen); + iov[1].iov_len = slen; + memcpy(iov[1].iov_base, SERVER_STRING, slen); + + /* + * Check the file extension for certain common types of files + * on web pages and send the appropriate content-type header. + * Since extensions can be mixed case like JPG, jpg or Jpg, + * we turn the extension into lower case before checking. + * */ + const char *file_ext = get_filename_ext(small_case_path); + if (strcmp("jpg", file_ext) == 0) { + strcpy(send_buffer, "Content-Type: image/jpeg\r\n"); + } + if (strcmp("jpeg", file_ext) == 0) { + strcpy(send_buffer, "Content-Type: image/jpeg\r\n"); + } + if (strcmp("png", file_ext) == 0) { + strcpy(send_buffer, "Content-Type: image/png\r\n"); + } + if (strcmp("gif", file_ext) == 0) { + strcpy(send_buffer, "Content-Type: image/gif\r\n"); + } + if (strcmp("htm", file_ext) == 0) { + strcpy(send_buffer, "Content-Type: text/html\r\n"); + } + if (strcmp("html", file_ext) == 0) { + strcpy(send_buffer, "Content-Type: text/html\r\n"); + } + if (strcmp("js", file_ext) == 0) { + strcpy(send_buffer, "Content-Type: application/javascript\r\n"); + } + if (strcmp("css", file_ext) == 0) { + strcpy(send_buffer, "Content-Type: text/css\r\n"); + } + if (strcmp("txt", file_ext) == 0) { + strcpy(send_buffer, "Content-Type: text/plain\r\n"); + } + slen = strlen(send_buffer); + iov[2].iov_base = zh_malloc(slen); + iov[2].iov_len = slen; + memcpy(iov[2].iov_base, send_buffer, slen); + + /* Send the content-length header, which is the file size in this case. */ + sprintf(send_buffer, "content-length: %ld\r\n", len); + slen = strlen(send_buffer); + iov[3].iov_base = zh_malloc(slen); + iov[3].iov_len = slen; + memcpy(iov[3].iov_base, send_buffer, slen); + + /* + * When the browser sees a '\r\n' sequence in a line on its own, + * it understands there are no more headers. Content may follow. + * */ + strcpy(send_buffer, "\r\n"); + slen = strlen(send_buffer); + iov[4].iov_base = zh_malloc(slen); + iov[4].iov_len = slen; + memcpy(iov[4].iov_base, send_buffer, slen); +} + +void handle_get_method(char *path, int client_socket) { + char final_path[1024]; + + /* + If a path ends in a trailing slash, the client probably wants the index + file inside of that directory. + */ + if (path[strlen(path) - 1] == '/') { + strcpy(final_path, "public"); + strcat(final_path, path); + strcat(final_path, "index.html"); + } else { + strcpy(final_path, "public"); + strcat(final_path, path); + } + + /* The stat() system call will give you information about the file + * like type (regular file, directory, etc), size, etc. */ + struct stat path_stat; + if (stat(final_path, &path_stat) == -1) { + printf("404 Not Found: %s (%s)\n", final_path, path); + handle_http_404(client_socket); + } else { + /* Check if this is a normal/regular file and not a directory or + * something else */ + if (S_ISREG(path_stat.st_mode)) { + struct request *req = reinterpret_cast( + zh_malloc(sizeof(*req) + (sizeof(struct iovec) * 6)) + ); + req->iovec_count = 6; + req->client_socket = client_socket; + send_headers(final_path, path_stat.st_size, req->iov); + copy_file_contents(final_path, path_stat.st_size, &req->iov[5]); + printf("200 %s %ld bytes\n", final_path, path_stat.st_size); + add_write_request(req); + } else { + handle_http_404(client_socket); + printf("404 Not Found: %s\n", final_path); + } + } +} + +/* + * This function looks at method used and calls the appropriate handler + * function. Since we only implement GET and POST methods, it calls + * handle_unimplemented_method() in case both these don't match. This sends an + * error to the client. + * */ + +void handle_http_method(char *method_buffer, int client_socket) { + char *method, *path, *saveptr; + + method = strtok_r(method_buffer, " ", &saveptr); + strtolower(method); + path = strtok_r(NULL, " ", &saveptr); + + if (strcmp(method, "get") == 0) { + handle_get_method(path, client_socket); + } else { + handle_unimplemented_method(client_socket); + } +} + +int get_line(const char *src, char *dest, int dest_sz) { + for (int i = 0; i < dest_sz; i++) { + dest[i] = src[i]; + if (src[i] == '\r' && src[i + 1] == '\n') { + dest[i] = '\0'; + return 0; + } + } + return 1; +} + +int handle_client_request(struct request *req) { + char http_request[1024]; + /* Get the first line, which will be the request */ + if (get_line( + (const char *)req->iov[0].iov_base, http_request, + sizeof(http_request) + )) { + fprintf(stderr, "Malformed request\n"); + exit(1); + } + handle_http_method(http_request, req->client_socket); + return 0; +} + +void server_loop(int server_socket) { + struct sockaddr_in client_addr; + socklen_t client_addr_len = sizeof(client_addr); + + add_accept_request(server_socket, &client_addr, &client_addr_len); + + const liburingcxx::cq_entry *cqe; + while (1) { + [[maybe_unused]] int err = ring.wait_cq_entry(cqe); + struct request *req = (struct request *)cqe->user_data; + // if (ret < 0) fatal_error("io_uring_wait_cqe"); + if (cqe->res < 0) { + fprintf( + stderr, "Async request failed: %s for event: %d\n", + strerror(-cqe->res), req->event_type + ); + exit(1); + } + + switch (req->event_type) { + case EVENT_TYPE_ACCEPT: + add_accept_request( + server_socket, &client_addr, &client_addr_len + ); + add_read_request(cqe->res); + free(req); + break; + case EVENT_TYPE_READ: + if (!cqe->res) { + fprintf(stderr, "Empty request!\n"); + break; + } + handle_client_request(req); + free(req->iov[0].iov_base); + free(req); + break; + case EVENT_TYPE_WRITE: + for (unsigned i = 0; i < req->iovec_count; i++) { + free(req->iov[i].iov_base); + } + close(req->client_socket); + free(req); + break; + } + /* Mark this request as processed */ + ring.seen_cq_entry(cqe); + } +} + +void sigint_handler(int signo) { + printf("^C pressed. Shutting down.\n"); + exit(0); +} + +int main() { + if (check_kernel_version()) { + return EXIT_FAILURE; + } + check_for_index_file(); + int server_socket = setup_listening_socket(DEFAULT_SERVER_PORT); + printf("ZeroHTTPd listening on port: %d\n", DEFAULT_SERVER_PORT); + + signal(SIGINT, sigint_handler); + ring.init(QUEUE_DEPTH); + server_loop(server_socket); + + return 0; +} diff --git a/extern/liburingcxx/format_count.sh b/extern/liburingcxx/format_count.sh new file mode 100755 index 0000000..62c0622 --- /dev/null +++ b/extern/liburingcxx/format_count.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +clang-format -i `find include/ -type f -name *.hpp` +clang-format -i `find example/ test/ -type f -name *.cpp` + +cloc --git `git branch --show-current` diff --git a/extern/liburingcxx/include/uring/.gitignore b/extern/liburingcxx/include/uring/.gitignore new file mode 100644 index 0000000..ce1bce0 --- /dev/null +++ b/extern/liburingcxx/include/uring/.gitignore @@ -0,0 +1 @@ +/config.hpp diff --git a/include/uring/arch/aarch64/lib.h b/extern/liburingcxx/include/uring/arch/aarch64/lib.h similarity index 100% rename from include/uring/arch/aarch64/lib.h rename to extern/liburingcxx/include/uring/arch/aarch64/lib.h diff --git a/include/uring/arch/aarch64/syscall.h b/extern/liburingcxx/include/uring/arch/aarch64/syscall.h similarity index 100% rename from include/uring/arch/aarch64/syscall.h rename to extern/liburingcxx/include/uring/arch/aarch64/syscall.h diff --git a/include/uring/arch/generic/lib.h b/extern/liburingcxx/include/uring/arch/generic/lib.h similarity index 100% rename from include/uring/arch/generic/lib.h rename to extern/liburingcxx/include/uring/arch/generic/lib.h diff --git a/include/uring/arch/generic/syscall.h b/extern/liburingcxx/include/uring/arch/generic/syscall.h similarity index 100% rename from include/uring/arch/generic/syscall.h rename to extern/liburingcxx/include/uring/arch/generic/syscall.h diff --git a/include/uring/arch/syscall-defs.h b/extern/liburingcxx/include/uring/arch/syscall-defs.h similarity index 100% rename from include/uring/arch/syscall-defs.h rename to extern/liburingcxx/include/uring/arch/syscall-defs.h diff --git a/include/uring/arch/x86/lib.h b/extern/liburingcxx/include/uring/arch/x86/lib.h similarity index 100% rename from include/uring/arch/x86/lib.h rename to extern/liburingcxx/include/uring/arch/x86/lib.h diff --git a/include/uring/arch/x86/syscall.h b/extern/liburingcxx/include/uring/arch/x86/syscall.h similarity index 100% rename from include/uring/arch/x86/syscall.h rename to extern/liburingcxx/include/uring/arch/x86/syscall.h diff --git a/include/uring/barrier.h b/extern/liburingcxx/include/uring/barrier.h similarity index 100% rename from include/uring/barrier.h rename to extern/liburingcxx/include/uring/barrier.h diff --git a/include/uring/buf_ring.hpp b/extern/liburingcxx/include/uring/buf_ring.hpp similarity index 100% rename from include/uring/buf_ring.hpp rename to extern/liburingcxx/include/uring/buf_ring.hpp diff --git a/include/uring/compat.hpp b/extern/liburingcxx/include/uring/compat.hpp similarity index 67% rename from include/uring/compat.hpp rename to extern/liburingcxx/include/uring/compat.hpp index 48d015b..6a32f04 100644 --- a/include/uring/compat.hpp +++ b/extern/liburingcxx/include/uring/compat.hpp @@ -1,15 +1,18 @@ #pragma once -#ifdef CO_CONTEXT_OFFER_KERNEL_RWF_T +#include + +#include // IWYU pragma: export + +#if !LIBURINGCXX_HAS_KERNEL_RWF_T typedef int __kernel_rwf_t; #endif -#if __has_include() +#if LIBURINGCXX_HAS_TIME_TYPES #include /* is included above and not needed again */ #define UAPI_LINUX_IO_URING_H_SKIP_LINUX_TIME_TYPES_H 1 #else -#include struct __kernel_timespec { int64_t tv_sec; @@ -19,8 +22,3 @@ struct __kernel_timespec { /* is not available, so it can't be included */ #define UAPI_LINUX_IO_URING_H_SKIP_LINUX_TIME_TYPES_H 1 #endif - -#if __has_include() -#include -#define LIBURINGCXX_HAS_OPENAT2 -#endif diff --git a/extern/liburingcxx/include/uring/config.hpp.in b/extern/liburingcxx/include/uring/config.hpp.in new file mode 100644 index 0000000..25e772c --- /dev/null +++ b/extern/liburingcxx/include/uring/config.hpp.in @@ -0,0 +1,12 @@ +#pragma once +#ifndef LIBURINGCXX_CONFIG_HPP +#define LIBURINGCXX_CONFIG_HPP + +#cmakedefine LIBURINGCXX_KERNEL_VERSION_MAJOR @LIBURINGCXX_KERNEL_VERSION_MAJOR@ +#cmakedefine LIBURINGCXX_KERNEL_VERSION_MINOR @LIBURINGCXX_KERNEL_VERSION_MINOR@ + +#cmakedefine01 LIBURINGCXX_HAS_KERNEL_RWF_T +#cmakedefine01 LIBURINGCXX_HAS_TIME_TYPES +#cmakedefine01 LIBURINGCXX_HAS_OPENAT2 + +#endif diff --git a/include/uring/cq_entry.hpp b/extern/liburingcxx/include/uring/cq_entry.hpp similarity index 100% rename from include/uring/cq_entry.hpp rename to extern/liburingcxx/include/uring/cq_entry.hpp diff --git a/include/uring/detail/cq.hpp b/extern/liburingcxx/include/uring/detail/cq.hpp similarity index 100% rename from include/uring/detail/cq.hpp rename to extern/liburingcxx/include/uring/detail/cq.hpp diff --git a/include/uring/detail/int_flags.h b/extern/liburingcxx/include/uring/detail/int_flags.h similarity index 100% rename from include/uring/detail/int_flags.h rename to extern/liburingcxx/include/uring/detail/int_flags.h diff --git a/include/uring/detail/sq.hpp b/extern/liburingcxx/include/uring/detail/sq.hpp similarity index 100% rename from include/uring/detail/sq.hpp rename to extern/liburingcxx/include/uring/detail/sq.hpp diff --git a/include/uring/io_uring.h b/extern/liburingcxx/include/uring/io_uring.h similarity index 100% rename from include/uring/io_uring.h rename to extern/liburingcxx/include/uring/io_uring.h diff --git a/include/uring/sq_entry.hpp b/extern/liburingcxx/include/uring/sq_entry.hpp similarity index 99% rename from include/uring/sq_entry.hpp rename to extern/liburingcxx/include/uring/sq_entry.hpp index 9155c7e..9a1dd67 100644 --- a/include/uring/sq_entry.hpp +++ b/extern/liburingcxx/include/uring/sq_entry.hpp @@ -10,6 +10,9 @@ #include #include #include +#if LIBURINGCXX_HAS_OPENAT2 +#include +#endif struct __kernel_timespec; struct epoll_event; @@ -35,8 +38,7 @@ class sq_entry final : private io_uring_sqe { return *this; } - [[nodiscard]] - inline uint64_t get_data() const noexcept { + [[nodiscard]] inline uint64_t get_data() const noexcept { return user_data; } @@ -87,8 +89,7 @@ class sq_entry final : private io_uring_sqe { return *this; } - [[nodiscard]] - inline bool is_cqe_skip() const noexcept { + [[nodiscard]] inline bool is_cqe_skip() const noexcept { return (this->flags & IOSQE_CQE_SKIP_SUCCESS); } #endif diff --git a/include/uring/syscall.hpp b/extern/liburingcxx/include/uring/syscall.hpp similarity index 100% rename from include/uring/syscall.hpp rename to extern/liburingcxx/include/uring/syscall.hpp diff --git a/include/uring/uring.hpp b/extern/liburingcxx/include/uring/uring.hpp similarity index 100% rename from include/uring/uring.hpp rename to extern/liburingcxx/include/uring/uring.hpp diff --git a/include/uring/uring_define.hpp b/extern/liburingcxx/include/uring/uring_define.hpp similarity index 100% rename from include/uring/uring_define.hpp rename to extern/liburingcxx/include/uring/uring_define.hpp diff --git a/include/uring/utility/io_helper.hpp b/extern/liburingcxx/include/uring/utility/io_helper.hpp similarity index 100% rename from include/uring/utility/io_helper.hpp rename to extern/liburingcxx/include/uring/utility/io_helper.hpp diff --git a/include/uring/utility/kernel_version.hpp b/extern/liburingcxx/include/uring/utility/kernel_version.hpp similarity index 77% rename from include/uring/utility/kernel_version.hpp rename to extern/liburingcxx/include/uring/utility/kernel_version.hpp index c1a032d..f8ae0ca 100644 --- a/include/uring/utility/kernel_version.hpp +++ b/extern/liburingcxx/include/uring/utility/kernel_version.hpp @@ -1,11 +1,13 @@ #pragma once +#include + #ifndef LIBURINGCXX_KERNEL_VERSION_MAJOR -#error Fail to detect kernel version. Please check cmake/Platform.cmake . +#error Fail to detect kernel version. Please check cmake/configure.cmake . #endif #ifndef LIBURINGCXX_KERNEL_VERSION_MINOR -#error Fail to detect kernel version. Please check cmake/Platform.cmake . +#error Fail to detect kernel version. Please check cmake/configure.cmake . #endif namespace liburingcxx { diff --git a/extern/liburingcxx/test/CMakeLists.txt b/extern/liburingcxx/test/CMakeLists.txt new file mode 100755 index 0000000..9abe677 --- /dev/null +++ b/extern/liburingcxx/test/CMakeLists.txt @@ -0,0 +1,3 @@ +link_libraries(liburingcxx) + +add_executable(type_check type_check.cpp) diff --git a/extern/liburingcxx/test/type_check.cpp b/extern/liburingcxx/test/type_check.cpp new file mode 100644 index 0000000..a8bf6d2 --- /dev/null +++ b/extern/liburingcxx/test/type_check.cpp @@ -0,0 +1,56 @@ +/* + * A type tester of liburingcxx. + * + * Copyright (C) 2022 Zifeng Deng + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#if !__has_include() +#include + +int main() { + std::cerr << "This test requires .\n"; + return 1; +} +#else +#include + +#include + +#include +#include + +int main(int argc, char *argv[]) { + using std::cout, std::endl; + + using namespace liburingcxx; + using namespace liburingcxx::detail; + using namespace std; + static_assert(is_standard_layout_v> && !is_trivial_v>); + static_assert(is_standard_layout_v && is_trivial_v); + static_assert(is_standard_layout_v && is_trivial_v); + static_assert(is_standard_layout_v && is_trivial_v); + static_assert(is_standard_layout_v && is_trivial_v); + + static_assert(sizeof(io_uring_sqe) == sizeof(sq_entry)); + static_assert(sizeof(io_uring_cqe) == sizeof(cq_entry)); + static_assert(sizeof(io_uring_sq) != sizeof(submission_queue)); + static_assert(sizeof(io_uring_cq) != sizeof(completion_queue)); + + cout << "All test passed!\n"; + + return 0; +} +#endif diff --git a/include/co_context/all.hpp b/include/co_context/all.hpp index 20464bb..66aa526 100644 --- a/include/co_context/all.hpp +++ b/include/co_context/all.hpp @@ -14,6 +14,6 @@ #include #include -#ifndef CO_CONTEXT_NO_GENERATOR +#if !CO_CONTEXT_NO_GENERATOR #include #endif diff --git a/include/co_context/co/when_all.hpp b/include/co_context/co/when_all.hpp index cfa4d02..cbdbad1 100644 --- a/include/co_context/co/when_all.hpp +++ b/include/co_context/co/when_all.hpp @@ -22,9 +22,8 @@ struct all_meta_base { // happen! uint32_t wait_num; - explicit all_meta_base( - std::coroutine_handle<> await_handle, uint32_t n - ) noexcept + explicit + all_meta_base(std::coroutine_handle<> await_handle, uint32_t n) noexcept : await_handle(await_handle) , wait_num(n) {} diff --git a/include/co_context/config.hpp b/include/co_context/config.hpp deleted file mode 100644 index 984c266..0000000 --- a/include/co_context/config.hpp +++ /dev/null @@ -1,148 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include -#include - -namespace co_context { - -namespace config { - - // ================== io_uring/liburingcxx configuration ================== - inline constexpr unsigned io_uring_setup_flags = 0; - // inline constexpr unsigned io_uring_setup_flags = IORING_SETUP_SQPOLL; - - /** - * @brief This tells if `IORING_SETUP_COOP_TASKRUN` - * and `IORING_SETUP_TASKRUN_FLAG` should be enabled. - * - * @note Do not modify this, check `io_uring_setup_flags` instead. - */ - inline constexpr unsigned io_uring_coop_taskrun_flag = - bool(io_uring_setup_flags & IORING_SETUP_SQPOLL) - ? 0 - : (IORING_SETUP_COOP_TASKRUN | IORING_SETUP_TASKRUN_FLAG); - - inline constexpr uint64_t uring_setup_flags = 0; - // ======================================================================== - - // ========================== CPU configuration =========================== - /** - * @brief Size of cache line on the CPU L1 cache. Usually this is 64 byte. - * @note Now `std::hardware_destructive_interference_size` is not used - * because of warning from gcc 12 - */ -#if 0 && __cpp_lib_hardware_interference_size >= 201603 - inline constexpr size_t cache_line_size = - std::hardware_destructive_interference_size; -#else - inline constexpr size_t cache_line_size = 64; -#endif - // ======================================================================== - - // ====================== Thread model configuration ====================== - // At most 255 threads/io_contexts. - using ctx_id_t = uint8_t; - - inline constexpr bool is_using_hyper_threading = true; - - /** - * @brief Use msg_ring to co_spawn betweens io_contexts, instead of - * eventfd, std::mutex and std::queue. - */ -#define CO_CONTEXT_IS_USING_MSG_RING LIBURINGCXX_IS_KERNEL_REACH(5, 18) -#define CO_CONTEXT_IS_USING_EVENTFD !CO_CONTEXT_IS_USING_MSG_RING - inline constexpr bool is_using_msg_ring = - LIBURINGCXX_IS_KERNEL_REACH(5, 18); - - /** - * @brief This tells if a standalone thread will run in kernel space. - * - * @note Do not modify this, check `io_uring_setup_flags` instead. - */ - inline constexpr bool is_using_sqpoll = - bool(io_uring_setup_flags & IORING_SETUP_SQPOLL); - - // ======================================================================== - - // ======================= io_context configuration ======================= - using cur_t = uint16_t; - // inline constexpr cur_t swap_capacity = 4; - // inline constexpr cur_t swap_capacity = 8; - // inline constexpr cur_t swap_capacity = 16; - // inline constexpr cur_t swap_capacity = 32; - // inline constexpr cur_t swap_capacity = 64; - // inline constexpr cur_t swap_capacity = 128; - // inline constexpr cur_t swap_capacity = 256; - // inline constexpr cur_t swap_capacity = 512; - // inline constexpr cur_t swap_capacity = 1024; - // inline constexpr cur_t swap_capacity = 2048; - // inline constexpr cur_t swap_capacity = 4096; - // inline constexpr cur_t swap_capacity = 8192; - inline constexpr cur_t swap_capacity = 16384; - // inline constexpr cur_t swap_capacity = 32768; - static_assert(swap_capacity % 4 == 0); - - inline constexpr uint32_t default_io_uring_entries = - std::bit_ceil(swap_capacity * 2ULL); - - /** - * @brief Maximal batch size of submissions. -1 means the batch size is - * unlimited. - * @note Once the threshold is reached, it is not mandatary to submit to - * io_uring immediately. As a result, the actual batch size might be equal - * to or slightly grater than the threshold. - */ - inline constexpr uint32_t submission_threshold = 32; - // inline constexpr uint32_t submission_threshold = -1U; - // ======================================================================== - - // ========================== net configuration =========================== - inline constexpr bool is_loopback_only = false; - // ======================================================================== - - // =========================== co configuration =========================== - using semaphore_counting_t = std::ptrdiff_t; - using condition_variable_counting_t = std::uintptr_t; - // ======================================================================== - - // ========================= timer configuration ========================== - /** - * @brief Fix the timer expiring time point, to improve accuracy. - * E.g. timeout(time) => timeout(time + bias); - * @note The accuracy of the timer is mainly limited by the OS. The actual - * time is usually 20~500 microseconds later than the scheduled time. - * @warning If it is more important to ensure that no network signal is - * missed than to ensure accuracy, it is recommended to set bias to 0. - */ - // inline constexpr int64_t timeout_bias_nanosecond = 0; - inline constexpr int64_t timeout_bias_nanosecond = -30'000; - // ======================================================================== - -} // namespace config - -// logging config -namespace config { - - enum class level : uint8_t { verbose, debug, info, warning, error, no_log }; - - // inline constexpr level log_level = level::verbose; - // inline constexpr level log_level = level::debug; - // inline constexpr level log_level = level::info; - inline constexpr level log_level = level::warning; - // inline constexpr level log_level = level::error; - // inline constexpr level log_level = level::no_log; - - inline constexpr bool is_log_v = log_level <= level::verbose; - inline constexpr bool is_log_d = log_level <= level::debug; - inline constexpr bool is_log_i = log_level <= level::info; - inline constexpr bool is_log_w = log_level <= level::warning; - inline constexpr bool is_log_e = log_level <= level::error; -} // namespace config - -} // namespace co_context diff --git a/include/co_context/config/.gitignore b/include/co_context/config/.gitignore new file mode 100644 index 0000000..ce1bce0 --- /dev/null +++ b/include/co_context/config/.gitignore @@ -0,0 +1 @@ +/config.hpp diff --git a/include/co_context/config/config.hpp.in b/include/co_context/config/config.hpp.in new file mode 100644 index 0000000..6fdf80f --- /dev/null +++ b/include/co_context/config/config.hpp.in @@ -0,0 +1,8 @@ +#pragma once +#ifndef CO_CONTEXT_CONFIG_HPP +#define CO_CONTEXT_CONFIG_HPP + +#cmakedefine01 CO_CONTEXT_NO_GENERATOR +#cmakedefine01 CO_CONTEXT_USE_MIMALLOC + +#endif diff --git a/include/co_context/config/io_context.hpp b/include/co_context/config/io_context.hpp new file mode 100644 index 0000000..857aca6 --- /dev/null +++ b/include/co_context/config/io_context.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include + +namespace co_context::config { + +// ========================== CPU configuration =========================== +/** + * @brief Size of cache line on the CPU L1 cache. Usually this is 64 byte. + * @note Now `std::hardware_destructive_interference_size` is not used + * because of warning from gcc 12 + */ +#if 0 && __cpp_lib_hardware_interference_size >= 201603 + inline constexpr size_t cache_line_size = + std::hardware_destructive_interference_size; +#else +inline constexpr size_t cache_line_size = 64; +#endif +// ======================================================================== + +// ====================== Thread model configuration ====================== +// At most 255 threads/io_contexts. +using ctx_id_t = uint8_t; + +inline constexpr bool is_using_hyper_threading = true; +// ======================================================================== + +// ======================= io_context configuration ======================= +using cur_t = uint32_t; +// inline constexpr cur_t swap_capacity = 4; +// inline constexpr cur_t swap_capacity = 8; +// inline constexpr cur_t swap_capacity = 16; +// inline constexpr cur_t swap_capacity = 32; +// inline constexpr cur_t swap_capacity = 64; +// inline constexpr cur_t swap_capacity = 128; +// inline constexpr cur_t swap_capacity = 256; +// inline constexpr cur_t swap_capacity = 512; +// inline constexpr cur_t swap_capacity = 1024; +// inline constexpr cur_t swap_capacity = 2048; +// inline constexpr cur_t swap_capacity = 4096; +// inline constexpr cur_t swap_capacity = 8192; +inline constexpr cur_t swap_capacity = 16384; +// inline constexpr cur_t swap_capacity = 32768; +static_assert(swap_capacity % 4 == 0); + +inline constexpr uint32_t default_io_uring_entries = + std::bit_ceil(swap_capacity * 2ULL); + +/** + * @brief Maximal batch size of submissions. -1 means the batch size is + * unlimited. + * @note Once the threshold is reached, it is not mandatary to submit to + * io_uring immediately. As a result, the actual batch size might be equal + * to or slightly greater than the threshold. + */ +inline constexpr uint32_t submission_threshold = 32; +// inline constexpr uint32_t submission_threshold = -1U; +// ======================================================================== + +// =========================== co configuration =========================== +using semaphore_counting_t = std::ptrdiff_t; +using condition_variable_counting_t = std::uintptr_t; +// ======================================================================== + +// ========================= timer configuration ========================== +/** + * @brief Fix the timer expiring time point, to improve accuracy. + * E.g. timeout(time) => timeout(time + bias); + * @note The accuracy of the timer is mainly limited by the OS. The actual + * time is usually 20~500 microseconds later than the scheduled time. + * @warning If it is more important to ensure that no network signal is + * missed than to ensure accuracy, it is recommended to set bias to 0. + */ +// inline constexpr int64_t timeout_bias_nanosecond = 0; +inline constexpr int64_t timeout_bias_nanosecond = -30'000; +// ======================================================================== + +} // namespace co_context::config diff --git a/include/co_context/config/log.hpp b/include/co_context/config/log.hpp new file mode 100644 index 0000000..1db0829 --- /dev/null +++ b/include/co_context/config/log.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace co_context::config { + +enum class level : uint8_t { verbose, debug, info, warning, error, no_log }; + +// inline constexpr level log_level = level::verbose; +// inline constexpr level log_level = level::debug; +// inline constexpr level log_level = level::info; +inline constexpr level log_level = level::warning; +// inline constexpr level log_level = level::error; +// inline constexpr level log_level = level::no_log; + +inline constexpr bool is_log_v = log_level <= level::verbose; +inline constexpr bool is_log_d = log_level <= level::debug; +inline constexpr bool is_log_i = log_level <= level::info; +inline constexpr bool is_log_w = log_level <= level::warning; +inline constexpr bool is_log_e = log_level <= level::error; + +} // namespace co_context::config diff --git a/include/co_context/config/net.hpp b/include/co_context/config/net.hpp new file mode 100644 index 0000000..2bc0e37 --- /dev/null +++ b/include/co_context/config/net.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace co_context::config { + +inline constexpr bool is_loopback_only = false; + +} // namespace co_context::config diff --git a/include/co_context/config/uring.hpp b/include/co_context/config/uring.hpp new file mode 100644 index 0000000..89758ad --- /dev/null +++ b/include/co_context/config/uring.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include + +namespace co_context::config { + +inline constexpr unsigned io_uring_setup_flags = 0; +// inline constexpr unsigned io_uring_setup_flags = IORING_SETUP_SQPOLL; + +/** + * @brief This tells if `IORING_SETUP_COOP_TASKRUN` + * and `IORING_SETUP_TASKRUN_FLAG` should be enabled. + * + * @note Do not modify this, check `io_uring_setup_flags` instead. + */ +inline constexpr unsigned io_uring_coop_taskrun_flag = + bool(io_uring_setup_flags & IORING_SETUP_SQPOLL) + ? 0 + : (IORING_SETUP_COOP_TASKRUN | IORING_SETUP_TASKRUN_FLAG); + +inline constexpr uint64_t uring_setup_flags = 0; + +/** + * @brief Use msg_ring to co_spawn betweens io_contexts, instead of + * eventfd, std::mutex and std::queue. + */ +#define CO_CONTEXT_IS_USING_MSG_RING LIBURINGCXX_IS_KERNEL_REACH(5, 18) +#define CO_CONTEXT_IS_USING_EVENTFD !CO_CONTEXT_IS_USING_MSG_RING +inline constexpr bool is_using_msg_ring = LIBURINGCXX_IS_KERNEL_REACH(5, 18); + +/** + * @brief This tells if a standalone thread will run in kernel space. + * + * @note Do not modify this, check `io_uring_setup_flags` instead. + */ +inline constexpr bool is_using_sqpoll = + bool(io_uring_setup_flags & IORING_SETUP_SQPOLL); + +} // namespace co_context::config diff --git a/include/co_context/detail/io_context_meta.hpp b/include/co_context/detail/io_context_meta.hpp index 2c69161..1142738 100644 --- a/include/co_context/detail/io_context_meta.hpp +++ b/include/co_context/detail/io_context_meta.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/include/co_context/detail/lazy_io_awaiter.hpp b/include/co_context/detail/lazy_io_awaiter.hpp index 8e66db5..e1cbfb4 100644 --- a/include/co_context/detail/lazy_io_awaiter.hpp +++ b/include/co_context/detail/lazy_io_awaiter.hpp @@ -220,18 +220,16 @@ struct lazy_recvmsg : lazy_awaiter { #if LIBURINGCXX_IS_KERNEL_REACH(5, 20) struct lazy_recvmsg_multishot : lazy_awaiter { - inline lazy_recvmsg_multishot( - int fd, msghdr *msg, unsigned flags - ) noexcept { + inline + lazy_recvmsg_multishot(int fd, msghdr *msg, unsigned flags) noexcept { sqe->prep_recvmsg_multishot(fd, msg, flags); } }; #endif struct lazy_sendmsg : lazy_awaiter { - inline lazy_sendmsg( - int fd, const msghdr *msg, unsigned int flags - ) noexcept { + inline + lazy_sendmsg(int fd, const msghdr *msg, unsigned int flags) noexcept { sqe->prep_sendmsg(fd, msg, flags); } }; @@ -510,9 +508,8 @@ struct lazy_link_timeout }; struct lazy_connect : lazy_awaiter { - inline lazy_connect( - int sockfd, const sockaddr *addr, socklen_t addrlen - ) noexcept { + inline + lazy_connect(int sockfd, const sockaddr *addr, socklen_t addrlen) noexcept { sqe->prep_connect(sockfd, addr, addrlen); } }; @@ -524,17 +521,15 @@ struct lazy_files_update : lazy_awaiter { }; struct lazy_fallocate : lazy_awaiter { - inline lazy_fallocate( - int fd, int mode, uint64_t offset, uint64_t len - ) noexcept { + inline + lazy_fallocate(int fd, int mode, uint64_t offset, uint64_t len) noexcept { sqe->prep_fallocate(fd, mode, offset, len); } }; struct lazy_openat : lazy_awaiter { - inline lazy_openat( - int dfd, const char *path, int flags, mode_t mode - ) noexcept { + inline + lazy_openat(int dfd, const char *path, int flags, mode_t mode) noexcept { sqe->prep_openat(dfd, path, flags, mode); } }; @@ -565,9 +560,8 @@ struct lazy_read : lazy_awaiter { }; struct lazy_write : lazy_awaiter { - inline lazy_write( - int fd, std::span buf, uint64_t offset - ) noexcept { + inline + lazy_write(int fd, std::span buf, uint64_t offset) noexcept { sqe->prep_write(fd, buf, offset); } }; @@ -585,9 +579,8 @@ struct lazy_statx : lazy_awaiter { }; struct lazy_fadvise : lazy_awaiter { - inline lazy_fadvise( - int fd, uint64_t offset, off_t len, int advice - ) noexcept { + inline + lazy_fadvise(int fd, uint64_t offset, off_t len, int advice) noexcept { sqe->prep_fadvise(fd, offset, len, advice); } }; @@ -599,9 +592,8 @@ struct lazy_madvise : lazy_awaiter { }; struct lazy_send : lazy_awaiter { - inline lazy_send( - int sockfd, std::span buf, int flags - ) noexcept { + inline + lazy_send(int sockfd, std::span buf, int flags) noexcept { sqe->prep_send(sockfd, buf, flags); } }; @@ -642,9 +634,8 @@ struct lazy_recv : lazy_awaiter { #if LIBURINGCXX_IS_KERNEL_REACH(5, 20) struct lazy_recv_multishot : lazy_awaiter { - inline lazy_recv_multishot( - int sockfd, std::span buf, int flags - ) noexcept { + inline + lazy_recv_multishot(int sockfd, std::span buf, int flags) noexcept { sqe->prep_recv_multishot(sockfd, buf, flags); } }; @@ -772,9 +763,8 @@ struct lazy_linkat : lazy_awaiter { }; struct lazy_link : lazy_awaiter { - inline lazy_link( - const char *oldpath, const char *newpath, int flags - ) noexcept { + inline + lazy_link(const char *oldpath, const char *newpath, int flags) noexcept { sqe->prep_link(oldpath, newpath, flags); } }; @@ -822,9 +812,8 @@ struct lazy_setxattr : lazy_awaiter { }; struct lazy_fgetxattr : lazy_awaiter { - inline lazy_fgetxattr( - int fd, const char *name, char *value, size_t len - ) noexcept { + inline + lazy_fgetxattr(int fd, const char *name, char *value, size_t len) noexcept { sqe->prep_fgetxattr(fd, name, value, len); } }; diff --git a/include/co_context/detail/spinlock.hpp b/include/co_context/detail/spinlock.hpp index 8d48a35..22909d0 100644 --- a/include/co_context/detail/spinlock.hpp +++ b/include/co_context/detail/spinlock.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/include/co_context/detail/spsc_cursor.hpp b/include/co_context/detail/spsc_cursor.hpp index c84feb4..a21b458 100644 --- a/include/co_context/detail/spsc_cursor.hpp +++ b/include/co_context/detail/spsc_cursor.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include diff --git a/include/co_context/detail/task_info.hpp b/include/co_context/detail/task_info.hpp index 2e8d536..8968cec 100644 --- a/include/co_context/detail/task_info.hpp +++ b/include/co_context/detail/task_info.hpp @@ -1,9 +1,9 @@ #pragma once -#include #include #include #include +#include #include namespace co_context::detail { diff --git a/include/co_context/detail/thread_meta.hpp b/include/co_context/detail/thread_meta.hpp index 6255be2..31671cc 100644 --- a/include/co_context/detail/thread_meta.hpp +++ b/include/co_context/detail/thread_meta.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include namespace co_context { diff --git a/include/co_context/detail/uring_type.hpp b/include/co_context/detail/uring_type.hpp index 32d6837..f9b1fbb 100644 --- a/include/co_context/detail/uring_type.hpp +++ b/include/co_context/detail/uring_type.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include diff --git a/include/co_context/detail/worker_meta.hpp b/include/co_context/detail/worker_meta.hpp index 46b7c7f..7601ae0 100644 --- a/include/co_context/detail/worker_meta.hpp +++ b/include/co_context/detail/worker_meta.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include #include diff --git a/include/co_context/generator.hpp b/include/co_context/generator.hpp index 489357f..5a24c52 100644 --- a/include/co_context/generator.hpp +++ b/include/co_context/generator.hpp @@ -4,13 +4,13 @@ // https://godbolt.org/z/5hcaPcfvP // #pragma once -#ifdef CO_CONTEXT_STD_GENERATOR +#ifdef CO_CONTEXT_HAS_STD_GENERATOR #include namespace co_context { using std::generator; } -#elif !defined(CO_CONTEXT_NO_GENERATOR) +#elif !CO_CONTEXT_NO_GENERATOR #pragma GCC system_header #include #include diff --git a/include/co_context/io_context.hpp b/include/co_context/io_context.hpp index cf0df93..c2c5301 100644 --- a/include/co_context/io_context.hpp +++ b/include/co_context/io_context.hpp @@ -18,7 +18,7 @@ */ #pragma once -#include +#include #include #include #include diff --git a/include/co_context/log/log.hpp b/include/co_context/log/log.hpp index 8399b18..c657d61 100644 --- a/include/co_context/log/log.hpp +++ b/include/co_context/log/log.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include diff --git a/include/co_context/net/inet_address.hpp b/include/co_context/net/inet_address.hpp index 6bc7bae..a404a94 100644 --- a/include/co_context/net/inet_address.hpp +++ b/include/co_context/net/inet_address.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include diff --git a/include/co_context/task.hpp b/include/co_context/task.hpp index d5d8616..ed8c107 100644 --- a/include/co_context/task.hpp +++ b/include/co_context/task.hpp @@ -393,8 +393,8 @@ namespace detail { } // namespace detail template -auto make_task(Awaiter awaiter) -> task>> { +auto make_task(Awaiter awaiter) -> task< + detail::remove_rvalue_reference_t>> { co_return co_await static_cast(awaiter); } diff --git a/include/co_context/utility/time_cast.hpp b/include/co_context/utility/time_cast.hpp index 47ac4e6..abc8723 100644 --- a/include/co_context/utility/time_cast.hpp +++ b/include/co_context/utility/time_cast.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include diff --git a/lib/co_context/detail/worker_meta.cpp b/lib/co_context/detail/worker_meta.cpp index 47ee001..60a6ae8 100644 --- a/lib/co_context/detail/worker_meta.cpp +++ b/lib/co_context/detail/worker_meta.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include diff --git a/lib/co_context/io_context.cpp b/lib/co_context/io_context.cpp index ab37714..e09d11a 100644 --- a/lib/co_context/io_context.cpp +++ b/lib/co_context/io_context.cpp @@ -1,8 +1,10 @@ +#include #ifdef CO_CONTEXT_USE_MIMALLOC #include #endif + #include -#include +#include #include #include #include diff --git a/lib/co_context/net/inet_address.cpp b/lib/co_context/net/inet_address.cpp index 2f6e642..26f3b1f 100644 --- a/lib/co_context/net/inet_address.cpp +++ b/lib/co_context/net/inet_address.cpp @@ -1,3 +1,4 @@ +#include #include #include diff --git a/test/generator_test.cpp b/test/generator_test.cpp index 97a1809..04ae54a 100644 --- a/test/generator_test.cpp +++ b/test/generator_test.cpp @@ -1,4 +1,4 @@ -#ifndef CO_CONTEXT_NO_GENERATOR +#if !CO_CONTEXT_NO_GENERATOR #include ///////////////////////////////////////////////////////////////////////////// @@ -318,12 +318,11 @@ int main() { stateful_alloc_example(std::allocator_arg, stateful_allocator{42}); - [[maybe_unused]] - member_coro m; + [[maybe_unused]] member_coro m; assert(*m.f().begin() == 42); } -#else // ifndef CO_CONTEXT_NO_GENERATOR +#else // if !CO_CONTEXT_NO_GENERATOR #include