├── .clang-format ├── .github └── workflows │ ├── bazel.yml │ ├── clang-format.yml │ └── cmake.yml ├── .gitignore ├── BUILD.bazel ├── CMakeLists.txt ├── README.md ├── WORKSPACE.bazel ├── bazel ├── BUILD.bazel ├── config │ ├── BUILD.bazel │ └── copt.bzl └── fmt.BUILD ├── build.sh ├── cmake └── utils.cmake ├── examples ├── chat_room.cc ├── echo_server.cc ├── echo_udp_client.cc ├── echo_udp_server.cc ├── fiber.cc └── http_server.cc ├── flexy ├── db.h ├── db │ ├── db.h │ ├── mysql.cpp │ ├── mysql.h │ ├── sqlite3.cpp │ └── sqlite3.h ├── env.h ├── env │ ├── application.cpp │ ├── application.h │ ├── daemon.cpp │ ├── daemon.h │ ├── env.cpp │ ├── env.h │ ├── signal.cpp │ └── signal.h ├── fiber.h ├── fiber │ ├── allocator.h │ ├── condition_variable.cpp │ ├── condition_variable.h │ ├── fiber.cpp │ ├── fiber.h │ ├── internal │ │ └── x86_64 │ │ │ ├── jump_fcontext.S │ │ │ ├── make_fcontext.S │ │ │ └── ontop_fcontext.S │ ├── mutex.cpp │ ├── mutex.h │ └── this_fiber.h ├── flexy.h ├── http.h ├── http │ ├── http.cpp │ ├── http.h │ ├── http11_common.h │ ├── http11_parser.h │ ├── http11_parser.rl │ ├── http11_parser.rl.cpp │ ├── http_parser.cpp │ ├── http_parser.h │ ├── http_server.cpp │ ├── http_server.h │ ├── http_session.cpp │ ├── http_session.h │ ├── httpclient_parser.h │ ├── httpclient_parser.rl │ ├── httpclient_parser.rl.cpp │ ├── servlet.cpp │ ├── servlet.h │ ├── ws_server.cpp │ ├── ws_server.h │ ├── ws_servlet.cpp │ ├── ws_servlet.h │ ├── ws_session.cpp │ └── ws_session.h ├── http2 │ ├── README.md │ ├── dynamic_table.cpp │ ├── dynamic_table.h │ ├── frame.cpp │ ├── frame.h │ ├── hpack.cpp │ ├── hpack.h │ ├── huffman.cpp │ ├── huffman.h │ └── huffman_table.h ├── net.h ├── net │ ├── address.cpp │ ├── address.h │ ├── bytearray.cpp │ ├── bytearray.h │ ├── edian.h │ ├── fd_manager.cpp │ ├── fd_manager.h │ ├── hook.cpp │ ├── hook.h │ ├── socket.cpp │ ├── socket.h │ ├── tcp_server.cpp │ └── tcp_server.h ├── schedule.h ├── schedule │ ├── async_io.cpp │ ├── async_io.h │ ├── channel.cpp │ ├── channel.h │ ├── iomanager.cpp │ ├── iomanager.h │ ├── scheduler.cpp │ ├── scheduler.h │ ├── semaphore.cpp │ ├── semaphore.h │ ├── timer.cpp │ ├── timer.h │ ├── worker.cpp │ └── worker.h ├── stream.h ├── stream │ ├── async_socket_stream.cpp │ ├── async_socket_stream.h │ ├── socket_stream.cpp │ ├── socket_stream.h │ ├── stream.cpp │ └── stream.h ├── thread.h ├── thread │ ├── atomic.h │ ├── blocking_queue.h │ ├── condition_variable.h │ ├── mutex.h │ ├── semaphore.h │ ├── this_thread.h │ ├── thread.cpp │ └── thread.h ├── util.h └── util │ ├── align.h │ ├── assert.h │ ├── config.cpp │ ├── config.h │ ├── file.cpp │ ├── file.h │ ├── function.h │ ├── hash_util.cpp │ ├── hash_util.h │ ├── lexical.h │ ├── likely.h │ ├── log.cpp │ ├── log.h │ ├── macro.h │ ├── memory.h │ ├── noncopyable.h │ ├── singleton.h │ ├── task.h │ ├── util.cpp │ └── util.h ├── output └── conf │ ├── mysql.yml │ ├── server.yml │ ├── sqlite3.yml │ ├── test.yml │ └── worker.yml └── tests ├── BUILD.bazel ├── CMakeLists.txt ├── test_address.cc ├── test_application.cc ├── test_async_io.cc ├── test_config.cc ├── test_daemon.cc ├── test_env.cc ├── test_fiber.cc ├── test_fiber_condition_variable.cc ├── test_fiber_mutex.cc ├── test_file.cc ├── test_function.cc ├── test_go.cc ├── test_hash_util.cc ├── test_hook.cc ├── test_http.cc ├── test_http_parser.cc ├── test_http_session.cc ├── test_iomanager.cc ├── test_logconfig.cc ├── test_logger.cc ├── test_mysql.cc ├── test_scheduler.cc ├── test_signal.cc ├── test_socket.cc ├── test_sqlite3.cc ├── test_task.cc ├── test_tcp_server.cc ├── test_this_fiber.cc ├── test_thread.cc ├── test_timer.cc ├── test_worker.cc └── test_ws_server.cc /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlinesLeft: true 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: true 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | BreakBeforeBinaryOperators: None 36 | BreakBeforeBraces: Attach 37 | BreakBeforeTernaryOperators: true 38 | BreakConstructorInitializersBeforeComma: false 39 | BreakAfterJavaFieldAnnotations: false 40 | BreakStringLiterals: true 41 | ColumnLimit: 80 42 | CommentPragmas: '^ IWYU pragma:' 43 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 44 | ConstructorInitializerIndentWidth: 4 45 | ContinuationIndentWidth: 4 46 | Cpp11BracedListStyle: true 47 | DerivePointerAlignment: true 48 | DisableFormat: false 49 | ExperimentalAutoDetectBinPacking: false 50 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 51 | IncludeCategories: 52 | - Regex: '^<.*\.h>' 53 | Priority: 1 54 | - Regex: '^<.*' 55 | Priority: 2 56 | - Regex: '.*' 57 | Priority: 3 58 | IncludeIsMainRegex: '([-_](test|unittest))?$' 59 | IndentCaseLabels: true 60 | IndentWidth: 4 61 | IndentWrappedFunctionNames: false 62 | JavaScriptQuotes: Leave 63 | JavaScriptWrapImports: true 64 | KeepEmptyLinesAtTheStartOfBlocks: false 65 | MacroBlockBegin: '' 66 | MacroBlockEnd: '' 67 | MaxEmptyLinesToKeep: 1 68 | NamespaceIndentation: None 69 | ObjCBlockIndentWidth: 4 70 | ObjCSpaceAfterProperty: false 71 | ObjCSpaceBeforeProtocolList: false 72 | PenaltyBreakBeforeFirstCallParameter: 1 73 | PenaltyBreakComment: 300 74 | PenaltyBreakFirstLessLess: 120 75 | PenaltyBreakString: 1000 76 | PenaltyExcessCharacter: 1000000 77 | PenaltyReturnTypeOnItsOwnLine: 200 78 | PointerAlignment: Left 79 | ReflowComments: true 80 | SortIncludes: true 81 | SpaceAfterCStyleCast: false 82 | SpaceBeforeAssignmentOperators: true 83 | SpaceBeforeParens: ControlStatements 84 | SpaceInEmptyParentheses: false 85 | SpacesBeforeTrailingComments: 2 86 | SpacesInAngles: false 87 | SpacesInContainerLiterals: true 88 | SpacesInCStyleCastParentheses: false 89 | SpacesInParentheses: false 90 | SpacesInSquareBrackets: false 91 | Standard: Auto 92 | TabWidth: 8 93 | UseTab: Never 94 | ... 95 | 96 | -------------------------------------------------------------------------------- /.github/workflows/bazel.yml: -------------------------------------------------------------------------------- 1 | name: Bazel 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ main ] 7 | 8 | jobs: 9 | build_on_20_04: 10 | strategy: 11 | matrix: 12 | gcc_version: [8, 9] 13 | runs-on: ubuntu-20.04 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | 18 | - name: deps 19 | run: | 20 | sudo apt install -y gcc-${{matrix.gcc_version}} g++-${{matrix.gcc_version}} 21 | sudo apt install libboost-all-dev -y 22 | - name: Build 23 | working-directory: ${{github.workspace}} 24 | run: bazel build --action_env=CXX=g++-${{matrix.gcc_version}} --action_env=CC=gcc-${{matrix.gcc_version}} ... 25 | 26 | - name: Test 27 | working-directory: ${{github.workspace}} 28 | run: bazel test --action_env=CXX=g++-${{matrix.gcc_version}} --action_env=CC=gcc-${{matrix.gcc_version}} --test_output=errors ... 29 | 30 | build_on_22_04: 31 | strategy: 32 | matrix: 33 | gcc_version: [10, 11, 12] 34 | runs-on: ubuntu-22.04 35 | 36 | steps: 37 | - uses: actions/checkout@v2 38 | 39 | - name: deps 40 | run: | 41 | sudo apt install -y gcc-${{matrix.gcc_version}} g++-${{matrix.gcc_version}} 42 | sudo apt install libboost-all-dev -y 43 | sudo apt install -y ragel 44 | - name: Build 45 | working-directory: ${{github.workspace}} 46 | run: bazel build --action_env=CXX=g++-${{matrix.gcc_version}} --action_env=CC=gcc-${{matrix.gcc_version}} ... 47 | 48 | - name: Test 49 | working-directory: ${{github.workspace}} 50 | run: bazel test --action_env=CXX=g++-${{matrix.gcc_version}} --action_env=CC=gcc-${{matrix.gcc_version}} --test_output=errors ... 51 | -------------------------------------------------------------------------------- /.github/workflows/clang-format.yml: -------------------------------------------------------------------------------- 1 | name: Clang Format Diff 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ main ] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-22.04 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | with: 16 | fetch-depth: 0 17 | - name: install clang-format 18 | run: sudo apt install -y clang-format 19 | - name: check-diff 20 | run: | 21 | diff=`git-clang-format --diff HEAD^` 22 | if ! [[ "$diff" = "no modified files to format" || "$diff" = "clang-format did not modify any files" ]]; then 23 | echo "The diff you sent is not formatted correctly." 24 | echo "The suggested format is" 25 | echo "$diff" 26 | exit 1 27 | fi 28 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake_gcc 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: [ main ] 7 | 8 | env: 9 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 10 | BUILD_TYPE: Release 11 | 12 | jobs: 13 | build_with_gcc: 14 | strategy: 15 | matrix: 16 | gcc_version: [10, 11, 12] 17 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 18 | # You can convert this to a matrix build if you need cross-platform coverage. 19 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 20 | runs-on: ubuntu-22.04 21 | 22 | steps: 23 | - uses: actions/checkout@v2 24 | 25 | - name: deps 26 | run: | 27 | sudo apt install -y build-essential 28 | sudo apt install -y gcc-${{matrix.gcc_version}} g++-${{matrix.gcc_version}} 29 | sudo apt install libboost-all-dev libyaml-cpp-dev libjsoncpp-dev libfmt-dev libssl-dev libmysqlclient-dev libsqlite-dev libgtest-dev libgmock-dev -y 30 | sudo cp -r /usr/include/jsoncpp/* /usr/include/ 31 | sudo apt install -y ragel 32 | - name: Configure CMake 33 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 34 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 35 | run: CXX=g++-${{matrix.gcc_version}} CC=gcc-${{matrix.gcc_version}} cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 36 | 37 | - name: Build 38 | # Build your program with the given configuration 39 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 40 | 41 | - name: Test 42 | working-directory: ${{github.workspace}}/build 43 | # Execute tests defined by the CMake configuration. 44 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 45 | run: ctest -C ${{env.BUILD_TYPE}} --output-on-failure -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #忽略build目录下的文件 2 | /build/* 3 | #忽略.vscode目录下的文件 4 | /.vscode/* 5 | /.cache/* 6 | #忽略include目录下的文件 7 | /include/* 8 | #忽略.idea目录下的文件 9 | /.idea/* 10 | 11 | #忽略ragel编译出来的文件 12 | # *.rl.cpp 13 | #忽略二进制文件 14 | /**/bin/* 15 | 16 | #忽略txt目录下文件 存放log日志和yaml配置文件 17 | /txt/* 18 | 19 | #忽略动态库和静态库 20 | *.so 21 | *.a 22 | # Bazel 23 | bazel-* 24 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("//bazel/config:copt.bzl", "FLEXY_COPTS") 2 | 3 | config_setting( 4 | name = "flexy_config_with_json", 5 | define_values = {"FLEXY_JSON": "true"}, 6 | visibility = ["//visibility:public"], 7 | ) 8 | 9 | cc_library( 10 | name = "flexy", 11 | srcs = glob(["flexy/**/*.cpp", "flexy/**/*.S"]), 12 | hdrs = glob(["flexy/**/*.h"]), 13 | visibility = ["//visibility:public"], 14 | deps = [ 15 | "@com_github_jsoncpp//:jsoncpp", 16 | "@com_github_fmt//:fmt", 17 | "@com_github_jbeder_yaml_cpp//:yaml-cpp", 18 | "@boringssl//:ssl", 19 | "@boringssl//:crypto", 20 | ], 21 | linkopts = [ 22 | "-ldl", 23 | "-lpthread", 24 | "-lsqlite3", 25 | "-lmysqlclient", 26 | ], 27 | copts = FLEXY_COPTS, 28 | defines = select({ 29 | "flexy_config_with_json": ["FLEXY_JSON"], 30 | "//conditions:default": ["FLEXY_YAML"], 31 | }), 32 | ) 33 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | project(flexy CXX C ASM) 3 | 4 | include(cmake/utils.cmake) 5 | 6 | set(CMAKE_CXX_STANDARD 17) 7 | set(CMAKE_CXX_FLAGS "$ENV{CXXFLAGS} -rdynamic -O0 -ggdb -Wall -Wno-deprecated -Werror -Wno-unused-function -Wno-builtin-macro-redefined -Wno-deprecated-declarations") 8 | 9 | include_directories(./) 10 | 11 | file(GLOB fcontext "flexy/fiber/internal/x86_64/*.S") 12 | file(GLOB fiber "flexy/fiber/*.cpp") 13 | file(GLOB thread "flexy/thread/*.cpp") 14 | file(GLOB schedule "flexy/schedule/*.cpp") 15 | file(GLOB net "flexy/net/*.cpp") 16 | file(GLOB http "flexy/http/*.cpp") 17 | file(GLOB db "flexy/db/*.cpp") 18 | file(GLOB stream "flexy/stream/*.cpp") 19 | file(GLOB http2 "flexy/http2/*.cpp") 20 | file(GLOB util "flexy/util/*.cpp") 21 | file(GLOB env "flexy/env/*.cpp") 22 | 23 | # set(LIB_SRC 24 | # ${fcontext} 25 | # ${fiber} 26 | # ${thread} 27 | # ${schedule} 28 | # ${net} 29 | # ${http} 30 | # ${db} 31 | # ${stream} 32 | # ${http2} 33 | # ${util} 34 | # ${env} 35 | # ) 36 | 37 | set(LIB_SRC 38 | flexy/thread/thread.cpp 39 | flexy/util/util.cpp 40 | flexy/util/log.cpp 41 | flexy/util/file.cpp 42 | flexy/fiber/fiber.cpp 43 | flexy/schedule/scheduler.cpp 44 | flexy/util/config.cpp 45 | flexy/schedule/timer.cpp 46 | flexy/schedule/channel.cpp 47 | flexy/schedule/iomanager.cpp 48 | flexy/net/fd_manager.cpp 49 | flexy/net/hook.cpp 50 | flexy/net/address.cpp 51 | flexy/net/socket.cpp 52 | flexy/net/tcp_server.cpp 53 | flexy/env/signal.cpp 54 | flexy/http/http.cpp 55 | flexy/http/http_parser.cpp 56 | flexy/stream/stream.cpp 57 | flexy/stream/socket_stream.cpp 58 | flexy/http/http_session.cpp 59 | flexy/http/http_server.cpp 60 | flexy/http/servlet.cpp 61 | flexy/env/env.cpp 62 | flexy/env/daemon.cpp 63 | flexy/env/application.cpp 64 | flexy/schedule/semaphore.cpp 65 | flexy/schedule/async_io.cpp 66 | flexy/db/mysql.cpp 67 | flexy/db/sqlite3.cpp 68 | flexy/stream/async_socket_stream.cpp 69 | flexy/util/hash_util.cpp 70 | flexy/schedule/worker.cpp 71 | flexy/http/ws_session.cpp 72 | flexy/http/ws_servlet.cpp 73 | flexy/http/ws_server.cpp 74 | flexy/http2/dynamic_table.cpp 75 | flexy/fiber/mutex.cpp 76 | flexy/net/bytearray.cpp 77 | flexy/http2/huffman.cpp 78 | flexy/http2/frame.cpp 79 | flexy/http2/hpack.cpp 80 | # flexy/http2/stream.cpp 81 | # flexy/http2/http2_stream.cpp 82 | # flexy/http2/http2_server.cpp 83 | flexy/fiber/condition_variable.cpp 84 | ) 85 | 86 | list(APPEND LIB_SRC ${fcontext}) 87 | 88 | ragelmaker(flexy/http/http11_parser.rl LIB_SRC ${CMAKE_CURRENT_SOURCE_DIR}/flexy/http) 89 | ragelmaker(flexy/http/httpclient_parser.rl LIB_SRC ${CMAKE_CURRENT_SOURCE_DIR}/flexy/http) 90 | 91 | set(LIBS 92 | flexy 93 | pthread 94 | fmt 95 | yaml-cpp 96 | jsoncpp 97 | dl 98 | mysqlclient 99 | sqlite3 100 | ssl 101 | crypto 102 | ) 103 | 104 | option(FLEXY_YAML "Config by Yaml" ON) 105 | option(FLEXY_JSON "Config by Json" OFF) 106 | option(BUILD_SHARED_LIBS "Build flexy as a shared lib" ON) 107 | option(BUILD_STATIC_LIBS "Build flext as a static lib" OFF) 108 | 109 | if(FLEXY_JSON) 110 | add_definitions(-DFLEXY_JSON) 111 | elseif(FLEXY_YAML) 112 | add_definitions(-DFLEXY_YAML) 113 | endif(FLEXY_JSON) 114 | 115 | if (BUILD_STATIC_LIBS) 116 | add_library(flexy STATIC ${LIB_SRC}) 117 | target_link_libraries(${LIBS}) 118 | elseif (BUILD_SHARED_LIBS) 119 | add_library(flexy SHARED ${LIB_SRC}) 120 | target_link_libraries(${LIBS}) 121 | endif(BUILD_STATIC_LIBS) 122 | 123 | redefine_file_macro(flexy) 124 | 125 | option(FLEXY_TESTS "flexy tests execute" ON) 126 | 127 | if(FLEXY_TESTS) 128 | 129 | set (GTEST_LIBS ${LIBS} gtest) 130 | enable_testing() 131 | 132 | add_subdirectory(tests) 133 | 134 | endif(FLEXY_TESTS) 135 | 136 | flexy_add_executable(echo_server "examples/echo_server.cc" "${LIBS}") 137 | flexy_add_executable(chat_room "examples/chat_room.cc" "${LIBS}") 138 | flexy_add_executable(http_server "examples/http_server.cc" "${LIBS}") 139 | # flexy_add_executable(http2_server "examples/http2_server.cc" "${LIBS}") 140 | flexy_add_executable(fiber "examples/fiber.cc" "${LIBS}") 141 | 142 | SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/output/bin) 143 | SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/output/lib) 144 | -------------------------------------------------------------------------------- /WORKSPACE.bazel: -------------------------------------------------------------------------------- 1 | workspace(name = "com_github_flexy") 2 | 3 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 4 | 5 | http_archive( 6 | name = "com_github_jsoncpp", 7 | sha256 = "a074e1b38083484e8e07789fd683599d19da8bb960959c83751cd0284bdf2043", 8 | strip_prefix = "jsoncpp-1.9.5", 9 | urls = ["https://github.com/open-source-parsers/jsoncpp/archive/refs/tags/1.9.5.zip"], 10 | ) 11 | 12 | http_archive( 13 | name = "com_github_fmt", 14 | sha256 = "e05c91d12165a06ba07e8006a2ffb6121abe73eb6987bd6121a8eafe8d49bac1", 15 | strip_prefix = "fmt-8.1.0", 16 | urls = ["https://github.com/fmtlib/fmt/archive/refs/tags/8.1.0.zip"], 17 | build_file = "//bazel:fmt.BUILD", 18 | ) 19 | 20 | http_archive( 21 | name = "com_github_jbeder_yaml_cpp", 22 | sha256 = "4d5e664a7fb2d7445fc548cc8c0e1aa7b1a496540eb382d137e2cc263e6d3ef5", 23 | strip_prefix = "yaml-cpp-yaml-cpp-0.7.0", 24 | urls = ["https://github.com/jbeder/yaml-cpp/archive/refs/tags/yaml-cpp-0.7.0.zip"], 25 | ) 26 | 27 | http_archive( 28 | name = "boringssl", 29 | sha256 = "5d299325d1db8b2f2db3d927c7bc1f9fcbd05a3f9b5c8239fa527c09bf97f995", # Last updated 2022-10-19 30 | strip_prefix = "boringssl-0acfcff4be10514aacb98eb8ab27bb60136d131b", 31 | urls = ["https://github.com/google/boringssl/archive/0acfcff4be10514aacb98eb8ab27bb60136d131b.tar.gz"], 32 | ) 33 | 34 | http_archive( 35 | name = "com_google_googletest", # 2021-07-09 36 | sha256 = "353571c2440176ded91c2de6d6cd88ddd41401d14692ec1f99e35d013feda55a", 37 | strip_prefix = "googletest-release-1.11.0", 38 | urls = ["https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip"], 39 | ) 40 | -------------------------------------------------------------------------------- /bazel/BUILD.bazel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4kangjc/flexy/7ed1f9b526801695a8b07729de2af77e419ac3d9/bazel/BUILD.bazel -------------------------------------------------------------------------------- /bazel/config/BUILD.bazel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4kangjc/flexy/7ed1f9b526801695a8b07729de2af77e419ac3d9/bazel/config/BUILD.bazel -------------------------------------------------------------------------------- /bazel/config/copt.bzl: -------------------------------------------------------------------------------- 1 | FLEXY_COPTS = [ 2 | "-rdynamic", 3 | "-ggdb", 4 | "-std=c++17", 5 | "-Wall", 6 | "-Werror", 7 | "-Wno-deprecated", 8 | "-Wno-unused-function", 9 | "-Wno-builtin-macro-redefined", 10 | "-Wno-deprecated-declarations", 11 | ] 12 | -------------------------------------------------------------------------------- /bazel/fmt.BUILD: -------------------------------------------------------------------------------- 1 | cc_library( 2 | name = "fmt", 3 | srcs = [ 4 | #"src/fmt.cc", # No C++ module support 5 | "src/format.cc", 6 | "src/os.cc", 7 | ], 8 | # hdrs = [ 9 | # "include/fmt/args.h", 10 | # "include/fmt/chrono.h", 11 | # "include/fmt/color.h", 12 | # "include/fmt/compile.h", 13 | # "include/fmt/core.h", 14 | # "include/fmt/format-inl.h", 15 | # "include/fmt/format.h", 16 | # "include/fmt/os.h", 17 | # "include/fmt/ostream.h", 18 | # "include/fmt/printf.h", 19 | # "include/fmt/ranges.h", 20 | # "include/fmt/std.h", 21 | # "include/fmt/xchar.h", 22 | # ], 23 | hdrs = glob(["include/fmt/*.h"]), 24 | includes = [ 25 | "include", 26 | ], 27 | strip_include_prefix = "include", 28 | visibility = ["//visibility:public"], 29 | ) 30 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | # !/bin/bash 2 | if [ ! -d "build" ]; then 3 | mkdir build && cd build && cmake .. 4 | else 5 | cd build 6 | fi 7 | make $@ 8 | -------------------------------------------------------------------------------- /cmake/utils.cmake: -------------------------------------------------------------------------------- 1 | # utils.cmake 2 | 3 | #重新定义当前目标的源文件的__FILE__宏 4 | function(redefine_file_macro targetname) 5 | #获取当前目标的所有源文件 6 | get_target_property(source_files "${targetname}" SOURCES) 7 | #遍历源文件 8 | foreach(sourcefile ${source_files}) 9 | #获取当前源文件的编译参数 10 | get_property(defs SOURCE "${sourcefile}" 11 | PROPERTY COMPILE_DEFINITIONS) 12 | #获取当前文件的绝对路径 13 | get_filename_component(filepath "${sourcefile}" ABSOLUTE) 14 | #将绝对路径中的项目路径替换成空,得到源文件相对于项目路径的相对路径 15 | string(REPLACE ${PROJECT_SOURCE_DIR}/ "" relpath ${filepath}) 16 | #将我们要加的编译参数(__FILE__定义)添加到原来的编译参数里面 17 | list(APPEND defs "__FILE__=\"${relpath}\"") 18 | #重新设置源文件的编译参数 19 | set_property( 20 | SOURCE "${sourcefile}" 21 | PROPERTY COMPILE_DEFINITIONS ${defs} 22 | ) 23 | endforeach() 24 | endfunction() 25 | 26 | function(ragelmaker src_rl outputlist outputdir) 27 | #Create a custom build step that will call ragel on the provided src_rl file. 28 | #The output .cpp file will be appended to the variable name passed in outputlist. 29 | 30 | get_filename_component(src_file ${src_rl} NAME_WE) 31 | 32 | set(rl_out ${outputdir}/${src_file}.rl.cpp) 33 | 34 | #adding to the list inside a function takes special care, we cannot use list(APPEND...) 35 | #because the results are local scope only 36 | set(${outputlist} ${${outputlist}} ${rl_out} PARENT_SCOPE) 37 | 38 | #Warning: The " -S -M -l -C -T0 --error-format=msvc" are added to match existing window invocation 39 | #we might want something different for mac and linux 40 | add_custom_command( 41 | OUTPUT ${rl_out} 42 | COMMAND cd ${outputdir} 43 | COMMAND ragel ${CMAKE_CURRENT_SOURCE_DIR}/${src_rl} -o ${rl_out} -l -G2 --error-format=msvc 44 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${src_rl} 45 | ) 46 | set_source_files_properties(${rl_out} PROPERTIES GENERATED TRUE) 47 | endfunction(ragelmaker) 48 | 49 | function(flexy_add_executable targetname srcs libs) 50 | add_executable(${targetname} ${srcs}) 51 | # add_dependencies(${targetname} ${depends}) 52 | redefine_file_macro(${targetname}) 53 | target_link_libraries(${targetname} ${libs}) 54 | endfunction() 55 | 56 | function(flexy_test_executable targetname srcs libs) 57 | add_executable(${targetname} ${srcs}) 58 | # add_dependencies(${targetname} ${depends}) 59 | redefine_file_macro(${targetname}) 60 | target_link_libraries(${targetname} ${libs}) 61 | add_test(NAME run_${targetname} COMMAND ${targetname}) 62 | endfunction() 63 | 64 | -------------------------------------------------------------------------------- /examples/chat_room.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace flexy; 4 | 5 | struct ChatRoom : public TcpServer { 6 | public: 7 | void handleClient(const Socket::ptr& client) override { 8 | { 9 | WRITELOCK(mutex_); 10 | clients_.insert(client); 11 | } 12 | while (!isStop_) { 13 | std::string s; 14 | s.resize(1024); 15 | if (client->recv(s) <= 0) { 16 | { 17 | WRITELOCK(mutex_); 18 | clients_.erase(client); 19 | } 20 | break; 21 | } 22 | READLOCK(mutex_); 23 | for (auto& sock : clients_) { 24 | if (sock != client) { 25 | sock->send(s); 26 | } 27 | } 28 | } 29 | } 30 | private: 31 | std::set clients_; 32 | rw_mutex mutex_; 33 | }; 34 | 35 | void run() { 36 | auto cr = std::make_shared(); 37 | auto addr = Address::LookupAny("0.0.0.0:8021"); 38 | cr->bind(addr); 39 | cr->start(); 40 | Signal::signal(SIGINT, [cr](){ cr->stop(); }); 41 | } 42 | 43 | int main() { 44 | IOManager iom(2); 45 | iom.async(run); 46 | } -------------------------------------------------------------------------------- /examples/echo_server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace flexy; 4 | 5 | static auto&& g_logger = FLEXY_LOG_ROOT(); 6 | 7 | class EchoServer : public TcpServer { 8 | public: 9 | void handleClient(const Socket::ptr& client) override { 10 | FLEXY_LOG_DEBUG(g_logger) << "handleClient " << *client; 11 | std::string s; 12 | s.resize(4096); 13 | while (true) { 14 | memset(s.data(), 0, 4096); 15 | if (client->recv(s) <= 0) break; 16 | client->send(s); 17 | } 18 | } 19 | }; 20 | 21 | void run() { 22 | auto es = std::make_shared(); 23 | auto addr = Address::LookupAny("0.0.0.0:8021"); 24 | es->bind(addr); 25 | es->start(); 26 | } 27 | 28 | int main() { 29 | IOManager iom(2, true, "echo"); 30 | iom.async(run); 31 | } -------------------------------------------------------------------------------- /examples/echo_udp_client.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace flexy; 4 | 5 | static auto&& g_logger = FLEXY_LOG_ROOT(); 6 | 7 | const char* ip; 8 | 9 | void run() { 10 | auto addr = Address::LookupAnyIPAddress(ip); 11 | if (!addr) { 12 | FLEXY_LOG_ERROR(g_logger) << "invalid ip: " << ip; 13 | return; 14 | } 15 | auto sock = Socket::CreateUDP(addr->getFamily()); 16 | go[sock]() { 17 | Address::ptr addr = std::make_shared(); 18 | FLEXY_LOG_INFO(g_logger) << "begin recv"; 19 | while (true) { 20 | char buff[1024]; 21 | int len = sock->recvFrom(buff, 1024, addr); 22 | if (len > 0) { 23 | std::cout << std::endl 24 | << " recv: " << std::string_view(buff, len) 25 | << " from: " << *addr << std::endl; 26 | } 27 | } 28 | }; 29 | 30 | // sleep(1); 31 | while (true) { 32 | std::string line; 33 | std::cout << "input> "; 34 | // std::getline(std::cin, line); 35 | getline(async_cin, line); 36 | if (!line.empty()) { 37 | int len = sock->sendTo(line.c_str(), line.size(), addr); 38 | if (len < 0) { 39 | int err = sock->getError(); 40 | FLEXY_LOG_ERROR(g_logger) 41 | << "send error err = " << err 42 | << " errstr = " << strerror(err) << " len = " << len 43 | << " addr = " << *addr << " sock = " << sock; 44 | } else { 45 | FLEXY_LOG_INFO(g_logger) << "send " << line << " len = " << len; 46 | } 47 | } 48 | } 49 | } 50 | 51 | int main(int argc, char** argv) { 52 | if (argc < 2) { 53 | return 0; 54 | } 55 | std::string s = argv[1] + std::string(":") + argv[2]; 56 | ip = s.c_str(); 57 | IOManager iom; 58 | go run; 59 | } -------------------------------------------------------------------------------- /examples/echo_udp_server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace flexy; 4 | 5 | static auto&& g_logger = FLEXY_LOG_ROOT(); 6 | 7 | void run() { 8 | auto addr = Address::LookupAnyIPAddress("0.0.0.0:8050"); 9 | auto sock = Socket::CreateUDP(addr->getFamily()); 10 | if (sock->bind(addr)) { 11 | FLEXY_LOG_INFO(g_logger) << "udp bind : " << *addr; 12 | } else { 13 | FLEXY_LOG_ERROR(g_logger) << "udp bind : " << *addr << " fail"; 14 | return; 15 | } 16 | while (true) { 17 | char buff[1024]; 18 | Address::ptr from = std::make_shared(); 19 | int len = sock->recvFrom(buff, 1024, from); 20 | if (len > 0) { 21 | buff[len] = 0; 22 | FLEXY_LOG_INFO(g_logger) << "recv: " << buff << " from: " << *from; 23 | len = sock->sendTo(buff, len, from); 24 | if (len < 0) { 25 | FLEXY_LOG_INFO(g_logger) << "send: " << buff << " to: " << *from 26 | << " error = " << len; 27 | } 28 | } 29 | } 30 | } 31 | 32 | int main() { 33 | IOManager iom; 34 | go run; 35 | } -------------------------------------------------------------------------------- /examples/fiber.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static auto& g_logger = FLEXY_LOG_ROOT(); 4 | 5 | struct run { 6 | void operator()(int& argc, char** argv) { 7 | for (int i = 1; i < argc; ++i) { 8 | FLEXY_LOG_DEBUG(g_logger) << argv[i]; 9 | } 10 | argc = 0x99; 11 | } 12 | }; 13 | 14 | struct TreeNode { 15 | int val; 16 | TreeNode* left; 17 | TreeNode* right; 18 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 19 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 20 | TreeNode(int x, TreeNode* left, TreeNode* right) 21 | : val(x), left(left), right(right) {} 22 | }; 23 | 24 | // leetcode 173 25 | struct BSTIterator { 26 | private: 27 | void dfs(TreeNode* root) { 28 | if (root == nullptr) { 29 | return; 30 | } 31 | dfs(root->left); 32 | cur = root->val; 33 | flexy::this_fiber::yield(); 34 | dfs(root->right); 35 | } 36 | 37 | public: 38 | BSTIterator(TreeNode* root) { 39 | fiber = flexy::fiber_make_shared(&BSTIterator::dfs, this, root); 40 | } 41 | 42 | int next() { 43 | fiber->resume(); 44 | return cur; 45 | } 46 | 47 | bool hasNext() { return fiber->getState() != flexy::Fiber::TERM; } 48 | 49 | private: 50 | int cur; 51 | flexy::Fiber::ptr fiber; 52 | }; 53 | 54 | void test_tree_iterator() { 55 | TreeNode node_3(9), node_4(20); 56 | TreeNode node_1(3), node_2(15, &node_3, &node_4); 57 | TreeNode root(7, &node_1, &node_2); 58 | 59 | BSTIterator tree_iterator(&root); 60 | 61 | std::stringstream ss; 62 | ss << '['; 63 | while (tree_iterator.hasNext()) { 64 | ss << tree_iterator.next() << ", "; 65 | } 66 | std::string s(std::move(ss.str())); 67 | s.pop_back(), s.pop_back(); // pop ", " 68 | s.push_back(']'); 69 | FLEXY_LOG_INFO(g_logger) << s; // [3, 7, 9, 15, 20] 70 | } 71 | 72 | int main(int argc, char** argv) { 73 | flexy::IOManager iom; // fiber scheduler [1 thread] 74 | 75 | auto fiber_1 = flexy::fiber_make_shared( 76 | [](int a, int b) { // like std::make_shared to create fiber 77 | FLEXY_LOG_FMT_INFO(g_logger, "{} + {} = {}", a, b, 78 | a + b); // cpp20 format log 79 | 80 | using namespace flexy; 81 | using namespace std::chrono_literals; 82 | 83 | FLEXY_LOG_FMT_DEBUG( 84 | g_logger, "fiber id = {}", 85 | this_fiber::get_id()); // like std::this_thread::get_id 86 | this_fiber::yield(); // like std::this_thread::yield 87 | FLEXY_LOG_DEBUG(g_logger) << "resume from hello fiber"; 88 | // this_fiber::sleep_for(1000ms); // like std::this_thread 89 | // ::sleep_for 90 | // this_fiber::sleep_until(std::chrono::steady_clock::now() + 91 | // 2000ms); // like std::this_thread::sleep_util 92 | }, 93 | 1, 2); 94 | iom.async(fiber_1); // schedule fiber 95 | 96 | go[fiber_1]() { 97 | FLEXY_LOG_DEBUG(g_logger) << "Hello fiber"; // go style schedule lambda 98 | go fiber_1; // resume fiber_1 99 | }; 100 | 101 | run r; // function object 102 | go_args(r, std::ref(argc), 103 | argv); // use args [pass by reference and pass by value] 104 | 105 | test_tree_iterator(); 106 | 107 | iom.async_first( 108 | [&argc]() { FLEXY_LOG_FMT_DEBUG(g_logger, "argc = {}", argc); }); 109 | 110 | iom.async( 111 | [](int& argc) { FLEXY_LOG_FMT_DEBUG(g_logger, "argc = {}", argc); }, 112 | std::ref(argc)); 113 | } -------------------------------------------------------------------------------- /examples/http_server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace flexy; 4 | 5 | static auto&& g_logger = FLEXY_LOG_ROOT(); 6 | 7 | void run() { 8 | auto addr = Address::LookupAnyIPAddress("[::]:8020", AF_INET6); 9 | // auto addr = Address::LookupAnyIPAddress("0.0.0.0:8020"); 10 | if (!addr) { 11 | FLEXY_LOG_ERROR(g_logger) << "get address error"; 12 | return; 13 | } 14 | 15 | auto http_server = std::make_shared(true); 16 | 17 | while (!http_server->bind(addr)) {} 18 | http_server->start(); 19 | } 20 | 21 | int main(int argc, char** argv) { 22 | g_logger->setLevel(LogLevel::INFO); 23 | int count = 1; 24 | if (argc > 1) { 25 | count = atoi(argv[1]); 26 | } 27 | IOManager worker(count, true, "worker"); 28 | go run; 29 | } 30 | -------------------------------------------------------------------------------- /flexy/db.h: -------------------------------------------------------------------------------- 1 | #include "db/db.h" 2 | #include "db/mysql.h" 3 | #include "db/sqlite3.h" -------------------------------------------------------------------------------- /flexy/db/db.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace flexy { 7 | 8 | class ISQLData { 9 | public: 10 | using ptr = std::shared_ptr; 11 | virtual ~ISQLData() {} 12 | 13 | virtual int getErrno() const = 0; 14 | virtual const std::string& getErrStr() const = 0; 15 | 16 | virtual int getDataCount() = 0; 17 | virtual int getColumnCount() = 0; 18 | virtual int getColumnBytes(int idx) = 0; 19 | virtual int getColumnType(int idx) = 0; 20 | virtual std::string getColumnName(int idx) = 0; 21 | 22 | virtual bool isNull(int idx) = 0; 23 | virtual int8_t getInt8(int idx) = 0; 24 | virtual uint8_t getUint8(int idx) = 0; 25 | virtual int16_t getInt16(int idx) = 0; 26 | virtual uint16_t getUint16(int idx) = 0; 27 | virtual int32_t getInt32(int idx) = 0; 28 | virtual uint32_t getUint32(int idx) = 0; 29 | virtual int64_t getInt64(int idx) = 0; 30 | virtual uint64_t getUint64(int idx) = 0; 31 | virtual float getFloat(int idx) = 0; 32 | virtual double getDouble(int idx) = 0; 33 | virtual std::string getString(int idx) = 0; 34 | virtual std::string getBlob(int idx) = 0; 35 | virtual time_t getTime(int idx) = 0; 36 | virtual std::string getTimeStr(int idx) = 0; 37 | virtual bool next() = 0; 38 | }; 39 | 40 | class ISQLUpdate { 41 | public: 42 | virtual ~ISQLUpdate() {} 43 | virtual int execute(const char* format, ...) = 0; 44 | virtual int execute(const std::string& sql) = 0; 45 | virtual int64_t getLastInsertId() = 0; 46 | }; 47 | 48 | class ISQLQuery { 49 | public: 50 | virtual ~ISQLQuery() {} 51 | virtual ISQLData::ptr query(const char* format, ...) = 0; 52 | virtual ISQLData::ptr query(const std::string& sql) = 0; 53 | }; 54 | 55 | class IStmt { 56 | public: 57 | typedef std::shared_ptr ptr; 58 | virtual ~IStmt() {} 59 | virtual int bindInt8(int idx, int8_t value) = 0; 60 | virtual int bindUint8(int idx, uint8_t value) = 0; 61 | virtual int bindInt16(int idx, int16_t value) = 0; 62 | virtual int bindUint16(int idx, uint16_t value) = 0; 63 | virtual int bindInt32(int idx, int32_t value) = 0; 64 | virtual int bindUint32(int idx, uint32_t value) = 0; 65 | virtual int bindInt64(int idx, int64_t value) = 0; 66 | virtual int bindUint64(int idx, uint64_t value) = 0; 67 | virtual int bindFloat(int idx, float value) = 0; 68 | virtual int bindDouble(int idx, double value) = 0; 69 | virtual int bindString(int idx, const char* value) = 0; 70 | virtual int bindString(int idx, const std::string& value) = 0; 71 | virtual int bindBlob(int idx, const void* value, int64_t size) = 0; 72 | virtual int bindBlob(int idx, const std::string& value) = 0; 73 | virtual int bindTime(int idx, time_t value) = 0; 74 | virtual int bindNull(int idx) = 0; 75 | 76 | virtual int execute() = 0; 77 | virtual int64_t getLastInsertId() = 0; 78 | virtual ISQLData::ptr query() = 0; 79 | 80 | virtual int getErrno() = 0; 81 | virtual std::string getErrStr() = 0; 82 | }; 83 | 84 | class ITransaction : public ISQLUpdate { 85 | public: 86 | using ptr = std::shared_ptr; 87 | virtual ~ITransaction(){}; 88 | virtual bool begin() = 0; 89 | virtual bool commit() = 0; 90 | virtual bool rollback() = 0; 91 | }; 92 | 93 | class IDB : public ISQLUpdate, public ISQLQuery { 94 | public: 95 | using ptr = std::shared_ptr; 96 | virtual ~IDB() {} 97 | 98 | virtual IStmt::ptr prepare(const std::string& stmt) = 0; 99 | virtual int getErrno() = 0; 100 | virtual std::string getErrStr() = 0; 101 | virtual ITransaction::ptr openTransaction(bool auto_commit = false) = 0; 102 | }; 103 | 104 | struct Blob { 105 | const void* value; 106 | int len; 107 | }; 108 | 109 | } // namespace flexy 110 | -------------------------------------------------------------------------------- /flexy/env.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "env/application.h" 4 | #include "env/daemon.h" 5 | #include "env/env.h" 6 | #include "env/signal.h" -------------------------------------------------------------------------------- /flexy/env/application.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/http/http_server.h" 4 | #include "flexy/http/ws_server.h" 5 | namespace flexy { 6 | 7 | class Application { 8 | public: 9 | Application(); 10 | 11 | static Application* GetInstance() { return s_instance; } 12 | bool init(int argc, char** argv); 13 | bool run(); 14 | 15 | template 16 | std::vector> getServer() const; 17 | 18 | auto listAllServer() const { return servers_; } 19 | 20 | private: 21 | int main(int argc, char** argv); 22 | void run_fiber(); 23 | 24 | private: 25 | int argc_ = 0; 26 | char** argv_ = nullptr; 27 | // std::vector httpservers_; 28 | std::unordered_map> servers_; 29 | inline static Application* s_instance = nullptr; 30 | 31 | public: 32 | std::function serverReady = nullptr; 33 | std::function serverUp = nullptr; 34 | }; 35 | 36 | template 37 | std::vector> Application::getServer() const { 38 | #define XX(type) \ 39 | if (auto it = servers_.find(#type); it != servers_.end()) { \ 40 | std::vector> servers; \ 41 | servers.reserve(it->second.size()); \ 42 | for (auto& server : it->second) { \ 43 | servers.push_back(std::dynamic_pointer_cast(server)); \ 44 | } \ 45 | return servers; \ 46 | } \ 47 | return {} 48 | 49 | if constexpr (std::is_same_v) { 50 | XX(http); 51 | } else if constexpr (std::is_same_v) { 52 | XX(ws); 53 | } else if constexpr (std::is_same_v) { 54 | XX(tcp); 55 | } else { 56 | return {}; 57 | } 58 | #undef XX 59 | } 60 | 61 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/env/daemon.cpp: -------------------------------------------------------------------------------- 1 | #include "daemon.h" 2 | #include 3 | #include "flexy/util/config.h" 4 | 5 | namespace flexy { 6 | 7 | static auto g_logger = FLEXY_LOG_NAME("system"); 8 | static auto g_daemon_restart_interval = Config::Lookup( 9 | "daemon.restart_interval", 5, "deamon restart interval"); 10 | 11 | std::ostream& ProcessInfo::dump(std::ostream& os) const { 12 | os << "[ProcessInfo parent_id = " << parent_id << " main_id = " << main_id 13 | << " parent_start_time = " << TimeToStr(parent_start_time) 14 | << " main_start_time = " << TimeToStr(main_start_time) 15 | << " restart_count = " << restart_count; 16 | return os; 17 | } 18 | 19 | std::string ProcessInfo::toString() const { 20 | std::stringstream ss; 21 | dump(ss); 22 | return ss.str(); 23 | } 24 | 25 | static int real_start(int argc, char** argv, 26 | std::function main_cb) { 27 | return main_cb(argc, argv); 28 | } 29 | 30 | static int real_daemon(int argc, char** argv, 31 | std::function main_cb) { 32 | [[maybe_unused]] auto res = daemon(1, 0); 33 | ProcessInfoMrg::GetInstance().parent_id = getpid(); 34 | ProcessInfoMrg::GetInstance().parent_start_time = time(0); 35 | while (true) { 36 | pid_t pid = fork(); 37 | if (pid == 0) { 38 | ProcessInfoMrg::GetInstance().main_id = getpid(); 39 | ProcessInfoMrg::GetInstance().main_start_time = time(0); 40 | FLEXY_LOG_INFO(g_logger) << "Process start pid = " << getpid(); 41 | return real_start(argc, argv, main_cb); 42 | } else if (pid < 0) { 43 | FLEXY_LOG_ERROR(g_logger) 44 | << "fork fail pid = " << pid << "errno = " << errno 45 | << " errstr = " << strerror(errno); 46 | } else { 47 | int status = 0; 48 | waitpid(pid, &status, 0); 49 | if (status) { 50 | FLEXY_LOG_ERROR(g_logger) 51 | << "child crash pid = " << pid << " status = " << status; 52 | } else { 53 | FLEXY_LOG_INFO(g_logger) << "child finished pid = " << pid; 54 | break; 55 | } 56 | 57 | ProcessInfoMrg::GetInstance().restart_count++; 58 | sleep(g_daemon_restart_interval->getValue()); 59 | } 60 | } 61 | return 0; 62 | } 63 | 64 | int start_daemon(int argc, char** argv, 65 | std::function main_cb, 66 | bool is_daemon) { 67 | if (!is_daemon) { 68 | return real_start(argc, argv, main_cb); 69 | } 70 | return real_daemon(argc, argv, main_cb); 71 | } 72 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/env/daemon.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "flexy/util/singleton.h" 7 | 8 | namespace flexy { 9 | 10 | struct ProcessInfo { 11 | pid_t parent_id; 12 | pid_t main_id; 13 | uint64_t parent_start_time; 14 | uint64_t main_start_time; 15 | uint32_t restart_count = 0; 16 | 17 | std::ostream& dump(std::ostream& os) const; 18 | std::string toString() const; 19 | }; 20 | 21 | using ProcessInfoMrg = Singleton; 22 | 23 | int start_daemon(int argc, char** argv, 24 | std::function main_cb, 25 | bool is_daemon); 26 | 27 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/env/env.cpp: -------------------------------------------------------------------------------- 1 | #include "env.h" 2 | #include "flexy/util/file.h" 3 | #include "flexy/util/log.h" 4 | // #include 5 | #include 6 | #include 7 | #include 8 | 9 | // namespace fs = std::filesystem; 10 | 11 | namespace flexy { 12 | 13 | static auto g_logger = FLEXY_LOG_NAME("system"); 14 | 15 | bool Env::init(int argc, char** argv) { 16 | init_ = true; 17 | program_ = argv[0]; 18 | // exe_ = fs::current_path()/program_; // 环境变量时不对 19 | 20 | char path[1024] = {0}; 21 | [[maybe_unused]] auto res = readlink("/proc/self/exe", path, sizeof(path)); 22 | exe_ = path; 23 | 24 | auto pos = exe_.find_last_of("/") + 1; 25 | cwd_ = exe_.substr(0, pos); 26 | 27 | const char* now_key = nullptr; 28 | for (int i = 1; i < argc; ++i) { 29 | if (argv[i][0] == '-') { 30 | if (strlen(argv[i]) > 1) { 31 | if (now_key) { 32 | add(now_key, ""); 33 | } 34 | now_key = argv[i] + 1; 35 | } else { 36 | FLEXY_LOG_ERROR(g_logger) 37 | << "invalid arg idx = " << i << " val = " << argv[i]; 38 | return false; 39 | } 40 | } else { 41 | if (now_key) { 42 | add(now_key, argv[i]); 43 | now_key = nullptr; 44 | } else { 45 | FLEXY_LOG_ERROR(g_logger) 46 | << "invalid arg idx = " << i << " val = " << argv[i]; 47 | return false; 48 | } 49 | } 50 | } 51 | 52 | if (now_key) { 53 | add(now_key, ""); 54 | } 55 | 56 | return true; 57 | } 58 | 59 | void Env::add(const std::string& key, const std::string& val) { 60 | WRITELOCK(mutex_); 61 | args_.emplace(key, val); 62 | } 63 | 64 | bool Env::has(const std::string& key) const { 65 | READLOCK(mutex_); 66 | return args_.find(key) != args_.end(); 67 | } 68 | 69 | void Env::del(const std::string& key) { 70 | WRITELOCK(mutex_); 71 | args_.erase(key); 72 | } 73 | 74 | std::string Env::get(const std::string& key, 75 | const std::string& default_val) const { 76 | READLOCK(mutex_); 77 | auto it = args_.find(key); 78 | return it != args_.end() ? it->second : default_val; 79 | } 80 | 81 | void Env::addHelp(const std::string& key, const std::string& desc) { 82 | WRITELOCK(mutex_); 83 | helps_.emplace(key, desc); 84 | } 85 | 86 | void Env::removeHelp(const std::string& key) { 87 | WRITELOCK(mutex_); 88 | helps_.erase(key); 89 | } 90 | 91 | void Env::printHelp() const { 92 | READLOCK(mutex_); 93 | std::cout << "Usage: " << program_ << " [options]" << std::endl; 94 | for (const auto& [x, y] : helps_) { 95 | std::cout << std::setw(5) << "-" << x << " : " << y << std::endl; 96 | } 97 | } 98 | 99 | bool Env::setEnv(const std::string& key, const std::string& val) { 100 | return !setenv(key.c_str(), val.c_str(), 1); 101 | } 102 | 103 | bool Env::setEnv(const char* key, const char* val) { 104 | return !setenv(key, val, 1); 105 | } 106 | 107 | std::string Env::getEnv(const std::string& key, 108 | const std::string& default_value) const { 109 | const char* v = getenv(key.c_str()); 110 | if (v == nullptr) { 111 | return default_value; 112 | } 113 | return v; 114 | } 115 | 116 | std::string Env::getEnv(const char* key, 117 | const std::string& default_value) const { 118 | const char* v = getenv(key); 119 | if (v == nullptr) { 120 | return default_value; 121 | } 122 | return v; 123 | } 124 | 125 | std::string Env::getAbsolutePath(std::string_view path) const { 126 | if (path.empty()) { 127 | return "/"; 128 | } 129 | if (path[0] == '/') { 130 | return std::string(path); 131 | } 132 | return cwd_ + std::string(path); 133 | } 134 | 135 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/env/env.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "flexy/thread/mutex.h" 5 | #include "flexy/util/singleton.h" 6 | 7 | namespace flexy { 8 | 9 | class Env { 10 | public: 11 | bool init(int argc, char** argv); 12 | void add(const std::string& key, const std::string& val); 13 | bool has(const std::string& key) const; 14 | void del(const std::string& key); 15 | std::string get(const std::string& key, const std::string& val = "") const; 16 | 17 | void addHelp(const std::string& key, const std::string& desc); 18 | void removeHelp(const std::string& key); 19 | void printHelp() const; 20 | 21 | auto& getExe() const { return exe_; } 22 | auto& getCwd() const { return cwd_; } 23 | 24 | bool setEnv(const std::string& key, const std::string& val); 25 | bool setEnv(const char* key, const char* val); 26 | std::string getEnv(const std::string& key, 27 | const std::string& default_value = "") const; 28 | std::string getEnv(const char* key, 29 | const std::string& default_value = "") const; 30 | 31 | std::string getAbsolutePath(std::string_view path) const; 32 | 33 | private: 34 | mutable rw_mutex mutex_; 35 | std::unordered_map args_; 36 | std::unordered_map helps_; 37 | std::string program_; 38 | std::string exe_; 39 | std::string cwd_; 40 | 41 | public: 42 | bool init_ = false; 43 | }; 44 | 45 | using EnvMgr = Singleton; 46 | 47 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/env/signal.cpp: -------------------------------------------------------------------------------- 1 | #include "signal.h" 2 | #include "flexy/schedule/iomanager.h" 3 | #include 4 | // #include // 放到头文件中 5 | 6 | namespace flexy { 7 | 8 | namespace { 9 | // std::function handlers[65]; 10 | detail::__task handlers[65]; 11 | 12 | static void signal_handler(int sig) { 13 | auto iom = Scheduler::GetThis(); 14 | if (iom) { 15 | iom->async(handlers[sig]); 16 | } else { 17 | handlers[sig](); 18 | } 19 | } 20 | 21 | } 22 | 23 | void Signal::signal(int sig, detail::__task&& handler) { 24 | // handlers[sig] = std::move(handler.get()); 25 | handlers[sig] = std::move(handler); 26 | ::signal(sig, signal_handler); 27 | // struct sigaction sa; 28 | // memset(&sa, '\0', sizeof(sa)); 29 | // sa.sa_handler = signal_handler; 30 | // sa.sa_flags |= SA_RESTART; 31 | // sigfillset(&sa.sa_mask); 32 | // sigaction(sig, &sa, nullptr); 33 | } 34 | } -------------------------------------------------------------------------------- /flexy/env/signal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/util/task.h" 4 | #include 5 | 6 | namespace flexy { 7 | 8 | struct Signal { 9 | public: 10 | template 11 | static void signal(int sig, Args&&... args) { 12 | return signal(sig, detail::__task(std::forward(args)...)); 13 | } 14 | private: 15 | static void signal(int sig, detail::__task&& handler); 16 | }; 17 | 18 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/fiber.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/fiber/allocator.h" 4 | #include "flexy/fiber/condition_variable.h" 5 | #include "flexy/fiber/fiber.h" 6 | #include "flexy/fiber/mutex.h" 7 | #include "flexy/fiber/this_fiber.h" 8 | -------------------------------------------------------------------------------- /flexy/fiber/allocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | // #include 6 | 7 | class MallocStackAllocator { 8 | public: 9 | static void* Alloc(size_t size) { return malloc(size); } 10 | static void Dealloc(void* vp, size_t size) { return free(vp); } 11 | }; 12 | 13 | class StdStackAllocator { 14 | public: 15 | static void* Alloc(size_t size) { return a.allocate(size); } 16 | static void Dealloc(void* vp, size_t size) { 17 | a.deallocate((char*)vp, size); 18 | } 19 | 20 | private: 21 | static std::allocator a; 22 | }; 23 | 24 | // static std::pmr::polymorphic_allocator a; 25 | 26 | // class PolymorphicAllocator { 27 | // public: 28 | // static void* Alloc(size_t size) { 29 | // return a.allocate(size); 30 | // } 31 | // static void Dealloc(void* vp, size_t size) { 32 | // a.deallocate((char*)vp, size); 33 | // } 34 | // private: 35 | // // static std::pmr::polymorphic_allocator 36 | // a(std::pmr::synchronized_pool_resource); 37 | // }; -------------------------------------------------------------------------------- /flexy/fiber/condition_variable.cpp: -------------------------------------------------------------------------------- 1 | #include "condition_variable.h" 2 | #include "flexy/schedule/scheduler.h" 3 | #include "flexy/util/macro.h" 4 | 5 | namespace flexy::fiber { 6 | 7 | void condition_variable::notify_all() noexcept { 8 | decltype(waiters_) waiters; 9 | { 10 | LOCK_GUARD(mutex_); 11 | waiters.swap(waiters_); 12 | } 13 | for (auto& [scheduler, fiber] : waiters) { 14 | scheduler->async(std::move(fiber)); 15 | } 16 | } 17 | 18 | void condition_variable::notify_one() noexcept { 19 | std::pair waiter; 20 | { 21 | LOCK_GUARD(mutex_); 22 | if (waiters_.empty()) { 23 | return; 24 | } 25 | waiter = std::move(waiters_.front()); 26 | waiters_.pop_front(); 27 | } 28 | waiter.first->async(std::move(waiter.second)); 29 | } 30 | 31 | // 两段相同的代码 暂时没想到协程锁和条件表量有什么可以优化的地方 32 | void condition_variable::wait(unique_lock& __lock) noexcept { 33 | FLEXY_ASSERT(Scheduler::GetThis()); 34 | FLEXY_ASSERT2(Fiber::GetFiberId() != 0, "Main Fiber cannot wait"); 35 | { 36 | LOCK_GUARD(mutex_); 37 | waiters_.emplace_back(Scheduler::GetThis(), Fiber::GetThis()); 38 | } 39 | // __lock.unlock(); 40 | // Fiber::Yield(); 41 | Fiber::GetThis()->yield_callback( 42 | [&__lock]() { // 保证 yield 和 unlock的原子性 43 | __lock.unlock(); 44 | }); 45 | 46 | __lock.lock(); 47 | } 48 | 49 | void condition_variable::wait(unique_lock& __lock) noexcept { 50 | FLEXY_ASSERT(Scheduler::GetThis()); 51 | FLEXY_ASSERT2(Fiber::GetFiberId() != 0, "Main Fiber cannot wait"); 52 | { 53 | LOCK_GUARD(mutex_); 54 | waiters_.emplace_back(Scheduler::GetThis(), Fiber::GetThis()); 55 | } 56 | // __lock.unlock(); 57 | // Fiber::Yield(); 58 | Fiber::GetThis()->yield_callback( 59 | [&__lock]() { // 保证 yield 和 unlock的原子性 60 | __lock.unlock(); 61 | }); 62 | 63 | __lock.lock(); 64 | } 65 | 66 | } // namespace flexy::fiber -------------------------------------------------------------------------------- /flexy/fiber/condition_variable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/util/noncopyable.h" 4 | // #include "flexy/thread/mutex.h" 5 | #include 6 | #include 7 | #include "flexy/fiber/mutex.h" 8 | 9 | namespace flexy { 10 | 11 | class Scheduler; 12 | 13 | } 14 | 15 | namespace flexy::fiber { 16 | 17 | class condition_variable : noncopyable { 18 | public: 19 | condition_variable() noexcept = default; 20 | ~condition_variable() noexcept = default; 21 | 22 | void notify_one() noexcept; 23 | void notify_all() noexcept; 24 | // wait std::mutex 25 | void wait(unique_lock& __lock) noexcept; 26 | // wait fiber::mutex 27 | void wait(unique_lock& __lock) noexcept; 28 | 29 | template 30 | void wait(unique_lock<_Mutex>& __lock, _Predicate __p) noexcept { 31 | while (!__p()) { 32 | wait(__lock); 33 | } 34 | } 35 | 36 | private: 37 | mutable Spinlock mutex_; 38 | std::deque> waiters_; 39 | }; 40 | 41 | } // namespace flexy::fiber -------------------------------------------------------------------------------- /flexy/fiber/fiber.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/util/memory.h" 4 | #include "flexy/util/task.h" 5 | 6 | typedef void* fcontext_t; 7 | 8 | struct transfer_t { 9 | fcontext_t fctx; 10 | void* data; 11 | }; 12 | 13 | extern "C" { 14 | 15 | transfer_t _fl_jump_fcontext(fcontext_t const to, void* vp); 16 | fcontext_t _fl_make_fcontext(void* sp, std::size_t size, 17 | void (*fn)(transfer_t)); 18 | transfer_t _fl_ontop_fcontext(fcontext_t const to, void* vp, 19 | transfer_t (*fn)(transfer_t)); 20 | } 21 | 22 | namespace flexy { 23 | 24 | class Fiber : public std::enable_shared_from_this { 25 | public: 26 | using ptr = std::shared_ptr; 27 | friend class Scheduler; 28 | friend transfer_t ontop_callback(transfer_t); 29 | 30 | template 31 | friend std::shared_ptr fiber_make_shared(First&& first, 32 | Args&&... args); 33 | 34 | enum State { 35 | READY, // 就绪状态 36 | EXEC, // 执行状态 37 | TERM, // 结束状态 38 | EXCEPT, // 异常状态 39 | }; 40 | 41 | private: 42 | explicit Fiber(std::size_t stacksize, 43 | detail::__task&& cb); // 子协程构造函数 44 | explicit Fiber(); // 主协程构造函数 45 | public: 46 | ~Fiber(); 47 | 48 | template >> 50 | void reset(Args&&... args) { 51 | return reset(detail::__task(std::forward(args)...)); 52 | } 53 | 54 | template >> 56 | void yield_callback(Args&&... args) { 57 | return yield_callback(detail::__task(std::forward(args)...)); 58 | } 59 | 60 | void yield(); // 让出执行权 61 | void resume(); // 进入协程 62 | 63 | uint64_t getId() const { return id_; } // 返回协程id 64 | State getState() const { return state_; } // 返回协程状态 65 | 66 | static Fiber::ptr GetThis(); // 返回当前协程 67 | static void Yield() { return GetThis()->yield(); } // 让出当前协程的执行权 68 | static uint64_t TotalFibers(); // 返回当前协程的总数量 69 | static void MainFunc(transfer_t); // 协程执行函数体 70 | static uint64_t GetFiberId(); // 获得当前协程id 71 | private: 72 | void reset(detail::__task&& cb); // 重置协程函数 并重置协程状态 73 | void yield_callback(detail::__task&& cb); // 让出执行权后回调一个函数 74 | void _M_return() const; // 协程返回 75 | private: 76 | uint64_t id_ = 0; // 协程id 77 | uint32_t stacksize_ = 0; // 协程栈大小 78 | State state_ = READY; // 协程状态 79 | fcontext_t ctx_; // 协程上下文 80 | detail::__task cb_; // 协程执行函数 81 | char stack_[]; // 协程栈首指针 82 | }; 83 | 84 | template 85 | std::shared_ptr fiber_make_shared(First&& first, Args&&... args) { 86 | extern Fiber* MallocFiber(size_t & stacksize); 87 | extern std::shared_ptr FreeFiber(Fiber * fiber); 88 | 89 | if constexpr (std::is_integral_v) { 90 | size_t stacksize = first > 0 ? first : 0; 91 | Fiber* fiber = MallocFiber(stacksize); 92 | new (fiber) 93 | Fiber(stacksize, detail::__task(std::forward(args)...)); 94 | 95 | return FreeFiber(fiber); 96 | } else { 97 | size_t stacksize = 0; 98 | Fiber* fiber = MallocFiber(stacksize); 99 | new (fiber) 100 | Fiber(stacksize, detail::__task(std::forward(first), 101 | std::forward(args)...)); 102 | 103 | return FreeFiber(fiber); 104 | } 105 | } 106 | 107 | namespace detail { 108 | 109 | template 110 | struct is_fiber_ptr { 111 | const static bool value = false; 112 | }; 113 | 114 | template <> 115 | struct is_fiber_ptr { 116 | const static bool value = true; 117 | }; 118 | 119 | } // namespace detail 120 | 121 | template 122 | constexpr bool is_fiber_ptr_v = detail::is_fiber_ptr>::value; 123 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/fiber/internal/x86_64/jump_fcontext.S: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Oliver Kowalke 2009. 3 | Distributed under the Boost Software License, Version 1.0. 4 | (See accompanying file LICENSE_1_0.txt or copy at 5 | http://www.boost.org/LICENSE_1_0.txt) 6 | */ 7 | 8 | /**************************************************************************************** 9 | * * 10 | * ---------------------------------------------------------------------------------- * 11 | * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * 12 | * ---------------------------------------------------------------------------------- * 13 | * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * 14 | * ---------------------------------------------------------------------------------- * 15 | * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * 16 | * ---------------------------------------------------------------------------------- * 17 | * ---------------------------------------------------------------------------------- * 18 | * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * 19 | * ---------------------------------------------------------------------------------- * 20 | * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * 21 | * ---------------------------------------------------------------------------------- * 22 | * | R15 | RBX | RBP | RIP | * 23 | * ---------------------------------------------------------------------------------- * 24 | * * 25 | ****************************************************************************************/ 26 | 27 | .text 28 | .globl _fl_jump_fcontext 29 | .type _fl_jump_fcontext,@function 30 | .align 16 31 | _fl_jump_fcontext: 32 | leaq -0x38(%rsp), %rsp /* prepare stack */ 33 | 34 | #if !defined(ASYNC_SIMPLE_USE_TSX) 35 | stmxcsr (%rsp) /* save MMX control- and status-word */ 36 | fnstcw 0x4(%rsp) /* save x87 control-word */ 37 | #endif 38 | 39 | movq %r12, 0x8(%rsp) /* save R12 */ 40 | movq %r13, 0x10(%rsp) /* save R13 */ 41 | movq %r14, 0x18(%rsp) /* save R14 */ 42 | movq %r15, 0x20(%rsp) /* save R15 */ 43 | movq %rbx, 0x28(%rsp) /* save RBX */ 44 | movq %rbp, 0x30(%rsp) /* save RBP */ 45 | 46 | /* store RSP (pointing to context-data) in RAX */ 47 | movq %rsp, %rax 48 | 49 | /* restore RSP (pointing to context-data) from RDI */ 50 | movq %rdi, %rsp 51 | 52 | movq 0x38(%rsp), %r8 /* restore return-address */ 53 | 54 | #if !defined(ASYNC_SIMPLE_USE_TSX) 55 | ldmxcsr (%rsp) /* restore MMX control- and status-word */ 56 | fldcw 0x4(%rsp) /* restore x87 control-word */ 57 | #endif 58 | 59 | movq 0x8(%rsp), %r12 /* restore R12 */ 60 | movq 0x10(%rsp), %r13 /* restore R13 */ 61 | movq 0x18(%rsp), %r14 /* restore R14 */ 62 | movq 0x20(%rsp), %r15 /* restore R15 */ 63 | movq 0x28(%rsp), %rbx /* restore RBX */ 64 | movq 0x30(%rsp), %rbp /* restore RBP */ 65 | 66 | leaq 0x40(%rsp), %rsp /* prepare stack */ 67 | 68 | /* return transfer_t from jump */ 69 | /* RAX == fctx, RDX == data */ 70 | movq %rsi, %rdx 71 | /* pass transfer_t as first arg in context function */ 72 | /* RDI == fctx, RSI == data */ 73 | movq %rax, %rdi 74 | 75 | /* indirect jump to context */ 76 | jmp *%r8 77 | .size _fl_jump_fcontext,.-_fl_jump_fcontext 78 | 79 | /* Mark that we don't need executable stack. */ 80 | .section .note.GNU-stack,"",%progbits 81 | -------------------------------------------------------------------------------- /flexy/fiber/internal/x86_64/make_fcontext.S: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Oliver Kowalke 2009. 3 | Distributed under the Boost Software License, Version 1.0. 4 | (See accompanying file LICENSE_1_0.txt or copy at 5 | http://www.boost.org/LICENSE_1_0.txt) 6 | */ 7 | 8 | /**************************************************************************************** 9 | * * 10 | * ---------------------------------------------------------------------------------- * 11 | * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * 12 | * ---------------------------------------------------------------------------------- * 13 | * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * 14 | * ---------------------------------------------------------------------------------- * 15 | * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * 16 | * ---------------------------------------------------------------------------------- * 17 | * ---------------------------------------------------------------------------------- * 18 | * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * 19 | * ---------------------------------------------------------------------------------- * 20 | * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * 21 | * ---------------------------------------------------------------------------------- * 22 | * | R15 | RBX | RBP | RIP | * 23 | * ---------------------------------------------------------------------------------- * 24 | * * 25 | ****************************************************************************************/ 26 | 27 | .text 28 | .globl _fl_make_fcontext 29 | .type _fl_make_fcontext,@function 30 | .align 16 31 | _fl_make_fcontext: 32 | /* first arg of make_fcontext() == top of context-stack */ 33 | movq %rdi, %rax 34 | 35 | /* shift address in RAX to lower 16 byte boundary */ 36 | andq $-16, %rax 37 | 38 | /* reserve space for context-data on context-stack */ 39 | /* on context-function entry: (RSP -0x8) % 16 == 0 */ 40 | leaq -0x40(%rax), %rax 41 | 42 | /* third arg of make_fcontext() == address of context-function */ 43 | /* stored in RBX */ 44 | movq %rdx, 0x28(%rax) 45 | 46 | /* save MMX control- and status-word */ 47 | stmxcsr (%rax) 48 | /* save x87 control-word */ 49 | fnstcw 0x4(%rax) 50 | 51 | /* compute abs address of label trampoline */ 52 | leaq trampoline(%rip), %rcx 53 | /* save address of trampoline as return-address for context-function */ 54 | /* will be entered after calling jump_fcontext() first time */ 55 | movq %rcx, 0x38(%rax) 56 | 57 | /* compute abs address of label finish */ 58 | leaq finish(%rip), %rcx 59 | /* save address of finish as return-address for context-function */ 60 | /* will be entered after context-function returns */ 61 | movq %rcx, 0x30(%rax) 62 | 63 | ret /* return pointer to context-data */ 64 | 65 | trampoline: 66 | /* store return address on stack */ 67 | /* fix stack alignment */ 68 | push %rbp 69 | /* jump to context-function */ 70 | jmp *%rbx 71 | 72 | finish: 73 | /* exit code is zero */ 74 | xorq %rdi, %rdi 75 | /* exit application */ 76 | call _exit@PLT 77 | hlt 78 | .size _fl_make_fcontext,.-_fl_make_fcontext 79 | 80 | /* Mark that we don't need executable stack. */ 81 | .section .note.GNU-stack,"",%progbits 82 | -------------------------------------------------------------------------------- /flexy/fiber/internal/x86_64/ontop_fcontext.S: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright Oliver Kowalke 2009. 3 | Distributed under the Boost Software License, Version 1.0. 4 | (See accompanying file LICENSE_1_0.txt or copy at 5 | http://www.boost.org/LICENSE_1_0.txt) 6 | */ 7 | 8 | /**************************************************************************************** 9 | * * 10 | * ---------------------------------------------------------------------------------- * 11 | * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | * 12 | * ---------------------------------------------------------------------------------- * 13 | * | 0x0 | 0x4 | 0x8 | 0xc | 0x10 | 0x14 | 0x18 | 0x1c | * 14 | * ---------------------------------------------------------------------------------- * 15 | * | fc_mxcsr|fc_x87_cw| R12 | R13 | R14 | * 16 | * ---------------------------------------------------------------------------------- * 17 | * ---------------------------------------------------------------------------------- * 18 | * | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | * 19 | * ---------------------------------------------------------------------------------- * 20 | * | 0x20 | 0x24 | 0x28 | 0x2c | 0x30 | 0x34 | 0x38 | 0x3c | * 21 | * ---------------------------------------------------------------------------------- * 22 | * | R15 | RBX | RBP | RIP | * 23 | * ---------------------------------------------------------------------------------- * 24 | * * 25 | ****************************************************************************************/ 26 | 27 | .text 28 | .globl _fl_ontop_fcontext 29 | .type _fl_ontop_fcontext,@function 30 | .align 16 31 | _fl_ontop_fcontext: 32 | /* preserve ontop-function in R8 */ 33 | movq %rdx, %r8 34 | 35 | leaq -0x38(%rsp), %rsp /* prepare stack */ 36 | 37 | #if !defined(ASYNC_SIMPLE_USE_TSX) 38 | stmxcsr (%rsp) /* save MMX control- and status-word */ 39 | fnstcw 0x4(%rsp) /* save x87 control-word */ 40 | #endif 41 | 42 | movq %r12, 0x8(%rsp) /* save R12 */ 43 | movq %r13, 0x10(%rsp) /* save R13 */ 44 | movq %r14, 0x18(%rsp) /* save R14 */ 45 | movq %r15, 0x20(%rsp) /* save R15 */ 46 | movq %rbx, 0x28(%rsp) /* save RBX */ 47 | movq %rbp, 0x30(%rsp) /* save RBP */ 48 | 49 | /* store RSP (pointing to context-data) in RAX */ 50 | movq %rsp, %rax 51 | 52 | /* restore RSP (pointing to context-data) from RDI */ 53 | movq %rdi, %rsp 54 | 55 | #if !defined(ASYNC_SIMPLE_USE_TSX) 56 | ldmxcsr (%rsp) /* restore MMX control- and status-word */ 57 | fldcw 0x4(%rsp) /* restore x87 control-word */ 58 | #endif 59 | 60 | movq 0x8(%rsp), %r12 /* restore R12 */ 61 | movq 0x10(%rsp), %r13 /* restore R13 */ 62 | movq 0x18(%rsp), %r14 /* restore R14 */ 63 | movq 0x20(%rsp), %r15 /* restore R15 */ 64 | movq 0x28(%rsp), %rbx /* restore RBX */ 65 | movq 0x30(%rsp), %rbp /* restore RBP */ 66 | 67 | leaq 0x38(%rsp), %rsp /* prepare stack */ 68 | 69 | /* return transfer_t from jump */ 70 | /* RAX == fctx, RDX == data */ 71 | movq %rsi, %rdx 72 | /* pass transfer_t as first arg in context function */ 73 | /* RDI == fctx, RSI == data */ 74 | movq %rax, %rdi 75 | 76 | /* keep return-address on stack */ 77 | 78 | /* indirect jump to context */ 79 | jmp *%r8 80 | .size _fl_ontop_fcontext,.-_fl_ontop_fcontext 81 | 82 | /* Mark that we don't need executable stack. */ 83 | .section .note.GNU-stack,"",%progbits 84 | -------------------------------------------------------------------------------- /flexy/fiber/mutex.cpp: -------------------------------------------------------------------------------- 1 | #include "flexy/fiber/mutex.h" 2 | #include "flexy/schedule/scheduler.h" 3 | #include "flexy/util/macro.h" 4 | 5 | namespace flexy::fiber { 6 | 7 | mutex::~mutex() { FLEXY_ASSERT(locked_ == false); } 8 | 9 | void mutex::lock() { 10 | FLEXY_ASSERT(Scheduler::GetThis()); 11 | FLEXY_ASSERT2(Fiber::GetFiberId() != 0, "Main Fiber cannot wait"); 12 | { 13 | LOCK_GUARD(mutex_); 14 | if (locked_) { 15 | waiters_.emplace_back(Scheduler::GetThis(), Fiber::GetThis()); 16 | } else { 17 | locked_ = true; 18 | return; 19 | } 20 | } 21 | Fiber::Yield(); 22 | } 23 | 24 | void mutex::unlock() { 25 | std::pair waiter; 26 | { 27 | LOCK_GUARD(mutex_); 28 | FLEXY_ASSERT(locked_); 29 | if (waiters_.empty()) { 30 | locked_ = false; 31 | return; 32 | } else { 33 | waiter = std::move(waiters_.front()); 34 | waiters_.pop_front(); 35 | } 36 | } 37 | waiter.first->async(std::move(waiter.second)); 38 | } 39 | 40 | } // namespace flexy::fiber -------------------------------------------------------------------------------- /flexy/fiber/mutex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "flexy/fiber/fiber.h" 6 | #include "flexy/thread/mutex.h" 7 | #include "flexy/util/noncopyable.h" 8 | 9 | namespace flexy { 10 | 11 | class Scheduler; 12 | 13 | } // namespace flexy 14 | 15 | namespace flexy::fiber { 16 | 17 | // Analogous to `std::mutex`, but it's for fiber. 18 | class mutex : noncopyable { 19 | public: 20 | mutex() = default; 21 | ~mutex(); 22 | void lock(); 23 | void unlock(); 24 | 25 | private: 26 | bool locked_ = false; 27 | mutable Spinlock mutex_; 28 | std::deque> waiters_; 29 | }; 30 | 31 | } // namespace flexy::fiber -------------------------------------------------------------------------------- /flexy/fiber/this_fiber.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "fiber.h" 3 | #include "flexy/schedule/iomanager.h" 4 | #include "flexy/util/macro.h" 5 | 6 | namespace flexy::this_fiber { 7 | 8 | inline void yield() { return Fiber::GetThis()->yield(); } 9 | inline uint64_t get_id() noexcept { return Fiber::GetThis()->getId(); } 10 | 11 | template >> 13 | inline void yield_callback(Args&&... __args) { 14 | return Fiber::GetThis()->yield_callback(std::forward(__args)...); 15 | } 16 | 17 | template 18 | inline void sleep_for( 19 | const std::chrono::duration& sleep_duration) { 20 | auto iom = flexy::IOManager::GetThis(); 21 | FLEXY_ASSERT(iom); 22 | uint64_t ms = 23 | std::chrono::duration_cast(sleep_duration) 24 | .count(); 25 | iom->addTimer(ms, [iom, fiber = flexy::Fiber::GetThis()]() { 26 | iom->async(std::move(fiber)); 27 | }); 28 | yield(); 29 | } 30 | 31 | template 32 | inline void sleep_until( 33 | const std::chrono::time_point& sleep_time) { 34 | return sleep_for(sleep_time - Clock::now()); 35 | } 36 | 37 | } // namespace flexy::this_fiber -------------------------------------------------------------------------------- /flexy/flexy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/db.h" 4 | #include "flexy/env.h" 5 | #include "flexy/fiber.h" 6 | #include "flexy/http.h" 7 | #include "flexy/net.h" 8 | #include "flexy/schedule.h" 9 | #include "flexy/stream.h" 10 | #include "flexy/thread.h" 11 | #include "flexy/util.h" 12 | -------------------------------------------------------------------------------- /flexy/http.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "http/http.h" 4 | #include "http/http11_common.h" 5 | #include "http/http11_parser.h" 6 | #include "http/httpclient_parser.h" 7 | #include "http/http_parser.h" 8 | #include "http/http_session.h" 9 | #include "http/http_server.h" -------------------------------------------------------------------------------- /flexy/http/http11_common.h: -------------------------------------------------------------------------------- 1 | #ifndef _http11_common_h 2 | #define _http11_common_h 3 | 4 | #include 5 | 6 | typedef void (*element_cb)(void *data, const char *at, size_t length); 7 | typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen); 8 | 9 | #endif -------------------------------------------------------------------------------- /flexy/http/http11_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef http11_parser_h 2 | #define http11_parser_h 3 | 4 | #include "http11_common.h" 5 | 6 | typedef struct http_parser { 7 | int cs; 8 | size_t body_start; 9 | int content_len; 10 | size_t nread; 11 | size_t mark; 12 | size_t field_start; 13 | size_t field_len; 14 | size_t query_start; 15 | int xml_sent; 16 | int json_sent; 17 | 18 | void *data; 19 | 20 | int uri_relaxed; 21 | field_cb http_field; 22 | element_cb request_method; 23 | element_cb request_uri; 24 | element_cb fragment; 25 | element_cb request_path; 26 | element_cb query_string; 27 | element_cb http_version; 28 | element_cb header_done; 29 | 30 | } http_parser; 31 | 32 | int http_parser_init(http_parser *parser); 33 | int http_parser_finish(http_parser *parser); 34 | size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off); 35 | int http_parser_has_error(http_parser *parser); 36 | int http_parser_is_finished(http_parser *parser); 37 | 38 | #define http_parser_nread(parser) (parser)->nread 39 | 40 | #endif -------------------------------------------------------------------------------- /flexy/http/http_parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "http.h" 4 | #include "http11_parser.h" 5 | #include "httpclient_parser.h" 6 | 7 | namespace flexy::http { 8 | 9 | class HttpRequestParser { 10 | public: 11 | HttpRequestParser(); 12 | 13 | size_t execute(char* data, size_t len); 14 | int isFinished(); 15 | int hasError(); 16 | void setError(int v) { error_ = v; } 17 | auto& getData() { return data_; } 18 | uint64_t getContentLength() const; 19 | auto& getParser() const { return parser_; } 20 | 21 | public: 22 | static uint64_t GetHttpRequestBufferSize(); 23 | static uint64_t GetHttpRequestMaxBodySize(); 24 | private: 25 | http_parser parser_; 26 | HttpRequest::ptr data_; 27 | int error_; /* 28 | * 1000 : invalid method 29 | * 1001 : invalid version 30 | * 1002 : invalid field 31 | */ 32 | }; 33 | 34 | class HttpResponseParser { 35 | public: 36 | HttpResponseParser(); 37 | size_t execute(char* data, size_t len, bool chunck); 38 | int isFinished(); 39 | int hasError(); 40 | void setError(int v) { error_ = v; } 41 | auto& getData() { return data_; } 42 | uint64_t getContentLength() const; 43 | 44 | auto& getParser() const { return parser_; } 45 | 46 | static uint64_t GetHttpResponseBufferSize(); 47 | static uint64_t GetHttpResponseMaxBodySize(); 48 | private: 49 | httpclient_parser parser_; 50 | HttpResponse::ptr data_; 51 | int error_; 52 | }; 53 | 54 | } // namespace flexy http -------------------------------------------------------------------------------- /flexy/http/http_server.cpp: -------------------------------------------------------------------------------- 1 | #include "http_server.h" 2 | #include "http_session.h" 3 | #include "flexy/util/log.h" 4 | 5 | namespace flexy::http { 6 | 7 | static auto g_logger = FLEXY_LOG_NAME("system"); 8 | 9 | void HttpServer::handleClient(const Socket::ptr& client) { 10 | // HttpSession::ptr session(new HttpSession(client)); 11 | auto session = std::make_shared(client); 12 | do { 13 | auto&& req = session->recvRequest(); 14 | if (!req) { 15 | FLEXY_LOG_DEBUG(g_logger) << "recv http request fail, error = " 16 | << errno << " errstr = " << strerror(errno) << " client = " 17 | << *client; 18 | break; 19 | } 20 | auto rsp = std::make_unique( 21 | req->getVersion(), req->isClose() || !isKeepalive_); 22 | dispatch_->handle(req, rsp, session); 23 | session->sendResponse(rsp); 24 | } while (isKeepalive_); 25 | 26 | session->close(); 27 | } 28 | 29 | HttpServer::HttpServer(bool keepalive, IOManager* worker, IOManager* io_worker, 30 | IOManager* accept_worker) 31 | : TcpServer(worker, io_worker, accept_worker), 32 | isKeepalive_(keepalive), 33 | dispatch_(std::make_shared()) { 34 | type_ = "http"; 35 | } 36 | 37 | } // namespace flexy http -------------------------------------------------------------------------------- /flexy/http/http_server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/net/tcp_server.h" 4 | #include "servlet.h" 5 | 6 | namespace flexy::http { 7 | 8 | class HttpServer : public TcpServer { 9 | public: 10 | HttpServer(bool keepalive = false, IOManager* worker = IOManager::GetThis(), 11 | IOManager* io_worker = IOManager::GetThis(), 12 | IOManager* accept_worker = IOManager::GetThis()); 13 | auto& getServletDispatch() const { return dispatch_; } 14 | bool isKeepalive() const { return isKeepalive_; } 15 | protected: 16 | void handleClient(const Socket::ptr& client) override; 17 | private: 18 | bool isKeepalive_; 19 | ServletDispatch::ptr dispatch_; 20 | }; 21 | 22 | } // namespace flexy http -------------------------------------------------------------------------------- /flexy/http/http_session.cpp: -------------------------------------------------------------------------------- 1 | #include "http_session.h" 2 | #include "http_parser.h" 3 | 4 | namespace flexy::http { 5 | 6 | HttpSession::HttpSession(const Socket::ptr& sock, bool owner) 7 | : SockStream(sock, owner) { 8 | 9 | } 10 | 11 | HttpRequest::ptr HttpSession::recvRequest() { 12 | HttpRequestParser parser; 13 | uint64_t buff_size = HttpRequestParser::GetHttpRequestBufferSize(); 14 | std::unique_ptr buffer(new char[buff_size]); 15 | 16 | char* data = buffer.get(); 17 | int offset = 0; 18 | 19 | while (true) { 20 | int len = read(data + offset, buff_size - offset); 21 | if (len <= 0) { 22 | goto error; 23 | } 24 | len += offset; 25 | size_t nparse = parser.execute(data, len); 26 | if (parser.hasError()) { 27 | goto error; 28 | } 29 | offset = len - nparse; 30 | if (offset == (int)buff_size) { 31 | goto error; 32 | } 33 | if (parser.isFinished()) { 34 | goto body; 35 | } 36 | } 37 | error: 38 | close(); 39 | return nullptr; 40 | 41 | body: 42 | int64_t length = parser.getContentLength(); 43 | if (length > 0) { 44 | std::string body; 45 | body.resize(length); 46 | int len = std::min(length, (int64_t)offset); 47 | memcpy(body.data(), data, len); 48 | if (length > 0) { 49 | if (readFixSize(&body[len], length) <= 0) { 50 | goto error; 51 | } 52 | } 53 | parser.getData()->setBody(body); 54 | } 55 | parser.getData()->init(); 56 | return std::move(parser.getData()); 57 | } 58 | 59 | int HttpSession::sendResponse(const HttpResponse::ptr& rsp) { 60 | std::stringstream ss; 61 | ss << *rsp; 62 | std::string data = ss.str(); 63 | return writeFixSize(data.c_str(), data.size()); 64 | } 65 | 66 | } // namespace flexy http -------------------------------------------------------------------------------- /flexy/http/http_session.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/stream/socket_stream.h" 4 | #include "http.h" 5 | 6 | namespace flexy::http { 7 | 8 | class HttpSession : public SockStream { 9 | public: 10 | using ptr = std::shared_ptr; 11 | HttpSession(const Socket::ptr& sock, bool owner = true); 12 | HttpRequest::ptr recvRequest(); 13 | int sendResponse(const HttpResponse::ptr& rsp); 14 | }; 15 | 16 | } // namespace flexy::http 17 | -------------------------------------------------------------------------------- /flexy/http/httpclient_parser.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (c) 2010, Zed A. Shaw and Mongrel2 Project Contributors. 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * * Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * * Neither the name of the Mongrel2 Project, Zed A. Shaw, nor the names 18 | * of its contributors may be used to endorse or promote products 19 | * derived from this software without specific prior written 20 | * permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 23 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 24 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 26 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 29 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 30 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 31 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 32 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | #ifndef httpclient_parser_h 36 | #define httpclient_parser_h 37 | 38 | #include "http11_common.h" 39 | 40 | typedef struct httpclient_parser { 41 | int cs; 42 | size_t body_start; 43 | int content_len; 44 | int status; 45 | int chunked; 46 | int chunks_done; 47 | int close; 48 | size_t nread; 49 | size_t mark; 50 | size_t field_start; 51 | size_t field_len; 52 | 53 | void *data; 54 | 55 | field_cb http_field; 56 | element_cb reason_phrase; 57 | element_cb status_code; 58 | element_cb chunk_size; 59 | element_cb http_version; 60 | element_cb header_done; 61 | element_cb last_chunk; 62 | 63 | 64 | } httpclient_parser; 65 | 66 | int httpclient_parser_init(httpclient_parser *parser); 67 | int httpclient_parser_finish(httpclient_parser *parser); 68 | int httpclient_parser_execute(httpclient_parser *parser, const char *data, size_t len, size_t off); 69 | int httpclient_parser_has_error(httpclient_parser *parser); 70 | int httpclient_parser_is_finished(httpclient_parser *parser); 71 | 72 | #define httpclient_parser_nread(parser) (parser)->nread 73 | 74 | #endif -------------------------------------------------------------------------------- /flexy/http/servlet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "http_session.h" 5 | #include "flexy/thread/mutex.h" 6 | 7 | namespace flexy::http { 8 | 9 | class Servlet { 10 | public: 11 | using ptr = std::shared_ptr; 12 | Servlet(std::string_view name) : name_(name) {} 13 | virtual ~Servlet() {} 14 | virtual int32_t handle(const HttpRequest::ptr& request, 15 | const HttpResponse::ptr& response, 16 | const SockStream::ptr& sesion) = 0; 17 | auto& getName() const { return name_; } 18 | 19 | protected: 20 | std::string name_; 21 | }; 22 | 23 | class FuncionServlet : public Servlet { 24 | public: 25 | using callback = 26 | std::function; 28 | FuncionServlet(callback&& cb); 29 | FuncionServlet(const callback& cb); 30 | int32_t handle(const HttpRequest::ptr& request, 31 | const HttpResponse::ptr& response, 32 | const SockStream::ptr& session) override; 33 | 34 | private: 35 | callback cb_; 36 | }; 37 | 38 | class ServletDispatch : public Servlet { 39 | public: 40 | using ptr = std::shared_ptr; 41 | ServletDispatch(); 42 | int32_t handle(const HttpRequest::ptr& request, 43 | const HttpResponse::ptr& response, 44 | const SockStream::ptr& session) override; 45 | void addServlet(const std::string& uri, const Servlet::ptr& slt); 46 | void addServlet(const std::string& uri, FuncionServlet::callback&& cb); 47 | void addGlobServlet(const std::string& uri, const Servlet::ptr& slt); 48 | void addGlobServlet(const std::string& uri, FuncionServlet::callback&& slt); 49 | 50 | void delServlet(const std::string& uri); 51 | void delGlobServlet(const std::string& uri); 52 | 53 | auto& getDefault() const { return default_; } 54 | void setDefault(Servlet::ptr&& v) { default_ = std::move(v); } 55 | 56 | Servlet::ptr getServlet(const std::string& uri) const; 57 | Servlet::ptr getGlobServlet(const std::string& uri) const; 58 | Servlet::ptr getMatchedServlet(const std::string& uri) const; 59 | 60 | private: 61 | mutable rw_mutex mutex_; 62 | std::unordered_map datas_; 63 | std::vector> globs_; 64 | Servlet::ptr default_; 65 | }; 66 | 67 | class NotFoundServlet : public Servlet { 68 | public: 69 | NotFoundServlet(); 70 | int32_t handle(const HttpRequest::ptr& request, 71 | const HttpResponse::ptr& response, 72 | const SockStream::ptr& session) override; 73 | }; 74 | } // namespace flexy http -------------------------------------------------------------------------------- /flexy/http/ws_server.cpp: -------------------------------------------------------------------------------- 1 | #include "ws_server.h" 2 | #include "flexy/util/log.h" 3 | 4 | namespace flexy::http { 5 | 6 | static auto g_logger = FLEXY_LOG_NAME("system"); 7 | 8 | WSServer::WSServer(IOManager* worker, IOManager* io_worker, 9 | IOManager* accept_worker) 10 | : TcpServer(worker, io_worker, accept_worker), 11 | dispatch_(std::make_shared()) { 12 | type_ = "websocket_server"; 13 | } 14 | 15 | void WSServer::handleClient(const Socket::ptr& client) { 16 | FLEXY_LOG_DEBUG(g_logger) << *client; 17 | // WSSession::ptr session(new WSSession(client)); 18 | auto session = std::make_shared(client); 19 | do { 20 | auto header = session->handleShake(); 21 | if (!header) { 22 | FLEXY_LOG_DEBUG(g_logger) << "handleShake error"; 23 | break; 24 | } 25 | auto servlet = dispatch_->getWSServlet(header->getPath()); 26 | if (!servlet) { 27 | FLEXY_LOG_DEBUG(g_logger) << "no match WSServlet"; 28 | break; 29 | } 30 | int rt = servlet->onConnect(header, session); 31 | if (rt) { 32 | FLEXY_LOG_DEBUG(g_logger) << "onConnect return " << rt; 33 | break; 34 | } 35 | 36 | while (true) { 37 | auto msg = session->recvMessage(); 38 | if (!msg) { 39 | break; 40 | } 41 | rt = servlet->handle(header, msg, session); 42 | if (rt) { 43 | FLEXY_LOG_DEBUG(g_logger) << "handle return " << rt; 44 | break; 45 | } 46 | } 47 | 48 | servlet->onClose(header, session); 49 | } while (false); 50 | session->close(); 51 | } 52 | 53 | } // namespace flexy::http -------------------------------------------------------------------------------- /flexy/http/ws_server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/net/tcp_server.h" 4 | #include "ws_servlet.h" 5 | #include "ws_session.h" 6 | 7 | namespace flexy::http { 8 | 9 | class WSServer : public TcpServer { 10 | public: 11 | using ptr = std::shared_ptr; 12 | WSServer(IOManager* worker = IOManager::GetThis(), 13 | IOManager* io_worker = IOManager::GetThis(), 14 | IOManager* accept_worker = IOManager::GetThis()); 15 | auto& getWSServletDispatch() const { return dispatch_; } 16 | void setWSServletDispatch(const WSServletDispatch::ptr& v) { 17 | dispatch_ = v; 18 | } 19 | 20 | protected: 21 | virtual void handleClient(const Socket::ptr& client) override; 22 | 23 | protected: 24 | WSServletDispatch::ptr dispatch_; 25 | }; 26 | 27 | } // namespace flexy::http -------------------------------------------------------------------------------- /flexy/http/ws_servlet.cpp: -------------------------------------------------------------------------------- 1 | #include "ws_servlet.h" 2 | 3 | namespace flexy::http { 4 | 5 | int32_t FunctionWSServlet::onConnect(const HttpRequest::ptr& header, 6 | const WSSession::ptr& session) { 7 | if (onConnect_) { 8 | return onConnect_(header, session); 9 | } 10 | return 0; 11 | } 12 | 13 | int32_t FunctionWSServlet::onClose(const HttpRequest::ptr& header, 14 | const WSSession::ptr& session) { 15 | if (onClose_) { 16 | return onClose_(header, session); 17 | } 18 | return 0; 19 | } 20 | 21 | int32_t FunctionWSServlet::handle(const HttpRequest::ptr& header, 22 | const WSFrameMessage::ptr& msg, 23 | const WSSession::ptr& session) { 24 | if (callback_) { 25 | return callback_(header, msg, session); 26 | } 27 | return 0; 28 | } 29 | 30 | WSServlet::ptr WSServletDispatch::getWSServlet(const std::string& uri) { 31 | auto slt = getMatchedServlet(uri); 32 | return std::dynamic_pointer_cast(slt); 33 | } 34 | 35 | } // namespace flexy::http -------------------------------------------------------------------------------- /flexy/http/ws_session.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "http_session.h" 6 | 7 | namespace flexy::http { 8 | 9 | /* 10 | 0 1 2 3 11 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 12 | +-+-+-+-+-------+-+-------------+-------------------------------+ 13 | |F|R|R|R| opcode|M| Payload len | Extended payload length | 14 | |I|S|S|S| (4) |A| (7) | (16/64) | 15 | |N|V|V|V| |S| | (if payload len==126/127) | 16 | | |1|2|3| |K| | | 17 | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + 18 | | Extended payload length continued, if payload len == 127 | 19 | + - - - - - - - - - - - - - - - +-------------------------------+ 20 | | |Masking-key, if MASK set to 1 | 21 | +-------------------------------+-------------------------------+ 22 | | Masking-key (continued) | Payload Data | 23 | +-------------------------------- - - - - - - - - - - - - - - - + 24 | : Payload Data continued ... : 25 | + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + 26 | | Payload Data continued ... | 27 | +---------------------------------------------------------------+ 28 | */ 29 | 30 | #pragma pack(1) 31 | struct WSFrameHead { 32 | enum OPCODE { 33 | // 数据分片帧 34 | CONTINUE = 0, 35 | // 文本帧 36 | TEXT_FRAME = 1, 37 | // 二进制帧 38 | BIN_FRAME = 2, 39 | // 断开连接 40 | CLOSE = 8, 41 | // PING 42 | PING = 9, 43 | // PONG 44 | PONG = 0xA 45 | }; 46 | uint32_t opcode : 4; 47 | bool rsv3 : 1; 48 | bool rsv2 : 1; 49 | bool rsv1 : 1; 50 | bool fin : 1; 51 | uint32_t payload : 7; 52 | bool mask : 1; 53 | 54 | std::string toString() const; 55 | operator std::string() { return toString(); } 56 | }; 57 | #pragma pack() 58 | 59 | class WSFrameMessage { 60 | public: 61 | using ptr = std::unique_ptr; 62 | WSFrameMessage(int opcode = 0, std::string_view data = "") 63 | : opcode_(opcode), data_(data) {} 64 | WSFrameMessage(int opcode = 0, std::string&& data = "") 65 | : opcode_(opcode), data_(std::move(data)) {} 66 | 67 | int getOpcode() const { return opcode_; } 68 | void setOpcode(int v) { opcode_ = v; } 69 | 70 | auto& getData() const { return data_; } 71 | auto& getData() { return data_; } 72 | void setData(std::string_view v) { data_ = v; } 73 | 74 | private: 75 | int opcode_; 76 | std::string data_; 77 | }; 78 | 79 | class WSSession : public HttpSession { 80 | public: 81 | using ptr = std::shared_ptr; 82 | WSSession(const Socket::ptr& sock, bool owner = true); 83 | 84 | HttpRequest::ptr handleShake(); 85 | 86 | WSFrameMessage::ptr recvMessage(); 87 | int32_t sendMessage(const WSFrameMessage::ptr& msg, bool fin = true); 88 | int32_t sendMessage(std::string_view msg, 89 | int32_t opcode = WSFrameHead::TEXT_FRAME, 90 | bool fin = true); 91 | int32_t ping(); 92 | int32_t pong(); 93 | 94 | private: 95 | bool handleServerShake(); 96 | bool handleClientShake(); 97 | }; 98 | 99 | WSFrameMessage::ptr WSRecvMessage(Stream* stream, bool client); 100 | int32_t WSSendMessage(Stream* stream, const WSFrameMessage::ptr& msg, 101 | bool client, bool fin); 102 | int32_t WSPing(Stream* stream); 103 | int32_t WSPong(Stream* stream); 104 | 105 | } // namespace flexy::http 106 | -------------------------------------------------------------------------------- /flexy/http2/README.md: -------------------------------------------------------------------------------- 1 | # http2 2 | 3 | ## 参考链接 4 | [http2 官方文档](https://http2.github.io/) 5 | [Halfrost-Field blog](https://github.com/halfrost/Halfrost-Field) 6 | [HTTP2帧格式](http://www.blogjava.net/yongboy/archive/2015/03/20/423655.html) 7 | [http2 抓包](https://juejin.cn/post/6977144949833728008) 8 | [多路复用](https://segmentfault.com/a/1190000016975064) -------------------------------------------------------------------------------- /flexy/http2/dynamic_table.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace flexy::http2 { 8 | 9 | class DynamicTable { 10 | public: 11 | DynamicTable(); 12 | int32_t update(std::string_view name, std::string_view value); 13 | int32_t findIndex(std::string_view name) const; 14 | std::pair findPair(std::string_view name, 15 | std::string_view value); 16 | std::pair getPair(uint32_t idx) const; 17 | std::string_view getName(uint32_t idx) const; 18 | std::string toString() const; 19 | 20 | void sexMaxDataSize(int32_t v) { maxDataSize_ = v; } 21 | 22 | public: 23 | static std::pair GetStaticHeaders( 24 | uint32_t idx); 25 | static int32_t GetStaticHeadersIndex(std::string_view name); 26 | static std::pair GetStaticHeadersPair(std::string_view name, 27 | std::string_view val); 28 | 29 | private: 30 | int32_t maxDataSize_; 31 | int32_t dataSize_; 32 | std::vector> datas_; 33 | }; 34 | 35 | } // namespace flexy::http2 36 | -------------------------------------------------------------------------------- /flexy/http2/huffman.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace flexy::http2::huffman { 6 | 7 | int EncodeString(const char* in, int in_len, std::string& out, int prefix); 8 | int EncodeString(std::string_view in, std::string& out, int prefix); 9 | int DecodeString(const char* in, int in_len, std::string& out); 10 | int DecodeString(std::string_view in, std::string& out); 11 | int EncodeLen(std::string_view in); 12 | int EncodeLen(const char* in, int in_len); 13 | 14 | bool ShouldEncode(std::string_view in); 15 | bool ShouldEncode(const char* in, int in_len); 16 | 17 | } // namespace flexy::http2::huffman -------------------------------------------------------------------------------- /flexy/net.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "net/address.h" 4 | #include "net/edian.h" 5 | #include "net/fd_manager.h" 6 | #include "net/hook.h" 7 | #include "net/socket.h" 8 | #include "net/tcp_server.h" -------------------------------------------------------------------------------- /flexy/net/bytearray.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | // #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "edian.h" 12 | 13 | namespace flexy { 14 | 15 | class ByteArray { 16 | public: 17 | using ptr = std::shared_ptr; 18 | struct Node { 19 | Node(size_t s) : ptr(new char[s]), next(nullptr) {} 20 | Node() : ptr(nullptr), next(nullptr) {} 21 | ~Node() = default; 22 | void free() { 23 | if (ptr) { 24 | delete[] ptr; 25 | ptr = nullptr; 26 | } 27 | } 28 | 29 | char* ptr; 30 | Node* next; 31 | }; 32 | 33 | ByteArray(size_t base_size = 4096); 34 | ByteArray(void* data, size_t size, bool owner); 35 | ~ByteArray(); 36 | void writeFint8(int8_t value); 37 | void writeFuint8(uint8_t value); 38 | void writeFint16(int16_t value); 39 | void writeFuint16(uint16_t value); 40 | void writeFint32(int32_t value); 41 | void writeFuint32(uint32_t value); 42 | void writeFint64(int64_t value); 43 | void writeFuint64(uint64_t value); 44 | 45 | void writeInt32(int32_t value); 46 | void writeUint32(uint32_t value); 47 | void writeInt64(int64_t value); 48 | void writeUint64(uint64_t value); 49 | 50 | void writeFloat(float value); 51 | void writeDouble(double value); 52 | 53 | void writeStringF16(const std::string& value); 54 | void writeStringF32(const std::string& value); 55 | void writeStringF64(const std::string& value); 56 | void writeStringVint(const std::string& value); 57 | void writeStringWithoutLength(const std::string& value); 58 | 59 | int8_t readFint8(); 60 | uint8_t readFuint8(); 61 | int16_t readFint16(); 62 | uint16_t readFuint16(); 63 | int32_t readFint32(); 64 | uint32_t readFuint32(); 65 | int64_t readFint64(); 66 | uint64_t readFuint64(); 67 | 68 | int32_t readInt32(); 69 | uint32_t readUint32(); 70 | uint64_t readUint64(); 71 | int64_t readInt64(); 72 | 73 | float readFloat(); 74 | double readDouble(); 75 | 76 | std::string readStringF16(); 77 | std::string readStringF32(); 78 | std::string readStringF64(); 79 | std::string readStringVint(); 80 | 81 | void clear(); 82 | void write(const void* buf, size_t size); 83 | void read(void* read, size_t size); 84 | void read(void* read, size_t size, size_t position) const; 85 | 86 | size_t getPosition() const { return m_position; } 87 | void setPosition(size_t v); 88 | 89 | bool writeToFile(std::string_view name); 90 | bool readFromFile(std::string_view name); 91 | 92 | size_t getBaseSize() const { return m_baseSize; } 93 | size_t getReadSize() const { return m_size - m_position; } 94 | 95 | bool isLittleEndian() const { return m_endian == FLEXY_LITTLE_ENDIAN; } 96 | void setLittleEndian(bool val) { 97 | m_endian = val ? FLEXY_LITTLE_ENDIAN : FLEXY_BIG_ENDIAN; 98 | } 99 | 100 | std::string toString() const; 101 | std::string toHexString() const; 102 | 103 | size_t getSize() const { return m_size; } 104 | uint64_t getReadBuffers(std::vector& buffers, 105 | uint64_t len = ~0ull) const; 106 | uint64_t getReadBuffers(std::vector& buffers, uint64_t len, 107 | uint64_t position) const; 108 | uint64_t getWriteBuffers(std::vector& buffers, uint64_t len); 109 | 110 | private: 111 | void addCapacity(size_t size); 112 | size_t getCapacity() const { return m_capacity - m_position; } 113 | 114 | private: 115 | size_t m_baseSize; // 内存块的大小 116 | size_t m_position; // 当前操作的位置 117 | size_t m_capacity; // 当前的总容量 118 | size_t m_size; // 当前的数据的大小 119 | int8_t m_endian; // 字节序 120 | Node* m_root; // 第一个内存指针 121 | Node* m_cur; // 当前操作的内存指针 122 | bool m_owner; // 是否是自己创建的内存 123 | }; 124 | 125 | } // namespace flexy 126 | -------------------------------------------------------------------------------- /flexy/net/edian.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define FLEXY_LITTLE_ENDIAN 1 7 | #define FLEXY_BIG_ENDIAN 2 8 | 9 | #if BYTE_ORDER == BIG_ENDIAN 10 | #define FLEXY_BYTE_ORDER FLEXY_BIG_ENDIAN 11 | #else 12 | #define FLEXY_BYTE_ORDER FLEXY_LITTLE_ENDIAN 13 | #endif 14 | 15 | 16 | namespace flexy { 17 | 18 | template 19 | T bswap(T value) { 20 | if constexpr (sizeof(T) == sizeof(uint64_t)) { 21 | return (T)bswap_64((uint64_t)value); 22 | } else if constexpr (sizeof(T) == sizeof(uint32_t)) { 23 | return (T)bswap_32((uint32_t)value); 24 | } else if constexpr (sizeof(T) == sizeof(uint16_t)) { 25 | return (T)bswap_16((uint16_t)value); 26 | } else { 27 | 28 | } 29 | } 30 | 31 | #if FLEXY_BYTE_ORDER == FLEXY_BIG_ENDIAN 32 | template 33 | T byteswap(T t) { 34 | return t; 35 | } 36 | 37 | #else 38 | template 39 | T byteswap(T t) { 40 | return bswap(t); 41 | } 42 | #endif 43 | 44 | } // namespace flexy 45 | -------------------------------------------------------------------------------- /flexy/net/fd_manager.cpp: -------------------------------------------------------------------------------- 1 | #include "fd_manager.h" 2 | #include "hook.h" 3 | #include "flexy/util/config.h" 4 | #include "flexy/util/macro.h" 5 | 6 | #include 7 | 8 | namespace flexy { 9 | 10 | static auto g_FdCtx_init_size = Config::Lookup("fdctx.init.size", 64, "FdCtx vector init size"); 11 | static auto g_FdCtx_resize_times = Config::Lookup("fdctx.resize.times", 2.0f, "FdCtx vector resize times"); 12 | 13 | FdCtx::FdCtx(int fd) : isInit_(false), isSocket_(false), sysNonblock_(false), 14 | userNonblock_(false), isClosed_(false), fd_(fd), recvTimeout_(-1), sendTimeout_(-1) { 15 | FLEXY_ASSERT(init()); 16 | } 17 | 18 | FdCtx::~FdCtx() = default; 19 | 20 | bool FdCtx::init() { 21 | if (isInit_) { 22 | return true; 23 | } 24 | recvTimeout_ = -1; 25 | sendTimeout_ = -1; 26 | struct stat fd_stat; 27 | if (fstat(fd_, &fd_stat) == -1) { 28 | isInit_ = false; 29 | isSocket_ = false; 30 | } else { 31 | isInit_ = true; 32 | isSocket_ = S_ISSOCK(fd_stat.st_mode); 33 | } 34 | 35 | if (isSocket_) { 36 | int flags = fcntl_f(fd_, F_GETFL, 0); 37 | if (!(flags & O_NONBLOCK)) { 38 | fcntl_f(fd_, F_SETFL, flags | O_NONBLOCK); 39 | } 40 | sysNonblock_ = true; 41 | } else { 42 | sysNonblock_ = false; 43 | } 44 | userNonblock_ = false; 45 | isClosed_ = false; 46 | return isInit_; 47 | } 48 | 49 | 50 | void FdCtx::setTimeout(int type, uint64_t v) { 51 | if (type == SO_RCVTIMEO) { 52 | recvTimeout_ = v; 53 | } else { 54 | sendTimeout_ = v; 55 | } 56 | } 57 | 58 | uint64_t FdCtx::getTimeout(int type) { 59 | return type == SO_RCVTIMEO ? recvTimeout_ : sendTimeout_; 60 | } 61 | 62 | FdManager::FdManager() { 63 | datas_.resize(g_FdCtx_init_size->getValue()); 64 | } 65 | 66 | FdManager::~FdManager() { 67 | for (auto ptr : datas_) { 68 | delete ptr; 69 | } 70 | } 71 | 72 | // 如果容器大小不大于fd并且auto_create为false, 返回nullptr 73 | // 如果容器大小大于fd, 但没有初始化, 并且auto_create为false, 返回nullptr 74 | FdCtx* FdManager::get(int fd, bool auto_create) { 75 | if (fd == -1) { 76 | return nullptr; 77 | } 78 | LOCK_GUARD(mutex_); 79 | if ((int)datas_.size() > fd) { 80 | if (datas_[fd]) { 81 | return datas_[fd]; 82 | } 83 | } 84 | if (!auto_create) { 85 | return nullptr; 86 | } 87 | if (fd >= (int)datas_.size()) { 88 | datas_.resize(fd * g_FdCtx_resize_times->getValue()); 89 | } 90 | datas_[fd] = new FdCtx(fd); 91 | return datas_[fd]; 92 | } 93 | 94 | 95 | void FdManager::del(int fd) { 96 | LOCK_GUARD(mutex_); 97 | if ((int)datas_.size() <= fd) { 98 | return; 99 | } 100 | datas_[fd] = nullptr; 101 | } 102 | 103 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/net/fd_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "flexy/thread/mutex.h" 5 | #include "flexy/util/singleton.h" 6 | 7 | namespace flexy { 8 | 9 | // 文件句柄上下文 10 | class FdCtx { 11 | public: 12 | FdCtx(int fd); 13 | ~FdCtx(); 14 | // 15 | bool init(); 16 | // 是否初始化完成 17 | bool isInit() const { return isInit_; } 18 | // 是否是socket 19 | bool isSocket() const { return isSocket_; } 20 | // 是否关闭 21 | bool isClose() const { return isClosed_; } 22 | // 设置用户态是否为阻塞 23 | void setUserNonblock(bool v) { userNonblock_ = v; } 24 | // 用户是否设置为非阻塞 25 | bool getUserNonblock() const { return userNonblock_; } 26 | // 设置系统(hook)是否为阻塞 27 | void setSysNonblock(bool v) { sysNonblock_ = v; } 28 | // 系统(hook)是否为阻塞 29 | bool getSysNonblock() const { return sysNonblock_; } 30 | // 设置type超时时间 31 | void setTimeout(int type, uint64_t v); 32 | // 获取type超时时间 33 | uint64_t getTimeout(int type); 34 | private: 35 | bool isInit_ : 1; // 是否初始化 36 | bool isSocket_ : 1; // 是否socket 37 | bool sysNonblock_ : 1; // 是否hook非阻塞 38 | bool userNonblock_ : 1; // 是否用户主动设置非阻塞 39 | bool isClosed_ : 1; // 是否关闭 40 | int fd_; // 文件句柄 41 | uint64_t recvTimeout_; // 读超时时间 (ms) 42 | uint64_t sendTimeout_; // 写超时时间 43 | }; 44 | 45 | 46 | class FdManager { 47 | public: 48 | FdManager(); 49 | ~FdManager(); 50 | FdCtx* get(int fd, bool auto_create = false); // 获取/创建 文件句柄 51 | void del(int fd); // 删除文件句柄 52 | private: 53 | mutable mutex mutex_; // 锁 54 | std::vector datas_; // 文件句柄集合 55 | }; 56 | 57 | using FdMsg = Singleton; 58 | 59 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/net/hook.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include /* See NOTES */ 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | namespace flexy { 13 | bool is_hook_enable(); 14 | void set_hook_enable(bool flag); 15 | } // namespace flexy 16 | 17 | extern "C" { 18 | 19 | // sleep 20 | typedef unsigned int (*sleep_fun)(unsigned int seconds); 21 | extern sleep_fun sleep_f; 22 | 23 | typedef int (*usleep_fun)(useconds_t usec); 24 | extern usleep_fun usleep_f; 25 | 26 | typedef int (*nanosleep_fun)(const struct timespec* req, struct timespec* rem); 27 | extern nanosleep_fun nanosleep_f; 28 | 29 | // socket 30 | typedef int (*socket_fun)(int domain, int type, int protocol); 31 | extern socket_fun socket_f; 32 | 33 | typedef int (*connect_fun)(int sockfd, const struct sockaddr* addr, socklen_t addrlen); 34 | extern connect_fun connect_f; 35 | 36 | typedef int (*accept_fun)(int sockfd, struct sockaddr* addr, socklen_t* addrlen); 37 | extern accept_fun accept_f; 38 | 39 | // write 40 | typedef ssize_t (*read_fun)(int fd, void* buf, size_t count); 41 | extern read_fun read_f; 42 | 43 | typedef ssize_t (*readv_fun)(int fd, const struct iovec* iov, int iovcnt); 44 | extern readv_fun readv_f; 45 | 46 | typedef ssize_t (*recv_fun)(int sockfd, void* buf, size_t len, int flags); 47 | extern recv_fun recv_f; 48 | 49 | typedef ssize_t (*recvfrom_fun)(int sockfd, void* buf, size_t len, int flags, 50 | struct sockaddr* src_addr, socklen_t* addrlen); 51 | extern recvfrom_fun recvfrom_f; 52 | 53 | typedef ssize_t (*recvmsg_fun)(int sockfd, struct msghdr* msg, int flags); 54 | extern recvmsg_fun recvmsg_f; 55 | 56 | // write 57 | typedef ssize_t (*write_fun)(int fd, const void* buf, size_t count); 58 | extern write_fun write_f; 59 | 60 | typedef ssize_t (*writev_fun)(int fd, const struct iovec* iov, int iovcnt); 61 | extern writev_fun writev_f; 62 | 63 | typedef ssize_t (*send_fun)(int sockfd, const void* buf, size_t len, int flags); 64 | extern send_fun send_f; 65 | 66 | typedef ssize_t (*sendto_fun)(int sockfd, const void* buf, size_t len, int flags, 67 | const struct sockaddr* dest_addr, socklen_t addrlen); 68 | extern sendto_fun sendto_f; 69 | 70 | typedef ssize_t (*sendmsg_fun)(int sockfd, const struct msghdr* msg, int flags); 71 | extern sendmsg_fun sendmsg_f; 72 | 73 | // socket op 74 | typedef int(*close_fun)(int fd); 75 | extern close_fun close_f; 76 | 77 | typedef int (*fcntl_fun)(int fd, int cmd, ... /* arg */ ); 78 | extern fcntl_fun fcntl_f; 79 | 80 | typedef int (*ioctl_fun)(int fd, unsigned long request, ...); 81 | extern ioctl_fun ioctl_f; 82 | 83 | typedef int (*getsockopt_fun)(int sockfd, int level, int optname, void* optval, socklen_t* optlen); 84 | extern getsockopt_fun getsockopt_f; 85 | 86 | typedef int (*setsockopt_fun)(int sockfd, int level, int optname, const void* optval, socklen_t optlen); 87 | extern setsockopt_fun setsockopt_f; 88 | 89 | extern int connect_with_timeout(int sockfd, const struct sockaddr* addr, socklen_t addrlen, __uint64_t timeout_ms); 90 | 91 | } -------------------------------------------------------------------------------- /flexy/net/tcp_server.cpp: -------------------------------------------------------------------------------- 1 | #include "tcp_server.h" 2 | #include "flexy/util/log.h" 3 | #include "flexy/util/config.h" 4 | 5 | namespace flexy { 6 | 7 | static auto g_logger = FLEXY_LOG_NAME("system"); 8 | 9 | static auto g_tcp_server_read_timeout = Config::Lookup("tcp_server.read_timeout", 10 | (uint64_t)(60 * 1000 * 2), "tcp server read timeout"); 11 | 12 | [[deprecated]] 13 | static void handleClientCb(const TcpServer::ptr& self, const Socket::ptr& client) { 14 | FLEXY_LOG_INFO(g_logger) << *client; 15 | } 16 | 17 | TcpServer::TcpServer(IOManager* worker, IOManager* io_worker, 18 | IOManager* accept_worker) : worker_(worker), ioWorker_(io_worker), 19 | acceptWorker_(accept_worker), recvTimeout_(g_tcp_server_read_timeout->getValue()), 20 | name_("flexy/1.0.0"), isStop_(true) /*, handleClient_(handleClientCb)*/ { 21 | } 22 | 23 | TcpServer::~TcpServer() { 24 | for (auto& sock : socks_) { 25 | sock->close(); 26 | } 27 | socks_.clear(); 28 | } 29 | 30 | void TcpServer::handleClient(const Socket::ptr& client) { 31 | FLEXY_LOG_INFO(g_logger) << *client; 32 | } 33 | 34 | bool TcpServer::bind(const Address::ptr& addr) { 35 | std::vector addrs; 36 | std::vector fails; 37 | addrs.push_back(addr); 38 | return bind(addrs, fails); 39 | } 40 | 41 | bool TcpServer::bind(const std::vector& addrs, std::vector& fails) { 42 | for (auto& addr : addrs) { 43 | auto sock = Socket::CreateTCP(addr->getFamily()); 44 | if (!sock->bind(addr)) { 45 | FLEXY_LOG_ERROR(g_logger) << "bind fail errno = " << errno 46 | << " errstr = " << strerror(errno) << " addr = [" << *addr 47 | << "]"; 48 | fails.push_back(addr); 49 | continue; 50 | } 51 | if (!sock->listen()) { 52 | FLEXY_LOG_ERROR(g_logger) << "listen fail errno = " << errno 53 | << "errstr = " << strerror(errno) << " addr = [" << *addr 54 | << "]"; 55 | fails.push_back(addr); 56 | continue; 57 | } 58 | socks_.push_back(sock); 59 | } 60 | if (!fails.empty()) { 61 | socks_.clear(); 62 | return false; 63 | } 64 | for (auto& sock : socks_) { 65 | FLEXY_LOG_INFO(g_logger) << "server bind success: " << *sock; 66 | } 67 | return true; 68 | } 69 | 70 | void TcpServer::startAccept(const Socket::ptr& sock) { 71 | while (!isStop_) { 72 | auto client = sock->accept(); 73 | if (client) { 74 | client->setRecvTimeout(recvTimeout_); 75 | // worker_->async(handleClient_, shared_from_this(), client); 76 | worker_->async(&TcpServer::handleClient, shared_from_this(), client); 77 | } else { 78 | FLEXY_LOG_ERROR(g_logger) << "accept errno = " << errno << "," 79 | << " errstr = " << strerror(errno); 80 | } 81 | } 82 | } 83 | 84 | bool TcpServer::start() { 85 | if(!isStop_) { 86 | return true; 87 | } 88 | isStop_ = false; 89 | for (auto& sock : socks_) { 90 | ioWorker_->async(&TcpServer::startAccept, shared_from_this(), sock); 91 | } 92 | return true; 93 | } 94 | 95 | void TcpServer::stop() { 96 | isStop_ = true; 97 | acceptWorker_->async([](const TcpServer::ptr& slef){ 98 | for (auto& sock : slef->socks_) { 99 | sock->cancelAll(); 100 | sock->close(); 101 | } 102 | slef->socks_.clear(); 103 | }, shared_from_this()); 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /flexy/net/tcp_server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/util/noncopyable.h" 4 | #include "flexy/schedule/iomanager.h" 5 | #include "address.h" 6 | #include "socket.h" 7 | #include 8 | #include 9 | 10 | namespace flexy { 11 | 12 | class TcpServer; 13 | using TcpCallBack = std::function&, const Socket::ptr&)>; 15 | 16 | class TcpServer : public std::enable_shared_from_this, noncopyable { 17 | public: 18 | using ptr = std::shared_ptr; 19 | TcpServer(IOManager* worker = IOManager::GetThis(), 20 | IOManager* io_worker = IOManager::GetThis(), 21 | IOManager* accept_worker = IOManager::GetThis()); 22 | virtual ~TcpServer(); 23 | bool bind(const Address::ptr& address); 24 | bool bind(const std::vector& addrs, std::vector& fails); 25 | bool start(); 26 | void stop(); 27 | 28 | uint64_t getRecvTimeout() const { return recvTimeout_; } 29 | void setRecvTimeout(uint64_t v) { recvTimeout_ = v; } 30 | auto& getName() const { return name_; } 31 | void setName(std::string_view name) { name_ = name; } 32 | IOManager* getWorker() const { return worker_; } 33 | bool isStop() const { return isStop_; } 34 | 35 | [[deprecated]] 36 | void onHandleClient(TcpCallBack&& cb) { handleClient_ = std::move(cb); } 37 | [[deprecated]] 38 | void onHandleClient(const TcpCallBack& cb) { handleClient_ = cb; } 39 | protected: 40 | virtual void handleClient(const Socket::ptr& client); 41 | void startAccept(const Socket::ptr& sock); 42 | protected: 43 | std::vector socks_; 44 | IOManager* worker_; 45 | IOManager* ioWorker_; 46 | IOManager* acceptWorker_; 47 | uint64_t recvTimeout_; 48 | std::string name_; 49 | std::string type_; 50 | std::atomic isStop_; 51 | [[deprecated]] TcpCallBack handleClient_; 52 | 53 | }; 54 | 55 | } -------------------------------------------------------------------------------- /flexy/schedule.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "schedule/async_io.h" 4 | #include "schedule/channel.h" 5 | #include "schedule/iomanager.h" 6 | #include "schedule/scheduler.h" 7 | #include "schedule/semaphore.h" 8 | #include "schedule/timer.h" -------------------------------------------------------------------------------- /flexy/schedule/async_io.cpp: -------------------------------------------------------------------------------- 1 | #include "async_io.h" 2 | 3 | namespace flexy { 4 | 5 | __async__cin async_cin; 6 | __async__cout async_cout; 7 | 8 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/schedule/async_io.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "flexy/net/hook.h" 3 | #include "flexy/util/log.h" 4 | #include "iomanager.h" 5 | 6 | namespace flexy { 7 | 8 | struct __async__cin { 9 | template 10 | static void Scan(Args&... args) { 11 | if (!is_hook_enable()) { 12 | FLEXY_LOG_INFO(FLEXY_LOG_ROOT()) << "no hook"; 13 | return; 14 | } 15 | auto iom = IOManager::GetThis(); 16 | if (!iom) { 17 | FLEXY_LOG_ERROR(FLEXY_LOG_ROOT()) << "IOManager is nullptr"; 18 | return; 19 | } 20 | int rt = iom->onRead(0); 21 | if (!rt) { 22 | FLEXY_LOG_ERROR(FLEXY_LOG_ROOT()) << "async scanln error"; 23 | return; 24 | } else { 25 | Fiber::Yield(); 26 | (std::cin >> ... >> args); 27 | } 28 | } 29 | 30 | template 31 | __async__cin& operator>>(T& val) { 32 | if (!is_hook_enable()) { 33 | FLEXY_LOG_INFO(FLEXY_LOG_ROOT()) << "no hook"; 34 | return *this; 35 | } 36 | auto iom = flexy::IOManager::GetThis(); 37 | if (!iom) { 38 | FLEXY_LOG_ERROR(FLEXY_LOG_ROOT()) << "IOManager is nullptr"; 39 | return *this; 40 | } 41 | int rt = iom->onRead(0); 42 | if (!rt) { 43 | FLEXY_LOG_ERROR(FLEXY_LOG_ROOT()) << "async cin error"; 44 | return *this; 45 | } else { 46 | Fiber::Yield(); 47 | std::cin >> val; 48 | return *this; 49 | } 50 | } 51 | 52 | void getline(std::string& s) { 53 | if (!is_hook_enable()) { 54 | FLEXY_LOG_INFO(FLEXY_LOG_ROOT()) << "no hook"; 55 | return; 56 | } 57 | auto iom = flexy::IOManager::GetThis(); 58 | if (!iom) { 59 | FLEXY_LOG_ERROR(FLEXY_LOG_ROOT()) << "IOManager is nullptr"; 60 | return; 61 | } 62 | int rt = iom->onRead(0); 63 | if (!rt) { 64 | FLEXY_LOG_ERROR(FLEXY_LOG_ROOT()) << "async getline error"; 65 | return; 66 | } else { 67 | Fiber::Yield(); 68 | std::getline(std::cin, s); 69 | } 70 | } 71 | }; 72 | 73 | struct __async__cout { 74 | template 75 | static void Print(Args&&... args) { 76 | if (!is_hook_enable()) { 77 | FLEXY_LOG_INFO(FLEXY_LOG_ROOT()) << "no hook"; 78 | return; 79 | } 80 | auto iom = IOManager::GetThis(); 81 | if (!iom) { 82 | FLEXY_LOG_ERROR(FLEXY_LOG_ROOT()) << "IOManager is nullptr"; 83 | return; 84 | } 85 | int rt = iom->onWrite(1); 86 | if (!rt) { 87 | FLEXY_LOG_ERROR(FLEXY_LOG_ROOT()) << "async scanln error"; 88 | return; 89 | } else { 90 | Fiber::Yield(); 91 | ((std::cout << std::forward(args)), ...); 92 | } 93 | } 94 | 95 | template 96 | __async__cout& operator<<(T&& val) { 97 | if (!is_hook_enable()) { 98 | FLEXY_LOG_INFO(FLEXY_LOG_ROOT()) << "no hook"; 99 | return *this; 100 | } 101 | auto iom = flexy::IOManager::GetThis(); 102 | if (!iom) { 103 | FLEXY_LOG_ERROR(FLEXY_LOG_ROOT()) << "IOManager is nullptr"; 104 | return *this; 105 | } 106 | int rt = iom->onWrite(1); 107 | if (!rt) { 108 | FLEXY_LOG_ERROR(FLEXY_LOG_ROOT()) << "async cin error"; 109 | return *this; 110 | } else { 111 | Fiber::Yield(); 112 | std::cout << std::forward(val); 113 | return *this; 114 | } 115 | } 116 | 117 | __async__cout& operator<<(std::ostream& (*pf)(std::ostream&)) { 118 | pf(std::cout); 119 | return *this; 120 | } 121 | }; 122 | 123 | extern __async__cin async_cin; 124 | extern __async__cout async_cout; 125 | 126 | static void getline(__async__cin& cin, std::string& s) { 127 | return cin.getline(s); 128 | } 129 | 130 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/schedule/channel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "flexy/util/noncopyable.h" 3 | #include "flexy/thread/mutex.h" 4 | #include "scheduler.h" 5 | 6 | #include 7 | 8 | namespace flexy { 9 | 10 | enum Event { 11 | NONE = 0x0, 12 | READ = 0x1, 13 | WRITE = 0x4 14 | }; 15 | 16 | class Channel : noncopyable { 17 | private: 18 | // 事件上下文 19 | struct EventContext { 20 | Scheduler* scheduler = nullptr; // 事件执行的调度器 21 | Fiber::ptr fiber = nullptr; // 事件协程 22 | detail::__task cb = nullptr; // 事件回调函数 23 | }; 24 | public: 25 | // 处理读写事件, 返回处理的事件数量 26 | int handleEvents(Event events); 27 | // 处理读事件 28 | void handleRead(); 29 | // 处理写事件 30 | void handleWrite(); 31 | // 由event得到对应的事件上下文 32 | EventContext& getContext(Event event); 33 | // 得到读事件上下文 34 | auto& getReadContext() { return read_; } 35 | // 得到写事件上下文 36 | auto& getWriteContext() { return write_; } 37 | // 构造函数 38 | Channel(int epoll_fd, int fd, Event evens = Event::NONE); 39 | // 启用或取消读事件 40 | bool enableRead(bool enable = true); 41 | // 启用或取消写事件 42 | bool enableWrite(bool enable = true); 43 | // 启用或取消读写事件 44 | bool enableEvents(Event events, bool enable = true); 45 | // 重置事件上下文 46 | void resetContext(EventContext& ctx); 47 | int fd() const { return fd_; } 48 | Event getEvents() const { return events_; } 49 | private: 50 | int epfd_; // epoll 文件描述符 51 | int fd_; // 事件关联的句柄 52 | Event events_ = Event::NONE; // 已经注册的事件 53 | EventContext read_; // 读事件 54 | EventContext write_; // 写事件 55 | public: 56 | mutable mutex mutex_; // 锁 // 由iomanager加锁 57 | }; 58 | 59 | inline std::ostream& operator<<(std::ostream& os, Event events) { 60 | bool first = true; 61 | #define XX(E) \ 62 | if(events & E) { \ 63 | if(!first) { \ 64 | os << "|"; \ 65 | } \ 66 | os << #E; \ 67 | first = false; \ 68 | } 69 | XX(Event::NONE) 70 | XX(Event::READ) 71 | XX(Event::WRITE) 72 | #undef XX 73 | return os; 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /flexy/schedule/iomanager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "channel.h" 4 | #include "timer.h" 5 | 6 | namespace flexy { 7 | 8 | class IOManager : public Scheduler, public TimerManager { 9 | public: 10 | IOManager(size_t thread = 1, bool use_caller = true, std::string_view name = ""); 11 | ~IOManager(); 12 | // 添加事件及其回调函数 13 | template 14 | bool addEvent(int fd, Event events, Args&&... args) { 15 | return onEvent(fd, events, detail::__task(std::forward(args)...)); 16 | } 17 | // 删除事件及其回调函数 18 | bool delEvent(int fd, Event event); 19 | // 删除读事件及其回调函数 20 | bool delRead(int fd) { return delEvent(fd, Event::READ); } 21 | // 删除写事件及其回调函数 22 | bool delWrite(int fd) { return delEvent(fd, Event::WRITE); } 23 | // 立即执行事件回调函数,取消事件 24 | bool cancelEvent(int fd, Event event); 25 | // 若有读事件取消读事件,若有写事件取消写事件 26 | bool cancelAll(int fd); 27 | // 取消读事件 28 | bool cancelRead(int fd) { return cancelEvent(fd, Event::READ); } 29 | // 取消写事件 30 | bool cancelWrite(int fd) { return cancelEvent(fd, Event::WRITE); } 31 | // 添加事件及其回调函数 32 | bool onEvent(int fd, Event event, detail::__task&& cb = nullptr); 33 | 34 | // 注册读事件 35 | template 36 | bool onRead(int fd, Args&&... args) { 37 | return onEvent(fd, Event::READ, 38 | detail::__task(std::forward(args)...)); 39 | } 40 | // 注册写事件 41 | template 42 | bool onWrite(int fd, Args&&... args) { 43 | return onEvent(fd, Event::WRITE, 44 | detail::__task(std::forward(args)...)); 45 | } 46 | // 返回当前的IOManager 47 | static IOManager* GetThis(); 48 | protected: 49 | [[deprecated]] void tickle() override; 50 | bool stopping() override; 51 | void idleFiber(); 52 | [[deprecated]] void idle() override; 53 | [[deprecated]] void onTimerInsertedAtFront() override; 54 | // 判断是否可以停止 55 | bool stopping(uint64_t& timeout); 56 | // 对 Channel 集合容器扩容 57 | void channelResize(size_t size); 58 | private: 59 | int epfd_; // epoll文件描述符 60 | int tickleFds_[2]; // 管道 用作tickle 61 | std::atomic pendingEventCount_ = {0}; // 当前等待执行的事件数量 62 | mutable mutex mutex_; // 锁 63 | std::vector channels_; // Channel集合 64 | }; 65 | 66 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/schedule/semaphore.cpp: -------------------------------------------------------------------------------- 1 | #include "semaphore.h" 2 | #include "flexy/schedule/scheduler.h" 3 | #include "flexy/util/macro.h" 4 | 5 | namespace flexy::fiber { 6 | 7 | Semaphore::~Semaphore() { FLEXY_ASSERT(waiters_.empty()); } 8 | 9 | bool Semaphore::tryWait() { 10 | FLEXY_ASSERT(Scheduler::GetThis()); 11 | { 12 | LOCK_GUARD(mutex_); 13 | if (concurrency_ > 0u) { 14 | --concurrency_; 15 | return true; 16 | } 17 | return false; 18 | } 19 | } 20 | 21 | void Semaphore::wait() { 22 | FLEXY_ASSERT(Scheduler::GetThis()); 23 | FLEXY_ASSERT2(Fiber::GetFiberId() != 0, "Main Fiber cannot wait"); 24 | { 25 | LOCK_GUARD(mutex_); 26 | if (concurrency_ > 0u) { 27 | --concurrency_; 28 | return; 29 | } 30 | waiters_.emplace(Scheduler::GetThis(), Fiber::GetThis()); 31 | } 32 | Fiber::Yield(); 33 | } 34 | 35 | void Semaphore::post() { 36 | LOCK_GUARD(mutex_); 37 | if (!waiters_.empty()) { 38 | auto&& [scheduler, fiber] = std::move(waiters_.front()); // reference 39 | // auto [scheduler, fiber] = std::move(waiters_.front()); // move 40 | // construct 41 | scheduler->async(std::move(fiber)); 42 | waiters_.pop(); 43 | } else { 44 | ++concurrency_; 45 | } 46 | } 47 | 48 | } // namespace flexy::fiber -------------------------------------------------------------------------------- /flexy/schedule/semaphore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/fiber/fiber.h" 4 | #include "flexy/thread/mutex.h" 5 | #include "flexy/util/noncopyable.h" 6 | 7 | #include 8 | 9 | namespace flexy { 10 | 11 | class Scheduler; 12 | 13 | } // namespace flexy 14 | 15 | namespace flexy::fiber { 16 | 17 | // fiber semaphore 18 | class Semaphore : noncopyable { 19 | public: 20 | Semaphore(size_t initial_councurrency = 0) 21 | : concurrency_(initial_councurrency) {} 22 | ~Semaphore(); 23 | 24 | bool tryWait(); 25 | void wait(); 26 | void post(); 27 | 28 | private: 29 | mutable Spinlock mutex_; 30 | size_t concurrency_; 31 | std::queue> waiters_; 32 | }; 33 | 34 | } // namespace flexy::fiber -------------------------------------------------------------------------------- /flexy/schedule/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "flexy/util/task.h" 5 | #include "flexy/thread/mutex.h" 6 | namespace flexy { 7 | 8 | class TimerManager; 9 | class Timer : public std::enable_shared_from_this { 10 | friend class TimerManager; 11 | public: 12 | using ptr = std::shared_ptr; 13 | // 取消定时器 14 | bool cancel(); 15 | // 刷新设置定时器的执行时间 16 | bool refresh(); 17 | // 重置定时器时间 from_now 是否从当前时间开始计算 18 | bool reset(uint64_t ms, bool from_now); 19 | private: 20 | Timer(uint64_t ms, detail::__task&& cb, bool recurring, 21 | TimerManager* manager); 22 | Timer(uint64_t next); 23 | private: 24 | bool recurring_; // 是否为循环定时器 25 | uint64_t ms_; // 执行周期 26 | uint64_t next_; // 精确的执行时间 27 | detail::__task cb_; // 回调函数 28 | TimerManager* manager_ = nullptr; // 定时器所属定时器管理者 29 | private: 30 | struct Comparator { 31 | bool operator()(const Timer::ptr& lhs, const Timer::ptr& rhs) const; 32 | }; 33 | }; 34 | 35 | class TimerManager { 36 | friend class Timer; 37 | public: 38 | TimerManager(); 39 | virtual ~TimerManager() { } 40 | // 添加定时器 41 | template 42 | Timer::ptr addTimer(uint64_t ms, Args&&... args) { 43 | Timer::ptr timer(new Timer( 44 | ms, detail::__task(std::forward(args)...), false, this)); 45 | WRITELOCK2(mutex_); 46 | addTimer(timer, lk); 47 | return timer; 48 | } 49 | // 添加循环定时器 50 | template 51 | Timer::ptr addRecTimer(uint64_t ms, Args&&... args) { 52 | Timer::ptr timer(new Timer( 53 | ms, detail::__task(std::forward(args)...), true, this)); 54 | WRITELOCK2(mutex_); 55 | addTimer(timer, lk); 56 | return timer; 57 | } 58 | // 添加条件定时器 59 | template 60 | Timer::ptr addCondtionTimer(uint64_t ms, std::weak_ptr weak_cond, Args&&... args) { 61 | return addTimer(ms, OnTimer, weak_cond, 62 | detail::__task(std::forward(args)...)); 63 | } 64 | // 添加循环条件定时器 65 | template 66 | Timer::ptr addRecCondtionTimer(uint64_t ms, std::weak_ptr weak_cond, Args&&... args) { 67 | return addRecTimer(ms, OnTimer, weak_cond, 68 | detail::__task(std::forward(args)...)); 69 | } 70 | // 获取下一个要执行的定时器任务的时间 71 | uint64_t getNextTimer(); 72 | // 获取已到期的定时器需要执行回调函数的列表 73 | std::vector listExpiriedTimer(); 74 | // 是否有定时器 75 | bool hasTimer() const; 76 | protected: 77 | // 当有新的定时器插入到定时器的首部,执行该函数 78 | [[deprecated]] virtual void onTimerInsertedAtFront() = 0; 79 | // 注册函数 80 | template 81 | void onRefreshNearest(Args&&... args) { 82 | refreshNearest_ = detail::__task(std::forward(args)...); 83 | } 84 | // 将定时器添加到timers_中 85 | void addTimer(Timer::ptr& val, unique_lock& lock); 86 | // 将定时器添加到timers_中 87 | void addTimer(Timer::ptr&& val, unique_lock& lock); 88 | // 检测服务器时间是否被调后了 89 | bool detectClockRollover(uint64_t now_ms); 90 | private: 91 | mutable mutex mutex_; // 锁 92 | std::set timers_; // 定时器集合 93 | bool tickled_ = false; // 是否触发onTimerInsertedAtFront 94 | uint64_t previouseTime_; // 上次执行时间 95 | protected: 96 | detail::__task 97 | refreshNearest_; // 当有新的定时器插入到定时器的首部,执行该函数 98 | private: 99 | static void OnTimer(std::weak_ptr weak_cond, detail::__task&& cb); 100 | }; 101 | 102 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/schedule/worker.cpp: -------------------------------------------------------------------------------- 1 | #include "worker.h" 2 | #include "flexy/util/config.h" 3 | 4 | namespace flexy { 5 | 6 | static auto g_worker_config = Config::Lookup( 7 | "workers", std::map>(), 8 | "worker config"); 9 | 10 | void WorkerManager::add(const ptr& s) { 11 | datas_[s->getName()].push_back(s); 12 | } 13 | 14 | std::shared_ptr WorkerManager::get(const std::string& name) { 15 | if (auto it = datas_.find(name); it != datas_.end()) { 16 | if (it->second.size() == 1) { 17 | return it->second[0]; 18 | } 19 | return it->second[rand() % it->second.size()]; 20 | } 21 | return nullptr; 22 | } 23 | 24 | std::shared_ptr WorkerManager::getAsIOManager( 25 | const std::string& name) { 26 | return std::dynamic_pointer_cast(get(name)); 27 | } 28 | 29 | bool WorkerManager::init( 30 | const std::map>& v) { 31 | for (auto& [name, map] : v) { 32 | int32_t thread_num = GetParamValue(map, "thread_num", 1); 33 | int32_t worker_num = GetParamValue(map, "worker_num", 1); 34 | 35 | datas_[name].emplace_back(new IOManager(thread_num, false, name)); 36 | for (int32_t i = 1; i < worker_num; ++i) { 37 | datas_[name + "-" + std::to_string(i)].emplace_back(new IOManager( 38 | thread_num, false, name + "-" + std::to_string(i))); 39 | } 40 | } 41 | stop_ = datas_.empty(); 42 | return true; 43 | } 44 | 45 | bool WorkerManager::init() { 46 | auto workers = g_worker_config->getValue(); 47 | return init(workers); 48 | } 49 | 50 | void WorkerManager::stop() { 51 | if (stop_) { 52 | return; 53 | } 54 | for (auto& [name, sch_vec] : datas_) { 55 | for (auto& sche : sch_vec) { 56 | sche->async([]() {}); 57 | sche->stop(); 58 | } 59 | } 60 | datas_.clear(); 61 | stop_ = true; 62 | } 63 | 64 | uint32_t WorkerManager::getCount() { return datas_.size(); } 65 | 66 | std::ostream& WorkerManager::dump(std::ostream& os) { 67 | for (auto& [name, sch_vec] : datas_) { 68 | for (auto& sche : sch_vec) { 69 | sche->dump(os) << std::endl; 70 | } 71 | } 72 | return os; 73 | } 74 | 75 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/schedule/worker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "flexy/util/log.h" 5 | #include "flexy/util/noncopyable.h" 6 | #include "iomanager.h" 7 | #include "semaphore.h" 8 | 9 | namespace flexy { 10 | 11 | class WorkerGroup : noncopyable, 12 | public std::enable_shared_from_this { 13 | public: 14 | using ptr = std::shared_ptr; 15 | static WorkerGroup::ptr Create(uint32_t batch_size, 16 | Scheduler* s = Scheduler::GetThis()) { 17 | return std::make_shared(batch_size, s); 18 | } 19 | 20 | WorkerGroup(uint32_t batch_size, Scheduler* s = Scheduler::GetThis()) 21 | : batchSize_(batch_size), 22 | finish_(false), 23 | scheduler_(s), 24 | sem_(batch_size) {} 25 | 26 | ~WorkerGroup() { waitAll(); } 27 | 28 | template >> 30 | void schedule(_Args&&... args) { 31 | static_assert(sizeof...(args) > 0, "args must > 0"); 32 | sem_.wait(); 33 | // scheduler_->async([self = shared_from_this()](auto&&... args) { 34 | // // std::invoke(std::forward(args)...); // 35 | // functor 失败 36 | // detail::__task(std::forward(args)...)(); 37 | // self->sem_.post(); 38 | // }, std::forward<_Args>(args)...); 39 | scheduler_->async( 40 | [self = shared_from_this(), 41 | task = detail::__task(std::forward<_Args>(args)...)]() { 42 | task(); 43 | self->sem_.post(); 44 | }); 45 | } 46 | 47 | void waitAll() { 48 | if (!finish_) { 49 | finish_ = true; 50 | for (uint32_t i = 0; i < batchSize_; ++i) { 51 | sem_.wait(); 52 | } 53 | } 54 | } 55 | 56 | private: 57 | template 58 | void doWork(Fn&& fn, Args&&... args) { 59 | fn(std::forward(args)...); 60 | sem_.post(); 61 | } 62 | 63 | private: 64 | uint32_t batchSize_; 65 | bool finish_; 66 | Scheduler* scheduler_; 67 | fiber::Semaphore sem_; 68 | }; 69 | 70 | class WorkerManager { 71 | public: 72 | template 73 | using ptr = std::shared_ptr; 74 | 75 | WorkerManager() : stop_(false) {} 76 | void add(const ptr& s); 77 | ptr get(const std::string& name); 78 | ptr getAsIOManager(const std::string& name); 79 | 80 | template 81 | void schedule(const std::string& name, FnAndArgs&&... args) { 82 | auto s = get(name); 83 | if (s) { 84 | s->async(std::forward(args)...); 85 | } else { 86 | static auto s_logger = FLEXY_LOG_NAME("system"); 87 | FLEXY_LOG_ERROR(s_logger) 88 | << "scheduler name = " << name << " not exists"; 89 | } 90 | } 91 | 92 | template 93 | void schedule(const std::string& name, Iter&& begin, Iter&& end) { 94 | auto s = get(name); 95 | if (s) { 96 | s->async(std::forward(begin), std::forward(end)); 97 | } else { 98 | static auto s_logger = FLEXY_LOG_NAME("system"); 99 | FLEXY_LOG_ERROR(s_logger) 100 | << "scheduler name = " << name << " not exists"; 101 | } 102 | } 103 | 104 | bool init(); 105 | bool init( 106 | const std::map>& v); 107 | void stop(); 108 | 109 | bool isStoped() const { return stop_; } 110 | std::ostream& dump(std::ostream& os); 111 | 112 | uint32_t getCount(); 113 | 114 | private: 115 | std::map>> datas_; 116 | bool stop_; 117 | }; 118 | 119 | using WorkerMgr = Singleton; 120 | 121 | } // namespace flexy 122 | -------------------------------------------------------------------------------- /flexy/stream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "stream/socket_stream.h" 4 | #include "stream/stream.h" 5 | -------------------------------------------------------------------------------- /flexy/stream/async_socket_stream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "flexy/net/socket.h" 9 | #include "flexy/schedule/iomanager.h" 10 | #include "flexy/schedule/semaphore.h" 11 | #include "socket_stream.h" 12 | 13 | namespace flexy { 14 | 15 | class AsyncSockStream : public SockStream, 16 | public std::enable_shared_from_this { 17 | public: 18 | using ptr = std::shared_ptr; 19 | using connect_callback = std::function; 20 | using disconnect_callback = 21 | std::function; 22 | 23 | AsyncSockStream(const Socket::ptr& sock, bool owner = true); 24 | 25 | virtual bool start(); 26 | virtual void close() override; 27 | 28 | public: 29 | enum Error { OK = 0, TIMEOUT = -1, IO_ERROR = -2, NOT_CONNECT = -3 }; 30 | 31 | protected: 32 | struct SendCtx { 33 | using ptr = std::shared_ptr; 34 | virtual ~SendCtx() {} 35 | virtual bool doSend(const AsyncSockStream::ptr& stream) = 0; 36 | }; 37 | 38 | struct Ctx : public SendCtx { 39 | public: 40 | using ptr = std::shared_ptr; 41 | virtual ~Ctx() {} 42 | Ctx(); 43 | 44 | uint32_t sn; 45 | uint32_t timeout; 46 | uint32_t result; 47 | bool timed; 48 | 49 | Scheduler* scheduler; 50 | Fiber::ptr fiber; 51 | Timer::ptr timer; 52 | 53 | virtual void doRsp(); 54 | }; 55 | 56 | public: 57 | void setWorker(IOManager* v) { worker_ = v; } 58 | IOManager* getWorker() const { return worker_; } 59 | 60 | void setIOManager(IOManager* v) { iomanager_ = v; } 61 | IOManager* getIOManager() const { return iomanager_; } 62 | 63 | bool isAutoConnect() const { return autoConnect_; } 64 | void setAutoConnect(bool v) { autoConnect_ = v; } 65 | 66 | const connect_callback& getConnectCb() const { return connectCb_; } 67 | const disconnect_callback& getDisconnectCb() const { return disconnectCb_; } 68 | 69 | void setConnectCb(const connect_callback& cb) { connectCb_ = cb; } 70 | void setConnectCb(connect_callback&& cb) { connectCb_ = std::move(cb); } 71 | void setDisconnectCb(const disconnect_callback& cb) { disconnectCb_ = cb; } 72 | void setDisconnectCb(disconnect_callback&& cb) { 73 | disconnectCb_ = std::move(cb); 74 | } 75 | 76 | template 77 | void setData(T&& v) { 78 | data_ = std::forward(v); 79 | } 80 | 81 | template 82 | T getData() const { 83 | try { 84 | return std::any_cast(data_); 85 | } catch (...) { 86 | } 87 | return T(); 88 | } 89 | 90 | protected: 91 | virtual void doRead(); 92 | virtual void doWrite(); 93 | virtual void startRead(); 94 | virtual void startWrite(); 95 | virtual void onTimeOut(const Ctx::ptr& ctx); 96 | virtual Ctx::ptr doRecv() = 0; 97 | virtual void onClose() {} 98 | 99 | Ctx::ptr getCtx(uint32_t sn); 100 | Ctx::ptr getAndDelCtx(uint32_t sn); 101 | 102 | template 103 | std::shared_ptr getCtxAs(uint32_t sn) { 104 | auto&& ctx = getCtx(sn); 105 | if (ctx) { 106 | return std::dynamic_pointer_cast(ctx); 107 | } 108 | return nullptr; 109 | } 110 | 111 | template 112 | std::shared_ptr getAndDelCtxAs(uint32_t sn) { 113 | auto&& ctx = getAndDelCtx(sn); 114 | if (ctx) { 115 | return std::dynamic_pointer_cast(ctx); 116 | } 117 | return nullptr; 118 | } 119 | 120 | bool addCtx(const Ctx::ptr& ctx); 121 | bool enqueue(const SendCtx::ptr& ctx); 122 | 123 | bool innerClose(); 124 | bool waitFiber(); 125 | 126 | protected: 127 | fiber::Semaphore sem_; 128 | fiber::Semaphore waitSem_; 129 | rw_mutex queueMutex_; 130 | std::deque queue_; 131 | rw_mutex mutex_; 132 | std::unordered_map ctxs_; 133 | 134 | uint32_t sn_; 135 | bool autoConnect_; 136 | // uint16_t tryConnectCount_; 137 | Timer::ptr timer_; 138 | IOManager* iomanager_; 139 | IOManager* worker_; 140 | 141 | connect_callback connectCb_; 142 | disconnect_callback disconnectCb_; 143 | 144 | std::any data_; 145 | }; 146 | 147 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/stream/socket_stream.cpp: -------------------------------------------------------------------------------- 1 | #include "socket_stream.h" 2 | #include "flexy/util/log.h" 3 | 4 | namespace flexy { 5 | 6 | static auto g_logger = FLEXY_LOG_NAME("system"); 7 | 8 | SockStream::SockStream(const Socket::ptr& socket, bool owner) 9 | : sock_(socket), owner_(owner) { 10 | 11 | } 12 | 13 | SockStream::~SockStream() { 14 | if (owner_ && sock_) { 15 | sock_->close(); 16 | } 17 | } 18 | 19 | bool SockStream::isConnected() const { 20 | return sock_ && sock_->isConnected(); 21 | } 22 | 23 | ssize_t SockStream::read(void* buffer, size_t length) { 24 | if (!isConnected()) { 25 | return -1; 26 | } 27 | return sock_->recv(buffer, length); 28 | } 29 | 30 | ssize_t SockStream::read(const ByteArray::ptr& ba, size_t length) { 31 | if (!isConnected()) { 32 | return -1; 33 | } 34 | std::vector iovs; 35 | ba->getWriteBuffers(iovs, length); 36 | ssize_t rt = sock_->recv(iovs.data(), iovs.size()); 37 | if (rt > 0) { 38 | ba->setPosition(ba->getPosition() + rt); 39 | } 40 | return rt; 41 | } 42 | 43 | ssize_t SockStream::write(const void* buffer, size_t length) { 44 | if (!isConnected()) { 45 | return -1; 46 | } 47 | return sock_->send(buffer, length); 48 | } 49 | 50 | ssize_t SockStream::write(const ByteArray::ptr& ba, size_t length) { 51 | if (!isConnected()) { 52 | return -1; 53 | } 54 | std::vector iovs; 55 | ba->getReadBuffers(iovs, length); 56 | ssize_t rt = sock_->send(iovs.data(), iovs.size()); 57 | if (rt > 0) { 58 | ba->setPosition(ba->getPosition() + rt); 59 | } else { 60 | FLEXY_LOG_FMT_ERROR(g_logger, "write fail length = {} errno = {}, {}", 61 | length, errno, strerror(errno)); 62 | } 63 | return rt; 64 | } 65 | 66 | void SockStream::close() { 67 | if (sock_) { 68 | sock_->close(); 69 | } 70 | } 71 | 72 | 73 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/stream/socket_stream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "stream.h" 3 | #include "flexy/net/socket.h" 4 | 5 | namespace flexy { 6 | 7 | class SockStream : public Stream { 8 | public: 9 | using ptr = std::shared_ptr; 10 | SockStream(const Socket::ptr& sock, bool owner); 11 | ~SockStream(); 12 | /*virtual*/ ssize_t read(void* buffer, size_t length) override; 13 | ssize_t read(const ByteArray::ptr& ba, size_t length) override; 14 | /*virtual*/ ssize_t write(const void* buffer, size_t length) override; 15 | ssize_t write(const ByteArray::ptr& ba, size_t length) override; 16 | virtual void close() override; 17 | 18 | auto& getSocket() const { return sock_; } 19 | bool isConnected() const; 20 | 21 | Address::ptr getLocalAddress() const { 22 | return sock_ ? sock_->getLocalAddress() : nullptr; 23 | } 24 | Address::ptr getRemoteAddress() const { 25 | return sock_ ? sock_->getRemoteAddress() : nullptr; 26 | } 27 | std::string getLocalAddressString() const { 28 | auto localAddress = getLocalAddress(); 29 | return localAddress ? localAddress->toString() : ""; 30 | } 31 | std::string getRemoteAddressString() const { 32 | auto remoteAddress = getRemoteAddress(); 33 | return remoteAddress ? remoteAddress->toString() : ""; 34 | } 35 | 36 | protected: 37 | Socket::ptr sock_; 38 | bool owner_; 39 | }; 40 | 41 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/stream/stream.cpp: -------------------------------------------------------------------------------- 1 | #include "stream.h" 2 | #include "flexy/util/log.h" 3 | 4 | namespace flexy { 5 | 6 | static auto g_logger = FLEXY_LOG_NAME("system"); 7 | 8 | ssize_t Stream::readFixSize(void* buffer, size_t length) { 9 | size_t offset = 0; 10 | size_t left = length; 11 | while (left > 0) { 12 | ssize_t len = read((char*)buffer + offset, left); 13 | if (len <= 0) { 14 | FLEXY_LOG_FMT_ERROR( 15 | g_logger, 16 | "readFixSize fail length = {} len = {} errno = {} errstr = {}", 17 | length, len, errno, strerror(errno)); 18 | return len; 19 | } 20 | offset += len; 21 | left -= len; 22 | } 23 | return length; 24 | } 25 | 26 | ssize_t Stream::readFixSize(const ByteArray::ptr& ba, size_t length) { 27 | size_t left = length; 28 | while (left > 0) { 29 | ssize_t len = read(ba, left); 30 | if (len <= 0) { 31 | FLEXY_LOG_FMT_ERROR( 32 | g_logger, 33 | "readFixSize fail length = {} len = {} errno = {} errstr = {}", 34 | length, len, errno, strerror(errno)); 35 | return len; 36 | } 37 | left -= len; 38 | } 39 | return length; 40 | } 41 | 42 | ssize_t Stream::writeFixSize(const void* buffer, size_t length) { 43 | size_t offset = 0; 44 | size_t left = length; 45 | while (left > 0) { 46 | ssize_t len = write((char*)buffer + offset, left); 47 | if (len <= 0) { 48 | FLEXY_LOG_FMT_ERROR( 49 | g_logger, 50 | "writeFixSize fail length = {} len = {} errno = {} errstr = {}", 51 | length, len, errno, strerror(errno)); 52 | return len; 53 | } 54 | offset += len; 55 | left -= len; 56 | } 57 | return length; 58 | } 59 | 60 | ssize_t Stream::writeFixSize(const ByteArray::ptr& ba, size_t length) { 61 | size_t left = length; 62 | while (left > 0) { 63 | ssize_t len = read(ba, left); 64 | if (len <= 0) { 65 | FLEXY_LOG_FMT_ERROR( 66 | g_logger, 67 | "writeFixSize fail length = {} len = {} errno = {} errstr = {}", 68 | length, len, errno, strerror(errno)); 69 | return len; 70 | } 71 | left -= len; 72 | } 73 | return length; 74 | } 75 | 76 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/stream/stream.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "flexy/net/bytearray.h" 4 | 5 | namespace flexy { 6 | 7 | class Stream { 8 | public: 9 | virtual ~Stream() = default; 10 | virtual ssize_t read(void* buffer, size_t length) = 0; 11 | virtual ssize_t read(const ByteArray::ptr& ba, size_t length) = 0; 12 | virtual ssize_t readFixSize(void* buffer, size_t length); 13 | virtual ssize_t readFixSize(const ByteArray::ptr& ba, size_t length); 14 | virtual ssize_t write(const void* buffer, size_t length) = 0; 15 | virtual ssize_t write(const ByteArray::ptr& ba, size_t length) = 0; 16 | virtual ssize_t writeFixSize(const void* buffer, size_t length); 17 | virtual ssize_t writeFixSize(const ByteArray::ptr& ba, size_t length); 18 | virtual void close() = 0; 19 | }; 20 | 21 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "thread/atomic.h" 4 | #include "thread/blocking_queue.h" 5 | #include "thread/condition_variable.h" 6 | #include "thread/mutex.h" 7 | #include "thread/semaphore.h" 8 | #include "thread/this_thread.h" 9 | #include "thread/thread.h" -------------------------------------------------------------------------------- /flexy/thread/atomic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace flexy { 6 | 7 | class Atomic { 8 | public: 9 | // 将 v 加到 t 上,结果更新到 t,并返回操作之后新 t 的值 10 | template 11 | static T addFetch(volatile T& t, S v = 1) { 12 | return __sync_add_and_fetch(&t, (T)v); 13 | } 14 | // 从 t 减去 v,结果更新到 t,并返回操作之后新 t 的值 15 | template 16 | static T subFetch(volatile T& t, S v = 1) { 17 | return __sync_sub_and_fetch(&t, (T)v); 18 | } 19 | // 将 t 与 v 相或, 结果更新到 t,并返回操作之后新 t的值 20 | template 21 | static T orFetch(volatile T& t, S v) { 22 | return __sync_or_and_fetch(&t, (T)v); 23 | } 24 | // 将 t 与 v 相与,结果更新到 t ,并返回操作之后新 t的值 25 | template 26 | static T andFetch(volatile T& t, S v) { 27 | return __sync_and_and_fetch(&t, (T)v); 28 | } 29 | // 将 t 与 v 异或,结果更新到 t ,并返回操作之后新 t 的值 30 | template 31 | static T xorFetch(volatile T& t, S v) { 32 | return __sync_xor_and_fetch(&t, (T)v); 33 | } 34 | // 将 v 取反后,与 v 相与,结果更新到 t,并返回操作之后新 t 的值 35 | template 36 | static T nandFetch(volatile T& t, S v) { 37 | return __sync_nand_and_fetch(&t, (T)v); 38 | } 39 | // 将 v 加到 t 上,结果更新到 t,并返回操作之前 t 的值 40 | template 41 | static T fetchAdd(volatile T& t, S v = 1) { 42 | return __sync_fetch_and_add(&t, (T)v); 43 | } 44 | // 从 t 减去 v,结果更新到 t,并返回操作之前 t 的值 45 | template 46 | static T fetchSub(volatile T& t, S v = 1) { 47 | return __sync_fetch_and_sub(&t, (T)v); 48 | } 49 | // 将 t 与 v 相或,结果更新到 t, 并返回操作之前t的值 50 | template 51 | static T fetchOr(volatile T& t, S v) { 52 | return __sync_fetch_and_or(&t, (T)v); 53 | } 54 | // 将 t 与 v 相与,结果更新到 t,并返回操作之前 t 的值 55 | template 56 | static T fetchAnd(volatile T& t, S v) { 57 | return __sync_fetch_and_and(&t, (T)v); 58 | } 59 | // 将 t 与 v 异或,结果更新到 t,并返回操作之前 t 的值 60 | template 61 | static T fetchXor(volatile T& t, S v) { 62 | return __sync_fetch_and_xor(&t, (T)v); 63 | } 64 | // 将 t 取反后,与 v 相与,结果更新到 t,并返回操作之前 t 的值 65 | template 66 | static T fetchNand(volatile T& t, S v) { 67 | return __sync_fetch_and_nand(&t, (T)v); 68 | } 69 | // 比较 t 和 old_val的值,如果两者相等,则将new_val更新到 t 并返回操作之前的 70 | // t 71 | template 72 | static T compareAndSwap(volatile T& t, S&& old_val, Q&& new_val) { 73 | return __sync_val_compare_and_swap(&t, (T)old_val, (T)new_val); 74 | } 75 | // 比较 t 和 old_val的值,如果两者相等,则将new_val更新到 t 并返回true 76 | template 77 | static bool compareAndSwapBool(volatile T& t, S&& old_val, Q&& new_val) { 78 | return __sync_bool_compare_and_swap(&t, (T)old_val, (T)new_val); 79 | } 80 | }; 81 | 82 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/thread/blocking_queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/schedule/semaphore.h" 4 | 5 | namespace flexy { 6 | 7 | template 8 | class BlockingQueue { 9 | public: 10 | using ptr = std::shared_ptr; 11 | using data_type = std::shared_ptr<_Tp>; 12 | 13 | size_t push(const data_type& data) { 14 | size_t size = 0; 15 | { 16 | LOCK_GUARD(mutex_); 17 | datas_.push_back(data); 18 | size = datas_.size(); 19 | } 20 | sem_.post(); 21 | return size; 22 | } 23 | 24 | data_type pop() { 25 | sem_.wait(); 26 | LOCK_GUARD(mutex_); 27 | auto v = std::move(datas_.front()); 28 | datas_.pop_front(); 29 | return v; 30 | } 31 | 32 | size_t size() { 33 | LOCK_GUARD(mutex_); 34 | return datas_.size(); 35 | } 36 | 37 | bool empty() { 38 | LOCK_GUARD(mutex_); 39 | return datas_.empty(); 40 | } 41 | 42 | private: 43 | fiber::Semaphore sem_; 44 | mutable Spinlock mutex_; 45 | std::deque datas_; 46 | }; 47 | 48 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/thread/condition_variable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace flexy { 6 | 7 | using condition_variable = std::condition_variable; 8 | 9 | } -------------------------------------------------------------------------------- /flexy/thread/mutex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/util/noncopyable.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #if __cpp_deduction_guides >= 201606 10 | #define LOCK_GUARD(x) std::lock_guard lk(x) 11 | #else 12 | #define LOCK_GUARD(x) std::lock_guard> lk(x) 13 | #endif 14 | 15 | namespace flexy { 16 | 17 | using rw_mutex = std::shared_mutex; 18 | using spin_rw_mutex = rw_mutex; 19 | 20 | template 21 | using lock_guard = std::lock_guard; 22 | 23 | template 24 | using unique_lock = std::unique_lock; 25 | 26 | template 27 | using scoped_lock = std::scoped_lock; 28 | 29 | template 30 | using ReadLock = std::shared_lock; 31 | template 32 | using WriteLock = std::lock_guard; // 不支持手动 unlock 33 | template 34 | using WriteLock2 = std::unique_lock; // 可手动 unlock 35 | 36 | #if __cplusplus > 201703L 37 | #define READLOCK(x) ReadLock lk(x) // cpp20 类别名模板实参推导 38 | #define WRITELOCK(x) WriteLock lk(x) 39 | #define WRITELOCK2(x) WriteLock2 lk(x) 40 | #elif __cpp_deduction_guides >= 201606 41 | #define READLOCK(x) std::shared_lock lk(x) // cpp17 类模板实参推导 (CTAD) 42 | #define WRITELOCK(x) std::lock_guard lk(x) 43 | #define WRITELOCK2(x) std::unique_lock lk(x) 44 | #else 45 | #define READLOCK(x) ReadLock> lk(x) 46 | #define WRITELOCK(x) WriteLock> lk(x) 47 | #define WRITELOCK2(x) WriteLock2> lk(x) 48 | #endif 49 | 50 | // TODO: boost::upgrade_lock 可从读锁直接升级为写锁 51 | 52 | using mutex = std::mutex; 53 | 54 | class NullMutex : noncopyable { 55 | public: 56 | NullMutex() = default; 57 | ~NullMutex() = default; 58 | void lock() {} 59 | void unlock() {} 60 | }; 61 | 62 | class Spinlock : noncopyable { 63 | public: 64 | Spinlock() { 65 | pthread_spin_init(&mutex_, 0); 66 | } 67 | ~Spinlock() { 68 | pthread_spin_destroy(&mutex_); 69 | } 70 | void lock() { 71 | pthread_spin_lock(&mutex_); 72 | } 73 | void unlock() { 74 | pthread_spin_unlock(&mutex_); 75 | } 76 | private: 77 | pthread_spinlock_t mutex_; 78 | }; 79 | 80 | class CASlock : noncopyable { 81 | public: 82 | CASlock() = default; 83 | ~CASlock() = default; 84 | void lock() { 85 | while (m_mutex.test_and_set(std::memory_order_acquire)) 86 | ; 87 | } 88 | void unlock() { m_mutex.clear(std::memory_order_release); } 89 | 90 | private: 91 | std::atomic_flag m_mutex = ATOMIC_FLAG_INIT; 92 | }; 93 | 94 | } // namespace flexy 95 | -------------------------------------------------------------------------------- /flexy/thread/semaphore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "flexy/util/noncopyable.h" 4 | #include 5 | #include 6 | #include 7 | 8 | namespace flexy { 9 | 10 | class Semaphore : noncopyable { 11 | public: 12 | Semaphore(uint32_t count = 0) { 13 | if (sem_init(&semaphore_, 0, count)) { 14 | throw std::logic_error("sem_init error"); 15 | } 16 | } 17 | ~Semaphore() { 18 | sem_destroy(&semaphore_); 19 | } 20 | void wait() { 21 | if (sem_wait(&semaphore_)) { 22 | throw std::logic_error("sem_wait error"); 23 | } 24 | } 25 | void post() { 26 | if (sem_post(&semaphore_)) { 27 | throw std::logic_error("sem_post error"); 28 | } 29 | } 30 | private: 31 | Semaphore(Semaphore&& ) = delete; 32 | private: 33 | sem_t semaphore_; 34 | }; 35 | 36 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/thread/this_thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "flexy/net/hook.h" 4 | #include "thread.h" 5 | 6 | namespace flexy::this_thread { 7 | 8 | inline pid_t get_id() noexcept { return Thread::GetThreadId(); } 9 | 10 | template 11 | inline void sleep_for(const std::chrono::duration<_Rep, _Period>& __rtime) { 12 | if (__rtime <= __rtime.zero()) { 13 | return; 14 | } 15 | auto __s = std::chrono::duration_cast(__rtime); 16 | auto __ns = 17 | std::chrono::duration_cast(__rtime - __s); 18 | struct ::timespec __ts = {static_cast(__s.count()), 19 | static_cast(__ns.count())}; 20 | while (nanosleep_f(&__ts, &__ts) == -1 && 21 | errno == EINTR) // 使用hook前的nanosleep 22 | { 23 | } 24 | } 25 | 26 | template 27 | inline void sleep_until( 28 | const std::chrono::time_point<_Clock, _Duration>& __atime) { 29 | #if __cplusplus > 201703L 30 | static_assert(std::chrono::is_clock_v<_Clock>); 31 | #endif 32 | auto __now = _Clock::now(); 33 | if (_Clock::is_steady) { 34 | if (__now < __atime) { 35 | sleep_for(__atime - __now); 36 | } 37 | return; 38 | } 39 | while (__now < __atime) { 40 | sleep_for(__atime - __now); 41 | __now = _Clock::now(); 42 | } 43 | } 44 | 45 | inline void yield() noexcept { __gthread_yield(); } 46 | 47 | } // namespace flexy::this_thread -------------------------------------------------------------------------------- /flexy/thread/thread.cpp: -------------------------------------------------------------------------------- 1 | #include "thread.h" 2 | #include "flexy/util/util.h" 3 | 4 | namespace flexy { 5 | 6 | static thread_local Thread* t_thread = nullptr; 7 | static thread_local std::string t_name = "UNKOWN"; 8 | static thread_local uint64_t t_start = 0; // 线程开始运行的时间 毫秒数 9 | static thread_local int t_id = -1; 10 | 11 | Thread::~Thread() { 12 | if (thread_) { 13 | pthread_detach(thread_); 14 | } 15 | } 16 | 17 | void Thread::join() { 18 | if (thread_) { 19 | pthread_join(thread_, nullptr); 20 | thread_ = 0; 21 | } 22 | } 23 | 24 | Thread* Thread::GetThis() { 25 | return t_thread; 26 | } 27 | 28 | const std::string& Thread::GetName() { 29 | return t_name; 30 | } 31 | 32 | uint64_t Thread::GetStartTime() { return t_start; } 33 | 34 | pid_t Thread::GetThreadId() { 35 | if (t_id == -1) { 36 | // t_id = syscall(SYS_gettid); 37 | t_id = gettid(); 38 | } 39 | return t_id; 40 | } 41 | 42 | void Thread::SetName(std::string_view name) { 43 | if (name.empty()) { 44 | return; 45 | } 46 | if (t_thread) { 47 | t_thread->name_ = name; 48 | } 49 | t_name = name; 50 | } 51 | 52 | void* Thread::run(void* arg) { 53 | Thread* thread = (Thread*)arg; 54 | t_thread = thread; 55 | t_name = thread->name_; 56 | thread->tid_ = Thread::GetThreadId(); 57 | pthread_setname_np(pthread_self(), thread->name_.substr(0, 15).c_str()); 58 | 59 | thread->semapthore_.post(); // 确保Thread创建时就已经运行起来 60 | t_start = GetTimeMs(); 61 | thread->cb_(); 62 | return nullptr; 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /flexy/thread/thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "flexy/util/task.h" 5 | #include "semaphore.h" 6 | 7 | namespace flexy { 8 | 9 | class Thread : noncopyable { 10 | public: 11 | using ptr = std::shared_ptr; 12 | // 线程构造函数 13 | template 14 | Thread(std::string_view name, Args&&... args) : name_(name), 15 | cb_(std::forward(args)...) { 16 | if (name.empty()) { 17 | name_ = "UNKOWN"; 18 | } 19 | pthread_create(&thread_, nullptr, &Thread::run, this); 20 | semapthore_.wait(); 21 | } 22 | ~Thread(); 23 | // 返回线程真实id 24 | pid_t getId() const { return tid_; } 25 | // 返回线程名称 26 | const std::string& getName() const { return name_; } 27 | // 等待线程结束 28 | void join(); 29 | // 返回当期线程 30 | static Thread* GetThis(); 31 | // 返回当前线程的名称 32 | static const std::string& GetName(); 33 | // 设置当前线程的名称 34 | static void SetName(std::string_view name); 35 | // 返回当前线程开始的毫秒数 不包括主线程 36 | static uint64_t GetStartTime(); 37 | // 返回当前线程真实id 38 | static pid_t GetThreadId(); 39 | 40 | private: 41 | static void* run(void* arg); // 线程真正执行函数 42 | private: 43 | pid_t tid_ = -1; // 线程真实id 44 | pthread_t thread_; // 线程结构 45 | std::string name_; // 线程名称 46 | detail::__task cb_; // 线程执行函数 47 | Semaphore semapthore_; // 信号量 48 | }; 49 | 50 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "util/config.h" 4 | #include "util/file.h" 5 | #include "util/lexical.h" 6 | #include "util/log.h" 7 | #include "util/macro.h" 8 | #include "util/noncopyable.h" 9 | #include "util/singleton.h" 10 | #include "util/task.h" 11 | #include "util/util.h" -------------------------------------------------------------------------------- /flexy/util/align.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace flexy { 6 | 7 | constexpr std::size_t max_align_v = alignof(max_align_t); 8 | 9 | } -------------------------------------------------------------------------------- /flexy/util/assert.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "likely.h" 5 | #include "log.h" 6 | #include "util.h" 7 | 8 | inline auto __assert_logger_ = FLEXY_LOG_NAME("system"); 9 | 10 | #define FLEXY_ASSERT(x) \ 11 | if (FLEXY_UNLIKELY(!(x))) { \ 12 | FLEXY_LOG_ERROR(__assert_logger_) \ 13 | << "ASSERTION: " << #x << "\nbacktrace:\n" \ 14 | << flexy::BacktraceToString(100, 2, " "); \ 15 | assert(x); \ 16 | } 17 | 18 | #define FLEXY_ASSERT2(x, w) \ 19 | if (FLEXY_UNLIKELY((!(x)))) { \ 20 | FLEXY_LOG_ERROR(__assert_logger_) \ 21 | << "ASSERTION: " << #x << '\n' \ 22 | << w << "\nbacktrace:\n" \ 23 | << flexy::BacktraceToString(100, 2, " "); \ 24 | assert(x); \ 25 | } 26 | -------------------------------------------------------------------------------- /flexy/util/hash_util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace flexy { 6 | 7 | std::string base64decode(const std::string& src); 8 | std::string base64encode(const std::string& data); 9 | std::string base64encode(const void* data, size_t len); 10 | 11 | std::string sha1sum(const std::string& data); 12 | std::string sha1sum(const void* data, size_t len); 13 | 14 | } // namespace flexy -------------------------------------------------------------------------------- /flexy/util/likely.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined __GNUC__ || defined __llvm__ 4 | // 告诉编译器优化,条件大概率成立 5 | #define FLEXY_LIKELY(x) __builtin_expect(!!(x), 1) 6 | // 告诉编译器优化,条件大概率不成立 7 | #define FLEXY_UNLIKELY(x) __builtin_expect(!!(x), 0) 8 | #else 9 | #define FLEXY_LIKELY(x) (x) 10 | #define FLEXY_UNLIKELY(x) (x) 11 | #endif -------------------------------------------------------------------------------- /flexy/util/macro.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "assert.h" 4 | #include "likely.h" 5 | -------------------------------------------------------------------------------- /flexy/util/memory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace flexy { 6 | 7 | // 两个缺点 8 | // 1. _Tp 不能是final 9 | // 2. _Tp 的构造函数不能是 private 10 | // 优点 11 | // _Tp的构造函数可以是protected 12 | // template 13 | // std::shared_ptr<_Tp> make_shared(_Args &&...__args) { 14 | // struct EnableMakeShared : public _Tp { 15 | // EnableMakeShared(_Args&&... args) 16 | // : _Tp(std::forward<_Args>(args)...) 17 | // {} 18 | // }; 19 | // return std::static_pointer_cast<_Tp>(std::make_shared 20 | // (std::forward<_Args>(__args)...)); 21 | // } 22 | 23 | } -------------------------------------------------------------------------------- /flexy/util/noncopyable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace flexy { 4 | 5 | class noncopyable { 6 | public: 7 | noncopyable() = default; 8 | ~noncopyable() = default; 9 | private: 10 | noncopyable(const noncopyable& ) = delete; 11 | noncopyable& operator=(const noncopyable& ) = delete; 12 | }; 13 | 14 | } -------------------------------------------------------------------------------- /flexy/util/singleton.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace flexy { 6 | 7 | template 8 | class Singleton { 9 | public: 10 | static T& GetInstance() { 11 | static T v; 12 | return v; 13 | } 14 | }; 15 | 16 | template 17 | class SingletonPtr { 18 | public: 19 | static std::shared_ptr GetInstance() { 20 | static auto v = std::make_shared(); 21 | return v; 22 | } 23 | }; 24 | 25 | } -------------------------------------------------------------------------------- /flexy/util/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace flexy { 9 | 10 | // 获得当前线程真实id 11 | int GetThreadId(); 12 | // 获取线程启动到现在的毫秒 13 | uint32_t GetThreadElapse(); 14 | // 获得线程名称 15 | const std::string& GetThreadName(); 16 | // 获得协程id 17 | uint32_t GetFiberId(); 18 | // 获得当前系统时间微秒数 19 | uint64_t GetTimeUs(); 20 | // 获得当前系统时间毫秒数 21 | uint64_t GetTimeMs(); 22 | // 获得当前Steady时钟微秒数 23 | uint64_t GetSteadyUs(); 24 | // 获得当前Steady时钟毫秒数 25 | uint64_t GetSteadyMs(); 26 | // 时间秒数 装换为 时间字符串 27 | std::string TimeToStr(time_t ts = time(0), 28 | const std::string& fmt = "%Y-%m-%d %H:%M:%S"); 29 | // 时间字符串 转换为 时间秒数 30 | time_t StrToTime(const char* str, const char* fmt = "%Y-%m-%d %H:%M:%S"); 31 | // 从 map m 中获得 key 为 k 的 value(type to V) 32 | template 33 | V GetParamValue(const Map& m, const K& k, const V& def = V()) { 34 | auto it = m.find(k); 35 | if (it == m.end()) { 36 | return def; 37 | } 38 | try { 39 | return boost::lexical_cast(it->second); 40 | } catch (...) { 41 | } 42 | return def; 43 | } 44 | 45 | template 46 | bool CheckGetParamValue(const Map& m, const K& k, V& v) { 47 | auto it = m.find(k); 48 | if (it == m.end()) { 49 | return false; 50 | } 51 | try { 52 | v = boost::lexical_cast(it->second); 53 | return true; 54 | } catch (...) { 55 | } 56 | return false; 57 | } 58 | 59 | // c语言格式化字符串 -> std::string 60 | std::string format(const char* fmt, ...); 61 | std::string format(const char* fmt, va_list ap); 62 | // 查找元素 [begin, end) 找不到返回end, find_first -> 是否从前往后找 63 | template 64 | Iter find(Iter&& begin, Iter&& end, T&& val, bool find_first = true) { 65 | if (find_first) { 66 | for (auto it = begin; it != end; ++it) { 67 | if (*it == val) { 68 | return it; 69 | } 70 | } 71 | } else { 72 | for (auto it = end - 1; it != begin; --it) { 73 | if (*it == val) { 74 | return it; 75 | } 76 | } 77 | } 78 | return end; 79 | } 80 | // 将[begin, end)范围中的字符转换为整数 81 | int64_t atoi(const char* begin, const char* end); 82 | //获得函数调用堆栈信息 83 | void Backtrace(std::vector& bt, int size = 64, int skip = 1); 84 | //获得函数调用堆栈信息字符串 85 | std::string BacktraceToString(int size = 64, int skip = 2, const std::string& prefix = ""); 86 | 87 | // TODO locale 88 | // 大小写转换 89 | char ToLower(char c); 90 | char ToUpper(char c); 91 | 92 | template 93 | std::conditional_t ToLower( 94 | std::conditional_t s) { 95 | if constexpr (copy) { 96 | std::string ret(*s); 97 | std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); 98 | return ret; 99 | } else { 100 | std::transform(s->begin(), s->end(), s->begin(), ::tolower); 101 | } 102 | } 103 | 104 | template 105 | std::conditional_t ToUpper( 106 | std::conditional_t s) { 107 | if constexpr (copy) { 108 | std::string ret(*s); 109 | std::transform(ret.begin(), ret.end(), ret.begin(), ::toupper); 110 | return ret; 111 | } else { 112 | std::transform(s->begin(), s->end(), s->begin(), ::toupper); 113 | } 114 | } 115 | 116 | std::string ToLower(std::string_view s); 117 | 118 | std::string ToUpper(std::string_view s); 119 | 120 | std::string_view Trim(std::string_view str, 121 | std::string_view delimit = "\t\r\n"); 122 | std::string_view TrimLeft(std::string_view str, 123 | std::string_view delimit = "\t\r\n"); 124 | std::string_view TrimRight(std::string_view str, 125 | std::string_view delimit = "\t\r\n"); 126 | std::vector Split(std::string_view s, std::string_view delim, 127 | bool keep_empty = false); 128 | 129 | inline std::vector Split(std::string_view s, char delim, 130 | bool keep_empty = false) { 131 | return Split(s, std::string_view(&delim, 1), keep_empty); 132 | } 133 | } -------------------------------------------------------------------------------- /output/conf/mysql.yml: -------------------------------------------------------------------------------- 1 | mysql: 2 | host: "121.41.169.58" 3 | user: root 4 | dbname: "blog" 5 | # host: 127.0.0.1 6 | # user: arlent 7 | passwd: '1' 8 | 9 | dbs: 10 | test_0: 11 | host: "121.41.169.58" 12 | user: root 13 | dbname: "blog" 14 | passwd: '1' 15 | -------------------------------------------------------------------------------- /output/conf/server.yml: -------------------------------------------------------------------------------- 1 | servers: 2 | - address: ["0.0.0.0:8090", "127.0.0.1:8091", "/tmp/test.sock"] 3 | keepalive: 1 4 | timeout: 1000 5 | name: flexy/1.1 6 | accept_worker: accept 7 | io_worker: io 8 | process_worker: io 9 | type: http 10 | - address: ["0.0.0.0:8072", "localhost:8071"] 11 | keepalive: 1 12 | timeout: 1000 13 | name: flexy/2.1 14 | accept_worker: accept 15 | io_worker: io 16 | process_worker: io 17 | type: ws 18 | 19 | server: 20 | work_path: /home/arlent/workspace/project/flexy/bin/work/ 21 | -------------------------------------------------------------------------------- /output/conf/sqlite3.yml: -------------------------------------------------------------------------------- 1 | sqlite3: 2 | dbs: 3 | test_0: 4 | path: "db/test1.db" 5 | # sql: "create table user ( 6 | # id integer primary key autoincrement, 7 | # name varchar(50) not null default '', 8 | # age int not null default 0 9 | # )" 10 | test_1: 11 | path: "db/test2" 12 | -------------------------------------------------------------------------------- /output/conf/test.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4kangjc/flexy/7ed1f9b526801695a8b07729de2af77e419ac3d9/output/conf/test.yml -------------------------------------------------------------------------------- /output/conf/worker.yml: -------------------------------------------------------------------------------- 1 | workers: 2 | io: 3 | thread_num: 4 4 | accept: 5 | thread_num: 1 6 | -------------------------------------------------------------------------------- /tests/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("//bazel/config:copt.bzl", "FLEXY_COPTS") 2 | 3 | cc_test( 4 | name = "test_function", 5 | srcs = ["test_function.cc"], 6 | deps = [ 7 | "//:flexy", 8 | "@com_google_googletest//:gtest", 9 | ], 10 | copts = FLEXY_COPTS, 11 | ) 12 | 13 | cc_test( 14 | name = "test_task", 15 | srcs = ["test_task.cc"], 16 | deps = [ 17 | "//:flexy", 18 | "@com_google_googletest//:gtest", 19 | ], 20 | copts = FLEXY_COPTS, 21 | ) 22 | 23 | # cc_test( 24 | # name = "test_file", 25 | # srcs = ["test_file.cc"], 26 | # deps = [ 27 | # "//:flexy", 28 | # "@com_google_googletest//:gtest", 29 | # ], 30 | # copts = FLEXY_COPTS, 31 | # ) 32 | 33 | cc_test( 34 | name = "test_fiber", 35 | srcs = ["test_fiber.cc"], 36 | deps = [ 37 | "//:flexy", 38 | "@com_google_googletest//:gtest", 39 | ], 40 | copts = FLEXY_COPTS, 41 | ) 42 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(${PROJECT_SOURCE_DIR}/cmake/utils.cmake) 2 | 3 | flexy_test_executable(test_thread "test_thread.cc" "${LIBS}") 4 | flexy_test_executable(test_log "test_logger.cc" "${LIBS}") 5 | flexy_test_executable(test_file "test_file.cc" "${GTEST_LIBS}") 6 | flexy_test_executable(test_fiber "test_fiber.cc" "${GTEST_LIBS}") 7 | flexy_test_executable(test_scheduler "test_scheduler.cc" "${LIBS}") 8 | flexy_test_executable(test_task "test_task.cc" "${GTEST_LIBS}") 9 | flexy_test_executable(test_config "test_config.cc" "${LIBS}") 10 | flexy_test_executable(test_timer "test_timer.cc" "${LIBS}") 11 | flexy_test_executable(test_iomanager "test_iomanager.cc" "${LIBS}") 12 | flexy_test_executable(test_hook "test_hook.cc" "${LIBS}") 13 | flexy_test_executable(test_address "test_address.cc" "${LIBS}") 14 | flexy_test_executable(test_socket "test_socket.cc" "${LIBS}") 15 | flexy_add_executable(test_tcp_server "test_tcp_server.cc" "${LIBS}") 16 | flexy_add_executable(test_signal "test_signal.cc" "${LIBS}") 17 | flexy_test_executable(test_go "test_go.cc" "${LIBS}") 18 | flexy_add_executable(test_logconf "test_logconfig.cc" "${LIBS}") 19 | flexy_test_executable(test_http "test_http.cc" "${LIBS}") 20 | flexy_test_executable(test_http_parser "test_http_parser.cc" "${LIBS}") 21 | flexy_add_executable(test_http_session "test_http_session.cc" "${LIBS}") 22 | flexy_test_executable(test_env "test_env.cc" "${LIBS}") 23 | flexy_test_executable(test_deamon "test_daemon.cc" "${LIBS}") 24 | flexy_add_executable(test_application "test_application.cc" "${LIBS}") 25 | flexy_add_executable(test_async_io "test_async_io.cc" "${LIBS}") 26 | flexy_add_executable(test_mysql "test_mysql.cc" "${LIBS}") 27 | flexy_add_executable(test_sqlite3 "test_sqlite3.cc" "${LIBS}") 28 | flexy_test_executable(test_worker "test_worker.cc" "${LIBS}") 29 | flexy_test_executable(test_hash_util "test_hash_util.cc" "${LIBS}") 30 | flexy_add_executable(test_ws_server "test_ws_server.cc" "${LIBS}") 31 | flexy_test_executable(test_fiber_mutex "test_fiber_mutex.cc" "${LIBS}") 32 | flexy_test_executable(test_this_fiber "test_this_fiber.cc" "${LIBS}") 33 | # flexy_test_executable(test_fiber_condition_variable "test_fiber_condition_variable.cc" "${LIBS}") 34 | flexy_test_executable(test_function "test_function.cc" "${GTEST_LIBS}") -------------------------------------------------------------------------------- /tests/test_address.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static auto g_logger = FLEXY_LOG_ROOT(); 5 | 6 | void testIPv6() { 7 | auto addr = flexy::IPv6Address::Create("2001:0db8:85a3::8a2e:0370:7334", 12345); 8 | FLEXY_LOG_INFO(g_logger) << "IPAddress = " << addr->toString(); 9 | FLEXY_LOG_INFO(g_logger) 10 | << "broadcastAddress = " << addr->broadcastAddress(44)->toString(); 11 | FLEXY_LOG_INFO(g_logger) 12 | << "networkAddress = " << addr->networkAddress(44)->toString(); 13 | FLEXY_LOG_INFO(g_logger) 14 | << "subnetMask = " << addr->subnetMask(44)->toString(); 15 | } 16 | 17 | void testUnixAddress() { 18 | flexy::UnixAddress ua; 19 | FLEXY_LOG_INFO(g_logger) << ua.getAddrLen(); 20 | } 21 | 22 | void testIpv4() { 23 | auto addr = flexy::IPv4Address::Create("172.17.237.213", 23); 24 | if (addr) { 25 | FLEXY_LOG_INFO(g_logger) << "IPAddress = " << addr->toString(); 26 | FLEXY_LOG_INFO(g_logger) 27 | << "broadcastAddress = " << addr->broadcastAddress(20)->toString(); 28 | FLEXY_LOG_INFO(g_logger) 29 | << "networkAddress = " << addr->networkAddress(20)->toString(); 30 | FLEXY_LOG_INFO(g_logger) 31 | << "subnetMask = " << addr->subnetMask(20)->toString(); 32 | } 33 | // auto baidu = flexy::IPAddress::Create("www.baidu.com"); 34 | auto baidu = flexy::IPAddress::Create("103.235.46.39", 80); 35 | if (baidu) { 36 | FLEXY_LOG_INFO(g_logger) << baidu->toString(); 37 | } 38 | } 39 | 40 | void test() { 41 | auto addpsptr = flexy::Address::Lookup("www.baidu.com:ftp"); 42 | FLEXY_ASSERT(addpsptr); 43 | for (auto& addr : *addpsptr) { 44 | FLEXY_LOG_INFO(g_logger) << "www.baidu.com ==> " << addr->toString(); 45 | } 46 | } 47 | 48 | void test_iface() { 49 | auto opt = flexy::Address::GetInterfaceAddress(); 50 | FLEXY_ASSERT(opt); 51 | bool flag = false; 52 | const char* sv = nullptr; 53 | for (auto& [x, y] : *opt) { 54 | if (!flag) { 55 | sv = x.c_str(); 56 | flag = true; 57 | } 58 | FLEXY_LOG_INFO(g_logger) << x << " - " << y.first->toString() << " - " << y.second; 59 | } 60 | auto wlan0 = flexy::Address::GetInterfaceAddress(sv); 61 | if (!wlan0) { 62 | FLEXY_LOG_INFO(g_logger) << "No such name as " << sv; 63 | } 64 | for (auto& [x, y] : *wlan0) { 65 | FLEXY_LOG_INFO(g_logger) << x->toString() << " - " << y; 66 | } 67 | } 68 | 69 | int main() { 70 | testIPv6(); 71 | //testUnixAddress(); 72 | FLEXY_LOG_DEBUG(g_logger) << "--------------------------------------------------------"; 73 | testIpv4(); 74 | FLEXY_LOG_DEBUG(g_logger) << "---------------------------------------------------------"; 75 | test(); 76 | FLEXY_LOG_DEBUG(g_logger) << "---------------------------------------------------------"; 77 | test_iface(); 78 | } -------------------------------------------------------------------------------- /tests/test_application.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv) { 4 | flexy::Application app; 5 | if (!app.init(argc, argv)) { 6 | return -1; 7 | } 8 | return app.run(); 9 | } -------------------------------------------------------------------------------- /tests/test_async_io.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using flexy::async_cin; 4 | using flexy::async_cout; 5 | 6 | void test() { 7 | std::string s1, s2; 8 | async_cin >> s1; 9 | async_cout << s1 << std::endl; 10 | 11 | async_cin.Scan(s1, s2); 12 | async_cout.Print(s1, '\n', s2, '\n'); 13 | } 14 | 15 | void sleep_print() { 16 | static int i = 10; 17 | while (i--) { 18 | sleep(1); 19 | printf("\nhello async io\n"); 20 | } 21 | } 22 | 23 | int main() { 24 | flexy::IOManager iom; 25 | go test; 26 | go sleep_print; 27 | } -------------------------------------------------------------------------------- /tests/test_daemon.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static auto&& g_logger = FLEXY_LOG_ROOT(); 6 | 7 | int server_main(int argc, char** argv) { 8 | FLEXY_LOG_INFO(g_logger) << flexy::ProcessInfoMrg::GetInstance().toString(); 9 | flexy::Timer::ptr timer; 10 | flexy::IOManager iom; 11 | timer = iom.addRecTimer(1000, [&timer]() { 12 | FLEXY_LOG_INFO(g_logger) << "onTimer"; 13 | static int count = 0; 14 | if (++count > 10) { 15 | timer->cancel(); 16 | } 17 | }); 18 | return 0; 19 | }; 20 | 21 | int main(int argc, char** argv) { 22 | return flexy::start_daemon(argc, argv, server_main, argc != 1); 23 | } -------------------------------------------------------------------------------- /tests/test_env.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static auto&& g_logger = FLEXY_LOG_ROOT(); 7 | 8 | int main(int argc, char** argv) { 9 | flexy::EnvMgr::GetInstance().addHelp("s", "start with the terminal"); 10 | flexy::EnvMgr::GetInstance().addHelp("d", "run as daemon"); 11 | flexy::EnvMgr::GetInstance().addHelp("p", "print help"); 12 | if (!flexy::EnvMgr::GetInstance().init(argc, argv)) { 13 | flexy::EnvMgr::GetInstance().printHelp(); 14 | return 0; 15 | } 16 | std::cout << "exe = " << flexy::EnvMgr::GetInstance().getExe() << std::endl; 17 | std::cout << "cwd = " << flexy::EnvMgr::GetInstance().getCwd() << std::endl; 18 | std::cout << "path = " << flexy::EnvMgr::GetInstance().getEnv("PATH", "xxx") 19 | << std::endl; 20 | std::cout << "test = " << flexy::EnvMgr::GetInstance().getEnv("test") 21 | << std::endl; 22 | std::cout << "set env " 23 | << flexy::EnvMgr::GetInstance().setEnv("test", "yyy") 24 | << std::endl; 25 | std::cout << "test = " << flexy::EnvMgr::GetInstance().getEnv("test") 26 | << std::endl; 27 | 28 | if (flexy::EnvMgr::GetInstance().has("p")) { 29 | flexy::EnvMgr::GetInstance().printHelp(); 30 | } 31 | 32 | std::cout << "------------------------------------------------\n"; 33 | 34 | auto ss = flexy::EnvMgr::GetInstance().getAbsolutePath("conf"); 35 | std::cout << ss << std::endl; 36 | 37 | std::vector files; 38 | flexy::FS::ListAllFile(files, ss, ".yml"); 39 | 40 | std::cout << "total has " << files.size() << " files\n"; 41 | 42 | for (auto& str : files) { 43 | std::cout << str << std::endl; 44 | } 45 | 46 | return 0; 47 | } -------------------------------------------------------------------------------- /tests/test_fiber.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "flexy/fiber/fiber.h" 4 | #include "flexy/util/log.h" 5 | #include "flexy/util/memory.h" 6 | 7 | static auto&& g_logger = FLEXY_LOG_ROOT(); 8 | 9 | std::vector indexs; 10 | 11 | void run_in_fiber1() { 12 | indexs.push_back(2); 13 | 14 | flexy::Fiber::Yield(); 15 | 16 | indexs.push_back(11); 17 | } 18 | 19 | void run_in_fiber2() { 20 | indexs.push_back(4); 21 | // flexy::Fiber::Yield(); 22 | flexy::Fiber::GetThis()->yield_callback([]() { indexs.push_back(5); }); 23 | 24 | indexs.push_back(7); 25 | } 26 | 27 | void run_in_fiber3() { indexs.push_back(9); } 28 | 29 | TEST(Fiber, Main) { 30 | indexs.push_back(0); 31 | { 32 | flexy::Fiber::GetThis(); 33 | 34 | indexs.push_back(1); 35 | 36 | auto fiber1 = flexy::fiber_make_shared(run_in_fiber1); 37 | auto fiber2 = flexy::fiber_make_shared(run_in_fiber2); 38 | ASSERT_EQ(fiber1->getState(), flexy::Fiber::READY); 39 | ASSERT_EQ(fiber2->getState(), flexy::Fiber::READY); 40 | 41 | fiber1->resume(); 42 | 43 | indexs.push_back(3); 44 | fiber2->resume(); 45 | 46 | indexs.push_back(6); 47 | 48 | fiber2->resume(); 49 | 50 | indexs.push_back(8); 51 | 52 | ASSERT_EQ(fiber2->getState(), flexy::Fiber::TERM); 53 | fiber2->reset(run_in_fiber3); 54 | 55 | fiber2->resume(); 56 | ASSERT_EQ(fiber2->getState(), flexy::Fiber::TERM); 57 | 58 | indexs.push_back(10); 59 | 60 | fiber1->resume(); 61 | ASSERT_EQ(fiber1->getState(), flexy::Fiber::TERM); 62 | } 63 | indexs.push_back(12); 64 | 65 | for (int i = 0; i < (int)indexs.size(); ++i) { 66 | ASSERT_EQ(indexs[i], i); 67 | } 68 | } 69 | 70 | int main(int argc, char** argv) { 71 | testing::InitGoogleTest(&argc, argv); 72 | return RUN_ALL_TESTS(); 73 | } -------------------------------------------------------------------------------- /tests/test_fiber_condition_variable.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static auto g_logger = FLEXY_LOG_ROOT(); 4 | 5 | flexy::mutex m; 6 | flexy::fiber::condition_variable cv; 7 | std::string data; 8 | bool ready = false; 9 | bool processed = false; 10 | 11 | void worker_thread() { 12 | FLEXY_LOG_DEBUG(g_logger) << "Worker thread begin..."; 13 | // 等待直至 main() 发送数据 14 | flexy::unique_lock lk(m); 15 | cv.wait(lk, []() { return ready; }); 16 | 17 | // 等待后,我们占有锁。 18 | FLEXY_LOG_DEBUG(g_logger) << "Worker thread is processing data"; 19 | data += " after processing"; 20 | 21 | // 发送数据回 main() 22 | processed = true; 23 | FLEXY_LOG_DEBUG(g_logger) 24 | << "Worker thread signals data processing completed"; 25 | 26 | // 通知前完成手动解锁,以避免等待线程才被唤醒就阻塞 27 | lk.unlock(); 28 | cv.notify_one(); 29 | } 30 | 31 | int main1() { 32 | FLEXY_LOG_DEBUG(g_logger) << "main() thread begin..."; 33 | // flexy::IOManager iom(2); 34 | // go worker_thread; 35 | 36 | data = "Example data"; 37 | // 发送数据到 worker 线程 38 | { 39 | LOCK_GUARD(m); 40 | ready = true; 41 | FLEXY_LOG_DEBUG(g_logger) << "main() signals data ready for processing"; 42 | } 43 | cv.notify_one(); 44 | 45 | // 等候 worker 46 | { 47 | std::unique_lock lk(m); 48 | cv.wait(lk, [] { return processed; }); 49 | } 50 | FLEXY_LOG_DEBUG(g_logger) << "Back in main(), data = " << data; 51 | 52 | return 0; 53 | } 54 | 55 | int main() { 56 | flexy::IOManager iom(2); 57 | go worker_thread; 58 | go main1; 59 | } -------------------------------------------------------------------------------- /tests/test_fiber_mutex.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static auto& g_logger = FLEXY_LOG_ROOT(); 6 | 7 | int sum; 8 | 9 | flexy::fiber::mutex mutex; 10 | // flexy::NullMutex mutex; 11 | // flexy::mutex mutex; 12 | 13 | void add() { 14 | LOCK_GUARD(mutex); 15 | for (int i = 0; i < 1000000; ++i) { 16 | ++sum; 17 | } 18 | FLEXY_LOG_DEBUG(g_logger) << "add finish!"; 19 | } 20 | 21 | void diff() { 22 | LOCK_GUARD(mutex); 23 | for (int i = 0; i < 500000; ++i) { 24 | --sum; 25 | } 26 | FLEXY_LOG_DEBUG(g_logger) << "diff finish!"; 27 | } 28 | 29 | int main() { 30 | flexy::Scheduler iom(2); 31 | iom.start(); 32 | iom.async(add); 33 | iom.async(diff); 34 | iom.stop(); 35 | FLEXY_LOG_DEBUG(g_logger) << "sum = " << sum; 36 | } -------------------------------------------------------------------------------- /tests/test_file.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "flexy/util/file.h" 4 | #include "flexy/util/log.h" 5 | 6 | static auto&& g_logger = FLEXY_LOG_ROOT(); 7 | 8 | // TEST(File, AbsolutePath) { 9 | // ASSERT_EQ(flexy::filesystem::AbsolutePath("~"), getenv("HOME")); 10 | // FLEXY_LOG_INFO(g_logger) << flexy::FS::AbsolutePath("."); 11 | // ASSERT_EQ(flexy::filesystem::AbsolutePath("/bin"), "/bin"); 12 | // } 13 | 14 | TEST(File, Mkdir) { 15 | ASSERT_TRUE(flexy::filesystem::Mkdir("temp")); 16 | ASSERT_TRUE(flexy::filesystem::Mkdir("temp/kkk/mm")); 17 | } 18 | 19 | TEST(File, Mv) { 20 | ASSERT_TRUE(flexy::filesystem::Mv("temp/kkk/mm", "temp/kkk/fff")); 21 | } 22 | 23 | TEST(File, Rm) { 24 | ASSERT_TRUE(flexy::filesystem::Rm("temp")); 25 | ASSERT_TRUE(flexy::filesystem::Rm("temp/kkk/fff")); 26 | } 27 | 28 | TEST(File, ListAllFile) { 29 | std::vector vec; 30 | flexy::FS::ListAllFile(vec, "../flexy", ".cpp"); 31 | for (auto& file : vec) { 32 | FLEXY_LOG_INFO(g_logger) << file; 33 | } 34 | } 35 | 36 | int main(int argc, char** argv) { 37 | testing::InitGoogleTest(&argc, argv); 38 | return RUN_ALL_TESTS(); 39 | } -------------------------------------------------------------------------------- /tests/test_go.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static auto&& g_logger = FLEXY_LOG_ROOT(); 6 | 7 | void test(std::string_view s = "hello") { 8 | FLEXY_LOG_INFO(g_logger) << s; 9 | } 10 | 11 | void test1() { 12 | static int i = 5; 13 | FLEXY_LOG_INFO(g_logger) << "i = " << i; 14 | if (--i) { 15 | go test1; 16 | } 17 | } 18 | 19 | struct Add { 20 | void operator()(int a, int b) { 21 | FLEXY_LOG_INFO(g_logger) << "a + b = " << a + b; 22 | } 23 | }; 24 | 25 | int main() { 26 | flexy::IOManager iom; 27 | go []() { 28 | FLEXY_LOG_INFO(g_logger) << "Hello go"; 29 | }; 30 | go_args(test, "hello"); 31 | Add add; 32 | go_args(&Add::operator(), &add, 1, 2); 33 | go_args(add, 1, 2); 34 | go test1; 35 | 36 | } -------------------------------------------------------------------------------- /tests/test_hash_util.cc: -------------------------------------------------------------------------------- 1 | #include "flexy/util/hash_util.h" 2 | #include "flexy/util/log.h" 3 | #include "flexy/util/macro.h" 4 | 5 | static auto& g_logger = FLEXY_LOG_ROOT(); 6 | 7 | void test_base64() { 8 | for (int i = 0; i < 100000; ++i) { 9 | int len = rand() % 100; 10 | std::string src; 11 | src.resize(len); 12 | for (int j = 0; j < len; ++j) { 13 | src[j] = rand() % 255; 14 | } 15 | auto rst = flexy::base64encode(src); 16 | // FLEXY_LOG_INFO(g_logger) << rst; 17 | auto ret = flexy::base64decode(rst); 18 | FLEXY_ASSERT2(ret == src, ret << " " << src 19 | << " , rst.size() == " << ret.size() 20 | << ", src.size()" << src.size()); 21 | } 22 | } 23 | 24 | std::string test_sha1(const std::string& key) { 25 | std::string v = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 26 | return flexy::base64encode(flexy::sha1sum(v)); 27 | } 28 | 29 | int main(int argc, char** argv) { 30 | test_base64(); 31 | if (argc > 1) { 32 | FLEXY_LOG_DEBUG(g_logger) << test_sha1(argv[1]); 33 | } 34 | } -------------------------------------------------------------------------------- /tests/test_hook.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | static auto&& g_logger = FLEXY_LOG_ROOT(); 9 | 10 | void test_sleep() { 11 | flexy::IOManager iom(1); 12 | iom.async([](){ 13 | sleep(2); 14 | FLEXY_LOG_INFO(g_logger) << "sleep 2"; 15 | }); 16 | iom.async([](){ 17 | sleep(3); 18 | FLEXY_LOG_INFO(g_logger) << "sleep 3"; 19 | }); 20 | FLEXY_LOG_INFO(g_logger) << "test_sleep"; 21 | } 22 | 23 | void test_sock() { 24 | int sock = socket(AF_INET, SOCK_STREAM, 0); 25 | sockaddr_in addr; 26 | bzero(&addr, sizeof(addr)); 27 | addr.sin_family = AF_INET; 28 | addr.sin_port = htons(80); 29 | inet_pton(AF_INET, "36.152.44.96", &addr.sin_addr); 30 | 31 | FLEXY_LOG_INFO(g_logger) << "begin connect"; 32 | int rt = connect(sock, (const sockaddr*)&addr, sizeof(addr)); 33 | FLEXY_LOG_INFO(g_logger) << "socket = " << sock << ", connect rt = " << rt << " errno = " << errno; 34 | if (rt) { 35 | return; 36 | } 37 | const char data[] = "GET / HTTP/1.0\r\n\r\n"; 38 | rt = send(sock, data, sizeof(data), 0); 39 | FLEXY_LOG_INFO(g_logger) << "send rt = " << rt << " errno = " << errno; 40 | 41 | if (rt <= 0) { 42 | return; 43 | } 44 | std::string buff; 45 | buff.resize(4096); 46 | rt = recv(sock, &buff[0], buff.size(), 0); 47 | FLEXY_LOG_INFO(g_logger) << "recv rt = " << rt << " errno = " << errno; 48 | if (rt <= 0) { 49 | return; 50 | } 51 | buff.resize(rt); 52 | FLEXY_LOG_INFO(g_logger) << buff; 53 | } 54 | 55 | int main() { 56 | // test_sleep(); 57 | flexy::IOManager iom; 58 | iom.async(test_sock); 59 | } -------------------------------------------------------------------------------- /tests/test_http.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static auto&& g_logger = FLEXY_LOG_ROOT(); 6 | 7 | void test_request() { 8 | auto req = std::make_shared(); 9 | req->setHeader("host", "www.baidu.com"); 10 | req->setBody("hello flexy"); 11 | 12 | req->dump(std::cout) << std::endl; 13 | // FLEXY_LOG_INFO(g_logger) << *req; 14 | } 15 | 16 | void test_response() { 17 | auto rsp = std::make_shared(); 18 | rsp->setHeader("X-X", "flexy"); 19 | rsp->setBody("hello flexy"); 20 | rsp->setClose(false); 21 | rsp->setStatus((flexy::http::HttpStatus)400); 22 | 23 | rsp->dump(std::cout) << std::endl; 24 | // FLEXY_LOG_INFO(g_logger) << *rsp; 25 | } 26 | 27 | int main() { 28 | test_request(); 29 | std::cout << std::endl; 30 | test_response(); 31 | } 32 | 33 | -------------------------------------------------------------------------------- /tests/test_http_parser.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static auto&& g_logger = FLEXY_LOG_ROOT(); 7 | 8 | const char test_request_data[] = "POST / HTTP/1.1\r\n" 9 | "Host: www.baidu.com\r\n" 10 | "Content-Length: 10\r\n\r\n" 11 | "1234567890"; 12 | 13 | void test_request() { 14 | flexy::http::HttpRequestParser parser; 15 | std::string tmp = test_request_data; 16 | char* begin = tmp.data(); 17 | size_t s = parser.execute(begin, tmp.size()); 18 | FLEXY_LOG_INFO(g_logger) << "execute rt = " << s << " has_error = " 19 | << parser.hasError() << " is_finnished = " << parser.isFinished() 20 | << " content_length = " << parser.getContentLength() << ", " << tmp.size() - s; 21 | 22 | tmp.resize(tmp.size() - s); 23 | FLEXY_LOG_INFO(g_logger) << *parser.getData(); 24 | FLEXY_LOG_INFO(g_logger) << begin; 25 | } 26 | 27 | void test_response() { 28 | flexy::http::HttpResponseParser parser; 29 | auto addr = flexy::Address::LookupAnyIPAddress("www.baidu.com:80", AF_UNSPEC); 30 | auto sock = flexy::Socket::CreateTCP(addr->getFamily()); 31 | sock->connect(addr); 32 | const char buff[] = "GET / HTTP/1.1\r\n\r\n"; 33 | sock->send(buff); 34 | std::string buf; 35 | buf.resize(1000); 36 | int offset = 0; 37 | char* data = buf.data(); 38 | while (true) { 39 | int len = sock->recv(data + offset, 1000 - offset); 40 | // FLEXY_LOG_INFO(g_logger) << data; 41 | if (len <= 0) { 42 | sock->close(); 43 | FLEXY_LOG_ERROR(g_logger) << "close"; 44 | return; 45 | } 46 | len += offset; 47 | size_t nparse = parser.execute(data, len, false); 48 | FLEXY_LOG_INFO(g_logger) << "execute rt = " << nparse; 49 | if (parser.hasError()) { 50 | FLEXY_LOG_ERROR(g_logger) << "has error"; 51 | sock->close(); 52 | return; 53 | } 54 | offset = len - nparse; 55 | if (parser.isFinished()) { 56 | FLEXY_LOG_INFO(g_logger) << "finish"; 57 | break; 58 | } 59 | } 60 | FLEXY_LOG_INFO(g_logger) << *parser.getData(); 61 | } 62 | 63 | int main() { 64 | test_request(); 65 | test_response(); 66 | } -------------------------------------------------------------------------------- /tests/test_http_session.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static auto&& g_logger = FLEXY_LOG_ROOT(); 8 | 9 | void test() { 10 | auto socket = std::make_shared(AF_INET, SOCK_STREAM); 11 | auto addr = flexy::Address::LookupAnyIPAddress("0.0.0.0:8020"); 12 | FLEXY_ASSERT(socket->bind(addr)); 13 | FLEXY_ASSERT(socket->listen()); 14 | auto client = socket->accept(); 15 | FLEXY_ASSERT(client); 16 | 17 | flexy::http::HttpSession hs(client); 18 | auto&& request = hs.recvRequest(); 19 | if (request) { 20 | FLEXY_LOG_INFO(g_logger) << request->toString(); 21 | } 22 | } 23 | 24 | int main() { 25 | // auto socket = std::make_shared(AF_INET, SOCK_STREAM); 26 | // auto addr = flexy::Address::LookupAnyIPAddress("0.0.0.0:8020"); 27 | // FLEXY_ASSERT(socket->bind(addr)); 28 | // FLEXY_ASSERT(socket->listen()); 29 | // auto client = socket->accept(); 30 | // FLEXY_ASSERT(client); 31 | 32 | // flexy::http::HttpSession hs(client); 33 | // auto&& request = hs.recvRequest(); 34 | // if (request) { 35 | // FLEXY_LOG_INFO(g_logger) << *request; 36 | // } 37 | flexy::IOManager iom; 38 | go test; 39 | } -------------------------------------------------------------------------------- /tests/test_iomanager.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | static auto&& g_logger = FLEXY_LOG_ROOT(); 15 | 16 | int sock = 0; 17 | 18 | 19 | void test_fiber() { 20 | sock = socket_f(AF_INET, SOCK_STREAM, 0); 21 | FLEXY_ASSERT(sock >= 0); 22 | FLEXY_LOG_INFO(g_logger) << "test_fiber sock = " << sock; 23 | fcntl_f(sock, F_SETFL, O_NONBLOCK); 24 | sockaddr_in addr; 25 | bzero(&addr, sizeof(addr)); 26 | addr.sin_family = AF_INET; 27 | addr.sin_port = htons(80); 28 | inet_pton(AF_INET, "36.152.44.95", &addr.sin_addr); // 百度ip会更换 29 | if (!connect_f(sock, (sockaddr*)&addr, sizeof(addr))) { 30 | 31 | } else if (errno == EINPROGRESS) { 32 | FLEXY_LOG_INFO(g_logger) << "add event error = " << errno << " " << strerror(errno); 33 | flexy::IOManager::GetThis()->onRead(sock, [](){ 34 | FLEXY_LOG_INFO(g_logger) << "read call back"; 35 | }); 36 | flexy::IOManager::GetThis()->onWrite(sock, [](){ 37 | FLEXY_LOG_INFO(g_logger) << "write call back"; 38 | bool res = flexy::IOManager::GetThis()->cancelRead(sock); 39 | if (!res) { 40 | FLEXY_LOG_INFO(g_logger) << "cacel event failed"; 41 | } 42 | close(sock); 43 | }); 44 | FLEXY_LOG_INFO(g_logger) << "else " << errno << " " << strerror(errno); 45 | } 46 | } 47 | 48 | void test1() { 49 | flexy::IOManager iom; 50 | //FLEXY::IOManager iom(2, false); 51 | iom.async(test_fiber); 52 | } 53 | 54 | 55 | int main() { 56 | test1(); 57 | return 0; 58 | } -------------------------------------------------------------------------------- /tests/test_logconfig.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static auto&& g_logger = FLEXY_LOG_ROOT(); 4 | static auto&& s_logger = FLEXY_LOG_NAME("system"); 5 | 6 | int main() { 7 | FLEXY_LOG_INFO(g_logger) << "begin"; 8 | FLEXY_LOG_INFO(g_logger) << "system log"; 9 | YAML::Node node = YAML::LoadFile("../bin/conf/log.yml"); 10 | flexy::Config::LoadFromYaml(node); 11 | FLEXY_LOG_TRACE(g_logger) << "root log"; 12 | FLEXY_LOG_DEBUG(s_logger) << "system log"; 13 | FLEXY_LOG_INFO(s_logger) << "stdout file server"; 14 | FLEXY_LOG_DEBUG(g_logger) << "root debug"; 15 | sleep(15); 16 | YAML::Node new_node = YAML::LoadFile("../bin/conf/log.yml"); 17 | flexy::Config::LoadFromYaml(new_node); 18 | FLEXY_LOG_TRACE(g_logger) << "root trace"; 19 | FLEXY_LOG_INFO(s_logger) << "root level = " << g_logger->getLevel(); 20 | 21 | std::ifstream is("../bin/conf/log.json"); 22 | Json::Value v; 23 | Json::Reader r; 24 | r.parse(is, v); 25 | flexy::Config::LoadFromJson(v); 26 | 27 | FLEXY_LOG_INFO(g_logger) << "root logger back"; 28 | FLEXY_LOG_INFO(s_logger) << "system server log"; 29 | } -------------------------------------------------------------------------------- /tests/test_logger.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static auto&& g_logger = FLEXY_LOG_ROOT(); 4 | 5 | int main() { 6 | FLEXY_LOG_INFO(g_logger) << "Hello flexy log!"; 7 | FLEXY_LOG_FMT_INFO(g_logger, "hello {}", "format"); 8 | FLEXY_LOG_TRACE(g_logger) << "trace"; 9 | flexy::LogAppender::ptr file(new flexy::FileLogAppender("../bin/conf/log.txt")); 10 | g_logger->addAppender(file); 11 | FLEXY_LOG_INFO(g_logger) << "log.txt"; 12 | auto s_logger = FLEXY_LOG_NAME("test"); 13 | FLEXY_LOG_DEBUG(s_logger) << "test s_lgger"; 14 | s_logger->setLevel(flexy::LogLevel::ERROR); 15 | flexy::LogAppender::ptr StdOut(new flexy::StdoutLogAppender); 16 | s_logger->addAppender(StdOut); 17 | FLEXY_LOG_FMT_WARN(s_logger, "add appender"); 18 | 19 | // auto server = 20 | // std::make_shared("121.41.169.58:2099"); 21 | // g_logger->addAppender(server); 22 | // FLEXY_LOG_FMT_INFO(g_logger, "hello flexy server log"); 23 | } -------------------------------------------------------------------------------- /tests/test_mysql.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static auto&& g_logger = FLEXY_LOG_ROOT(); 6 | 7 | static auto s_mysql_confif = flexy::Config::Lookup( 8 | "mysql", std::map(), "mysql config"); 9 | 10 | void printData(const flexy::ISQLData::ptr& res) { 11 | if (!res) { 12 | FLEXY_LOG_ERROR(g_logger) << "query error, is null"; 13 | return; 14 | } 15 | std::cout << "---------------------------------------------\n"; 16 | int col = res->getColumnCount(); 17 | for (int j = 0; j < col; ++j) { 18 | std::cout << res->getColumnName(j) << "\t"; 19 | } 20 | std::cout << std::endl; 21 | while (res->next()) { 22 | // for (int j = 0; j < col; ++j) { 23 | // std::cout << res->getString(j) << "\t"; 24 | // } 25 | // std::cout << std::endl; 26 | std::cout << res->getInt32(0) << "\t" << res->getTimeStr(1) 27 | << std::endl; 28 | } 29 | std::cout << "---------------------------------------------\n"; 30 | } 31 | 32 | void run() { 33 | do { 34 | flexy::Config::LoadFromConDir("conf/"); 35 | 36 | auto mysql = std::make_shared(s_mysql_confif->getValue()); 37 | if (!mysql->connect()) { 38 | std::cout << "connect fail" << std::endl; 39 | return; 40 | } 41 | 42 | int rt = mysql->execute("update user set update_time = %s where id = 1", 43 | "'2012-01-31 14:34:17'"); 44 | FLEXY_ASSERT(rt == 0); 45 | rt = mysql->execute("insert into user (update_time) values ('%s')", 46 | flexy::TimeToStr().c_str()); 47 | FLEXY_ASSERT(rt == 0); 48 | // auto stmt = flexy::MySQLStmt::Create(mysql, "update user set 49 | // update_time = ? where id = 1"); stmt->bindString(1, "2021-12-29 50 | // 10:10:10"); int rt = stmt->execute(); std::cout << "rt = " << rt << 51 | // std::endl; 52 | 53 | auto&& res = mysql->query("select * from user"); 54 | printData(res); 55 | 56 | } while (false); 57 | std::cout << "over" << std::endl; 58 | } 59 | 60 | void test_mysql_mgr() { 61 | auto& mgr = flexy::MySQLMgr::GetInstance(); 62 | flexy::Config::LoadFromConDir("conf/"); 63 | FLEXY_ASSERT(mgr.execStmt("test_0", 64 | "update user set update_time = ? where id = 2", 65 | flexy::TimeToStr()) == 0); 66 | auto res = mgr.queryStmt("test_0", "select * from user"); 67 | printData(res); 68 | } 69 | 70 | int main(int argc, char** argv) { 71 | flexy::EnvMgr::GetInstance().init(argc, argv); 72 | flexy::IOManager iom; 73 | // iom.addRecTimer(1000, run); 74 | go test_mysql_mgr; 75 | // go run; 76 | 77 | return 0; 78 | } -------------------------------------------------------------------------------- /tests/test_scheduler.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static auto&& g_logger = FLEXY_LOG_ROOT(); 6 | 7 | void println(const char* s, int argc, char** argv) { 8 | for (int i = 1; i < argc; ++i) { 9 | FLEXY_LOG_INFO(g_logger) << argv[i]; 10 | } 11 | FLEXY_LOG_INFO(g_logger) << s; 12 | } 13 | 14 | void test() { 15 | static int s_count = 5; 16 | FLEXY_LOG_FMT_INFO(g_logger, "test s_count = {}", s_count); 17 | if (--s_count >= 0) { 18 | flexy::Scheduler::GetThis()->async(test); 19 | } 20 | } 21 | 22 | int main(int argc, char** argv) { 23 | flexy::Scheduler sc(1, true, "TEST"); 24 | sc.async(println, "hello scheduler", argc, argv); 25 | sc.start(); 26 | // sleep(1); 27 | sc.async_first(nullptr); 28 | sc.async_first(test); 29 | 30 | sc.async([](auto&& first, auto&&... other){ 31 | FLEXY_LOG_INFO(g_logger) << "sum = " << (first + ... + other); 32 | }, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 33 | 34 | sc.stop(); 35 | } -------------------------------------------------------------------------------- /tests/test_signal.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static auto&& g_logger = FLEXY_LOG_ROOT(); 7 | 8 | void test() { 9 | FLEXY_LOG_INFO(g_logger) << "test"; 10 | sleep_f(10); 11 | } 12 | 13 | int main(int argc, char** argv) { 14 | flexy::IOManager iom(1); 15 | 16 | flexy::Signal::signal(SIGINT, [](int argc, char** argv){ 17 | FLEXY_LOG_INFO(g_logger) << "SIGINT"; 18 | for (int i = 1; i < argc; ++i) { 19 | FLEXY_LOG_DEBUG(g_logger) << argv[i]; 20 | } 21 | }, argc, argv); 22 | iom.async(test); 23 | } -------------------------------------------------------------------------------- /tests/test_socket.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static auto&& g_logger = FLEXY_LOG_ROOT(); 6 | 7 | void test_socket() { 8 | auto addr = flexy::Address::LookupAnyIPAddress("www.baidu.com", AF_UNSPEC); 9 | //auto addr = flexy::Address::LookupAnyIpAddress("ipv6.test-ipv6.com", AF_UNSPEC); 10 | if (addr) { 11 | FLEXY_LOG_INFO(g_logger) << "get address: " << addr->toString(); 12 | } else { 13 | FLEXY_LOG_ERROR(g_logger) << "get address fail"; 14 | return; 15 | } 16 | auto sock = flexy::Socket::CreateTCP(addr->getFamily()); 17 | addr->setPort(80); 18 | if (!sock->connect(addr)) { 19 | FLEXY_LOG_ERROR(g_logger) << "connect " << addr->toString() << " fail"; 20 | return; 21 | } else { 22 | FLEXY_LOG_INFO(g_logger) << "connect " << addr->toString() << " connected"; 23 | // FLEXY_LOG_DEBUG(g_logger) << *sock; 24 | } 25 | const char buff[] = "GET / HTTP/1.0\r\n\r\n"; 26 | int rt = sock->send(buff); 27 | if (rt <= 0) { 28 | FLEXY_LOG_ERROR(g_logger) << "send fail, rt = " << rt; 29 | return; 30 | } 31 | std::string buf; 32 | buf.resize(4096); 33 | rt = sock->recv(buf); 34 | if (rt <= 0) { 35 | FLEXY_LOG_ERROR(g_logger) << "recv fail, rt = " << rt; 36 | return; 37 | } 38 | buf.resize(rt); 39 | FLEXY_LOG_INFO(g_logger) << buf; 40 | } 41 | 42 | int main() { 43 | flexy::IOManager iom; 44 | iom.async(test_socket); 45 | 46 | return 0; 47 | } -------------------------------------------------------------------------------- /tests/test_sqlite3.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static auto&& g_logger = FLEXY_LOG_ROOT(); 9 | 10 | void printData(flexy::ISQLData::ptr& res) { 11 | if (!res) { 12 | FLEXY_LOG_ERROR(g_logger) << "query error, is null"; 13 | return; 14 | } 15 | std::cout << "---------------------------------------------\n"; 16 | int col = res->getColumnCount(); 17 | for (int j = 0; j < col; ++j) { 18 | std::cout << res->getColumnName(j) << "\t"; 19 | } 20 | std::cout << std::endl; 21 | while (res->next()) { 22 | for (int j = 0; j < col; ++j) { 23 | std::cout << res->getString(j) << "\t"; 24 | } 25 | std::cout << std::endl; 26 | } 27 | std::cout << "---------------------------------------------\n"; 28 | } 29 | 30 | void test_sqlite3() { 31 | std::string dbname = "test.db"; 32 | dbname = flexy::FS::AbsolutePath(dbname); 33 | auto db = flexy::SQLite3::Create(dbname, flexy::SQLite3::READWRITE); 34 | 35 | if (!db) { 36 | FLEXY_LOG_INFO(g_logger) << "dbname = " << dbname << " not exits"; 37 | db = flexy::SQLite3::Create( 38 | dbname, flexy::SQLite3::READWRITE | flexy::SQLite3::CREATE); 39 | if (!db) { 40 | FLEXY_LOG_ERROR(g_logger) 41 | << "dbname = " << dbname << " create error"; 42 | return; 43 | } 44 | #define XX(...) #__VA_ARGS__ 45 | int rt = db->execute(XX(create table user( 46 | id integer primary key autoincrement, 47 | name varchar(50) not null default "", age int not null default 0, 48 | create_time datetime))); 49 | #undef XX 50 | 51 | if (rt != SQLITE_OK) { 52 | FLEXY_LOG_ERROR(g_logger) << "create table error " << db->getErrno() 53 | << " - " << db->getErrStr(); 54 | return; 55 | } 56 | } 57 | db->execStmt("insert into user (name, age) values (?, ?)", "stmt_1", 1); 58 | auto stmt = flexy::SQLite3Stmt::Create( 59 | db, "insert into user(name, age, create_time) values(?, ?, ?)"); 60 | if (!stmt) { 61 | FLEXY_LOG_ERROR(g_logger) << "create statement error " << db->getErrno() 62 | << " - " << db->getErrStr(); 63 | } 64 | int64_t now = time(0); 65 | for (int i = 2; i < 10; ++i) { 66 | stmt->bind(1, "stmt_" + std::to_string(i)); 67 | stmt->bind(2, i); 68 | stmt->bind(3, now + rand() % 100); 69 | if (stmt->execute() != SQLITE_OK) { 70 | FLEXY_LOG_ERROR(g_logger) 71 | << "execute statement error " << i << " " << db->getErrno() 72 | << " - " << db->getErrStr(); 73 | } 74 | stmt->reset(); 75 | } 76 | 77 | auto res = db->query("select * from user"); 78 | 79 | printData(res); 80 | } 81 | 82 | void test_sqlite3_mgr() { 83 | auto& sql_mgr = flexy::SQLite3Mgr::GetInstance(); 84 | flexy::Config::LoadFromConDir("conf/"); 85 | sql_mgr.execStmt("test_0", "insert into user (name, age) values (?, ?)", 86 | "Jhon", 19); 87 | auto res = sql_mgr.query("test_0", "select * from user"); 88 | printData(res); 89 | sql_mgr.execute("test_0", 90 | "update uer set name = 'Jsoa', age = 15 where id = 2"); 91 | sleep(1); 92 | sql_mgr.checkConnection(1); 93 | sql_mgr.execute("test_0", "update user set name = 'Jully' where id = 1"); 94 | res = sql_mgr.query("test_0", "select * from user"); 95 | printData(res); 96 | } 97 | 98 | int main(int argc, char** argv) { 99 | // test_sqlite3(); 100 | flexy::EnvMgr::GetInstance().init(argc, argv); 101 | test_sqlite3_mgr(); 102 | } 103 | -------------------------------------------------------------------------------- /tests/test_task.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "flexy/util/log.h" 3 | #include "flexy/util/task.h" 4 | 5 | static auto&& g_logger = FLEXY_LOG_ROOT(); 6 | 7 | using namespace flexy::detail; 8 | 9 | // using task = __task_virtual; 10 | // using task = __task_function; 11 | // using task = __task_template; 12 | 13 | using task = __task_Function; 14 | 15 | TEST(__Task, Lambda) { 16 | int ans = 0; 17 | task t1([](int a, int b, int& ans) { ans = a + b; }, 1, 2, std::ref(ans)); 18 | t1(); 19 | ASSERT_EQ(ans, 3); 20 | 21 | task t2([](const char* s) { ASSERT_EQ(s, "Hello World"); }, "Hello World"); 22 | t2(); 23 | t2(); 24 | 25 | task t3([&ans](int a, int b) { ans = a * b; }, 4, 5); 26 | t3(); 27 | ASSERT_EQ(ans, 20); 28 | 29 | task t5([ans]() mutable { ans /= 4; }); 30 | ASSERT_EQ(ans, 20); 31 | 32 | task t6([](auto&& func) { func(); }, std::move(t2)); 33 | 34 | task t7( 35 | [](auto&& first, auto&&... other) { 36 | auto res = (first + ... + other); 37 | ASSERT_EQ(res, 55); 38 | }, 39 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 40 | t7(); 41 | } 42 | 43 | TEST(__Task, MemberMethod) { 44 | // noncopyable plus 45 | struct plus { 46 | int ans = 0; 47 | 48 | void operator()(int a, int b) { ans = a + b; } 49 | plus(const plus&) = delete; 50 | plus() = default; 51 | plus(plus&&) = default; 52 | }; 53 | 54 | plus p; 55 | task t1(std::ref(p), 4, 5); 56 | t1(); 57 | ASSERT_EQ(p.ans, 9); 58 | 59 | task t2(&plus::operator(), &p, 1, 2); 60 | t2(); 61 | ASSERT_EQ(p.ans, 3); 62 | 63 | task t3(std::move(p), 0, 0); 64 | t3(); 65 | 66 | struct Worker { 67 | void operator()() const {} 68 | 69 | void Work(std::string_view hello) { ASSERT_EQ(hello, "hello"); } 70 | Worker() = default; 71 | Worker(const Worker&) = default; 72 | }; 73 | 74 | const Worker w; 75 | task t4(w); 76 | task t5(&Worker::operator(), &w); 77 | 78 | Worker ww; 79 | task t6(w); 80 | task t7(&Worker::Work, &ww, "hello"); 81 | } 82 | 83 | void test_unique(std::unique_ptr&& uptr) { ASSERT_EQ(*uptr, 0x99); } 84 | 85 | void Invoke(task&& cb) { cb(); } 86 | 87 | template 88 | void Forward(Args&&... args) { 89 | return Invoke(task(std::forward(args)...)); 90 | } 91 | 92 | template 93 | void max(T a, T b, T& ans) { 94 | ans = a < b ? a : b; 95 | } 96 | 97 | TEST(__Task, Function) { 98 | struct NonCopyable { 99 | void operator()() {} 100 | NonCopyable() = default; 101 | NonCopyable(NonCopyable&&) = default; 102 | }; 103 | 104 | NonCopyable nc; 105 | task t1 = std::move(nc); 106 | t1(); 107 | 108 | task t2(&test_unique, std::make_unique(0x99)); 109 | t2(); 110 | 111 | Forward(test_unique, std::make_unique(0x99)); 112 | 113 | int ans = 0; 114 | task t3(&max, 0x99, -2, std::ref(ans)); 115 | t3(); 116 | ASSERT_EQ(ans, -2); 117 | } 118 | 119 | int main(int argc, char** argv) { 120 | testing::InitGoogleTest(&argc, argv); 121 | return RUN_ALL_TESTS(); 122 | } -------------------------------------------------------------------------------- /tests/test_tcp_server.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static auto&& g_logger = FLEXY_LOG_ROOT(); 5 | 6 | void run() { 7 | auto addr = flexy::Address::LookupAny("0.0.0.0:8013"); 8 | auto tcp_server = std::make_shared(); 9 | while (!tcp_server->bind(addr)) { 10 | // sleep(2); 11 | } 12 | tcp_server->start(); 13 | } 14 | 15 | int main() { 16 | flexy::IOManager iom(2); 17 | iom.async(run); 18 | } -------------------------------------------------------------------------------- /tests/test_this_fiber.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace flexy; 4 | using namespace std::chrono_literals; 5 | 6 | static auto& g_logger = FLEXY_LOG_ROOT(); 7 | 8 | void test_this_fiber() { 9 | FLEXY_LOG_INFO(g_logger) 10 | << "hello this fiber, id = " << this_fiber::get_id(); 11 | // IOManager::GetThis()->async(Fiber::GetThis()); 12 | // this_fiber::yield(); 13 | FLEXY_LOG_DEBUG(g_logger) << "begin sleep"; 14 | 15 | this_fiber::sleep_for(2000ms); 16 | FLEXY_LOG_DEBUG(g_logger) << "end sleep"; 17 | } 18 | 19 | void work() { 20 | FLEXY_LOG_DEBUG(g_logger) << "work..."; 21 | this_fiber::sleep_for(3s); 22 | FLEXY_LOG_DEBUG(g_logger) << "work finish!"; 23 | } 24 | 25 | void test_sleep_util() { 26 | FLEXY_LOG_FMT_DEBUG(g_logger, "welcome to sleep util"); 27 | const auto start(std::chrono::steady_clock::now()); 28 | this_fiber::sleep_until(start + 2000ms); 29 | } 30 | 31 | int main() { 32 | IOManager iom; 33 | go test_this_fiber; 34 | go work; 35 | go test_sleep_util; 36 | FLEXY_LOG_INFO(g_logger) << "main fiber id = " << this_fiber::get_id(); 37 | } -------------------------------------------------------------------------------- /tests/test_thread.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static auto&& g_logger = FLEXY_LOG_ROOT(); 5 | 6 | void test_thread(int argc, char** argv) { 7 | for (int i = 1; i < argc; ++i) { 8 | // printf("%s\n", argv[i]); 9 | FLEXY_LOG_FMT_INFO(g_logger, "{}", argv[i]); 10 | } 11 | } 12 | 13 | struct Add { 14 | void operator()(int a, int b) { 15 | FLEXY_LOG_FMT_INFO(g_logger, "{}", a + b); 16 | } 17 | }; 18 | 19 | void print() { 20 | //printf("Hello World!"); 21 | FLEXY_LOG_INFO(g_logger) << "Hello World!"; 22 | } 23 | 24 | int main(int argc, char** argv) { 25 | flexy::Thread t("hello", test_thread, argc, argv); 26 | flexy::Thread t2("print", print); 27 | Add a; 28 | flexy::Thread t3("add", &Add::operator(), a, 1, 2); 29 | flexy::Thread::SetName("main thread"); 30 | t.join(); 31 | t2.join(); 32 | t3.join(); 33 | FLEXY_LOG_INFO(g_logger) << "this is a log"; 34 | } -------------------------------------------------------------------------------- /tests/test_timer.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static auto&& g_logger = FLEXY_LOG_ROOT(); 5 | 6 | void test_timer() { 7 | flexy::Timer::ptr timer; 8 | flexy::IOManager iom(2); 9 | timer = iom.addRecTimer(1000, [](flexy::Timer::ptr& t) { 10 | static int i = 0; 11 | FLEXY_LOG_INFO(g_logger) << "hello timer"; 12 | if (++i == 5) { 13 | t->cancel(); 14 | } 15 | }, std::ref(timer)); 16 | } 17 | 18 | int main() { 19 | test_timer(); 20 | 21 | flexy::IOManager iom(1); 22 | iom.addTimer(1000, [](auto&&... args) { 23 | (std::cout << ... << args) << std::endl; 24 | }, "Hello Timer", " World Hello", 1, 31.0f, 4520ull); 25 | } -------------------------------------------------------------------------------- /tests/test_worker.cc: -------------------------------------------------------------------------------- 1 | #include "flexy/schedule/worker.h" 2 | 3 | static auto&& g_logger = FLEXY_LOG_ROOT(); 4 | 5 | class Work { 6 | public: 7 | int print(int i) { 8 | FLEXY_LOG_INFO(g_logger) << "work! " << i; 9 | return i; 10 | } 11 | void operator()() { FLEXY_LOG_INFO(g_logger) << "functor!"; } 12 | }; 13 | 14 | flexy::fiber::Semaphore fsem; 15 | 16 | void test_sem1() { 17 | FLEXY_LOG_INFO(g_logger) << "test1 wait"; 18 | fsem.wait(); 19 | FLEXY_LOG_INFO(g_logger) << "test1 finish wait"; 20 | } 21 | 22 | void test_sem2() { 23 | sleep(1); 24 | FLEXY_LOG_INFO(g_logger) << "test2 post"; 25 | fsem.post(); 26 | FLEXY_LOG_INFO(g_logger) << "test2 finish post"; 27 | } 28 | 29 | void test_work_group() { 30 | auto wg = flexy::WorkerGroup::Create(2); 31 | Work w; 32 | wg->schedule([]() { FLEXY_LOG_INFO(g_logger) << "Hello worker!"; }); 33 | #if __cplusplus > 201703L 34 | // wg->schedule([] (fmt::format_string fmt, 35 | // Args&&... args) { FLEXY_LOG_FMT_INFO(g_logger, fmt, 36 | // std::forward(args)...); 37 | // }, "{} {} {}", 1, 2, 3); 38 | #else 39 | wg->schedule( 40 | [](const char* fmt, auto&&... args) { 41 | FLEXY_LOG_FMT_INFO(g_logger, fmt, 42 | std::forward(args)...); 43 | }, 44 | "{} {} {}", 1, 2, 3); 45 | #endif 46 | 47 | wg->schedule([]() { 48 | 49 | }); 50 | wg->schedule(&Work::print, &w, 1); 51 | wg->schedule(w); 52 | wg->schedule(test_sem1); 53 | wg->schedule(test_sem2); 54 | } 55 | 56 | int main() { 57 | flexy::IOManager iom(1); 58 | 59 | go test_work_group; 60 | // go test_sem1; 61 | // go test_sem2; 62 | } -------------------------------------------------------------------------------- /tests/test_ws_server.cc: -------------------------------------------------------------------------------- 1 | #include "flexy/http/ws_server.h" 2 | #include "flexy/util/log.h" 3 | 4 | static auto& g_logger = FLEXY_LOG_ROOT(); 5 | 6 | void run() { 7 | auto server = std::make_shared(); 8 | auto addr = flexy::Address::LookupAnyIPAddress("0.0.0.0:8020"); 9 | if (!addr) { 10 | FLEXY_LOG_ERROR(g_logger) << "get address error"; 11 | return; 12 | } 13 | 14 | auto fun = [](const flexy::http::HttpRequest::ptr& header, 15 | const flexy::http::WSFrameMessage::ptr& msg, 16 | const flexy::http::WSSession::ptr& session) { 17 | session->sendMessage(msg); 18 | return 0; 19 | }; 20 | 21 | server->getWSServletDispatch()->addWSServlet("/flexy", fun); 22 | while (!server->bind(addr)) { 23 | FLEXY_LOG_ERROR(g_logger) << "bind " << *addr << " fail"; 24 | sleep(1); 25 | } 26 | server->start(); 27 | } 28 | 29 | int main() { 30 | flexy::IOManager iom(2); 31 | go run; 32 | } 33 | --------------------------------------------------------------------------------