├── .clang-format ├── .clang-tidy ├── .gitignore ├── README.md ├── docs ├── bench.md ├── debug.md ├── http.md └── interface.md ├── example ├── echo_server.cpp └── xmake.lua ├── luce ├── co │ ├── co_thread_pool.cpp │ ├── co_thread_pool.h │ ├── scheduler.cpp │ ├── scheduler.h │ └── task.h ├── codec │ ├── abstract_coder.h │ ├── abstract_protocol.h │ ├── serializer.h │ ├── tinypb_coder.cpp │ ├── tinypb_coder.h │ ├── tinypb_protocol.cpp │ ├── tinypb_protocol.h │ └── type_helper.h ├── common │ ├── Timestamp.cpp │ ├── Timestamp.h │ ├── blocking_queue.h │ ├── blocking_queue_thread_pool.h │ ├── count_down_latch.h │ ├── file_util.h │ ├── json.h │ ├── json │ │ ├── JObject.cpp │ │ ├── JObject.h │ │ ├── Parser.cpp │ │ └── Parser.h │ ├── logger.h │ ├── noncopyable.h │ ├── singleton.h │ ├── string_util.cpp │ ├── string_util.h │ └── thread_pool.h ├── io │ ├── io_awaiter.cpp │ ├── io_awaiter.h │ ├── io_buffer.cpp │ └── io_buffer.h ├── net │ ├── event_manager.cpp │ ├── event_manager.h │ ├── http │ │ ├── http_context.h │ │ ├── http_request.cpp │ │ ├── http_request.h │ │ ├── http_response.cpp │ │ ├── http_response.h │ │ ├── http_router.h │ │ ├── http_server.cpp │ │ └── http_server.h │ ├── http_all.h │ ├── inet_address.h │ ├── rpc │ │ ├── invoke_helper.h │ │ ├── rpc_client.h │ │ ├── rpc_err_code.h │ │ ├── rpc_server.h │ │ └── rpc_value.h │ ├── rpc_all.h │ ├── socket.cpp │ ├── socket.h │ ├── tcp │ │ ├── tcp_acceptor.cpp │ │ ├── tcp_acceptor.h │ │ ├── tcp_application.cpp │ │ ├── tcp_application.h │ │ ├── tcp_connection.cpp │ │ ├── tcp_connection.h │ │ ├── tcp_server.cpp │ │ └── tcp_server.h │ ├── tcp_all.h │ └── util.h ├── timer │ ├── timer.cpp │ └── timer.h └── xmake.lua ├── tests ├── codec │ └── test_serializer.cpp ├── common │ ├── test_logger.cpp │ └── test_threadpool.cpp ├── coroutine │ └── test_task.cpp ├── net │ ├── echo_server2.cpp │ ├── rpc │ │ ├── test_rpc_client.cpp │ │ └── test_rpc_server.cpp │ └── test_http.cpp └── xmake.lua └── xmake.lua /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | DerivePointerAlignment: false 3 | PointerAlignment: Left 4 | ColumnLimit: 80 5 | Standard: c++20 6 | 7 | # Default for clang-8, changed in later clangs. Set explicitly for forwards 8 | # compatibility for students with modern clangs 9 | IncludeBlocks: Preserve 10 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | # Modified from the Apache Arrow project for the Terrier project. 20 | # 21 | --- 22 | Checks: ' 23 | bugprone-*, 24 | clang-analyzer-*, 25 | google-*, 26 | modernize-*, 27 | performance-*, 28 | portability-*, 29 | readability-*, 30 | -bugprone-too-small-loop-variable, 31 | -clang-analyzer-cplusplus.NewDelete, 32 | -clang-analyzer-cplusplus.NewDeleteLeaks, 33 | -modernize-use-nodiscard, 34 | -modernize-avoid-c-arrays, 35 | -readability-magic-numbers, 36 | -bugprone-branch-clone, 37 | -bugprone-signed-char-misuse, 38 | -bugprone-unhandled-self-assignment, 39 | -bugprone-easily-swappable-parameters, 40 | -clang-diagnostic-implicit-int-float-conversion, 41 | -modernize-use-auto, 42 | -modernize-use-trailing-return-type, 43 | -modernize-pass-by-value, 44 | -performance-move-constArg, 45 | -readability-convert-member-functions-to-static, 46 | -readability-make-member-function-const, 47 | -readability-qualified-auto, 48 | -readability-identifier-length, 49 | -readability-redundant-access-specifiers, 50 | -readability-function-cognitive-complexity, 51 | -google-readability-casting, 52 | -bugprone-narrowing-conversions, 53 | ' 54 | # TODO(WAN): To be enabled in Fall 2020. 55 | #CheckOptions: 56 | # - { key: readability-identifier-naming.ClassCase, value: CamelCase } 57 | # - { key: readability-identifier-naming.EnumCase, value: CamelCase } 58 | # - { key: readability-identifier-naming.FunctionCase, value: CamelCase } 59 | # - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } 60 | # - { key: readability-identifier-naming.MemberCase, value: lower_case } 61 | # - { key: readability-identifier-naming.MemberSuffix, value: _ } 62 | # - { key: readability-identifier-naming.NamespaceCase, value: lower_case } 63 | # - { key: readability-identifier-naming.StructCase, value: CamelCase } 64 | # - { key: readability-identifier-naming.UnionCase, value: CamelCase } 65 | # - { key: readability-identifier-naming.VariableCase, value: lower_case } 66 | WarningsAsErrors: '*' 67 | HeaderFilterRegex: '/(src|test)/include' 68 | AnalyzeTemporaryDtors: true 69 | 70 | #### Disabled checks and why: ##### 71 | # 72 | # -bugprone-too-small-loop-variable, 73 | # Complains about uint8_t or uint16_t when the limit on the loop is a container's .size() (size_t). 74 | # We usually do this when we know the maximum size of the container though, so propose leaving disabled. 75 | # -clang-analyzer-cplusplus.NewDelete, 76 | # Seems to generate false positives. Suggest relying on ASAN and valgrind for memory stuff. 77 | # -clang-analyzer-cplusplus.NewDeleteLeaks, 78 | # Seems to generate false positives. Suggest relying on ASAN and valgrind for memory stuff. 79 | # -modernize-use-nodiscard, 80 | # New C++17 feature, slightly polarizing. Would clutter codebase. 81 | # -modernize-avoid-c-arrays, 82 | # We use C-style arrays in page.h, type.h and logger.h. They're a little more ergonomic than std::array. Thoughts? 83 | # -readability-magic-numbers, 84 | # Let's not deal with people doing ridiculous things to hack around this. If it bites them, it bites them. 85 | # -bugprone-branch-clone, -bugprone-signed-char-misuse, -bugprone-unhandled-self-assignment, 86 | # -clang-diagnostic-implicit-int-float-conversion, -modernize-use-auto, -modernize-use-trailing-return-type, 87 | # -readability-convert-member-functions-to-static, -readability-make-member-function-const, -readability-qualified-auto, 88 | # -readability-redundant-access-specifiers 89 | # Not available on clang-8. Disable for forward compatibility with students running modern clang versions. 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # CMake 35 | build/ 36 | CMakeLists.txt.user 37 | CMakeCache.txt 38 | CMakeFiles 39 | CMakeScripts 40 | Testing 41 | Makefile 42 | cmake_install.cmake 43 | install_manifest.txt 44 | compile_commands.json 45 | CTestTestfile.cmake 46 | _deps 47 | cmake-build-* 48 | 49 | # Visual Studio Code 50 | .vscode/* 51 | !.vscode/settings.json 52 | !.vscode/tasks.json 53 | !.vscode/launch.json 54 | !.vscode/extensions.json 55 | !.vscode/*.code-snippets 56 | 57 | # CLion 58 | .idea/ 59 | 60 | # Local History for Visual Studio Code 61 | .history/ 62 | 63 | # clangd 64 | .cache/ 65 | 66 | # Built Visual Studio Code Extensions 67 | *.vsix 68 | 69 | # ============ 70 | # custom rules 71 | draft/ 72 | 73 | perf.data* 74 | *.data 75 | 76 | # log for perf 77 | tmp 78 | *.rdb 79 | *.svg 80 | 81 | # public/index.html for httpd 82 | public/ 83 | 84 | # xmake 85 | .xmake/ 86 | compile_commands.json 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # luce 2 | ## 简介 3 | 基于`C++20 coroutine`的“高性能”epoll网络库,使用协程可以方便地用同步的代码写出异步的效果。 4 | 5 | ps: “高性能”是指单看echo_server,性能跟`muduo`接近,没有进行更多的性能测试。 6 | 同时`muduo`有很多内部的优化技巧,本项目暂未优化一些内部实现,所以有不少的提升空间。 7 | 8 | ### 期望 9 | C++20带来了无栈协程,但是不方便普通用户使用,目前的标准库适合库作者使用。 10 | 我想慢慢地封装一层协程异步框架,计划参考`rust`中的实现。 11 | 12 | ### 编译运行 13 | 本项目采用了`xmake`作为构建系统、依赖管理。 14 | ``` 15 | # 编译 16 | xmake build 17 | # 运行echo_server 18 | xmake run echo_server 19 | ``` 20 | 21 | ## 目前已实现的部分 22 | 23 | - IO多路复用(epoll)+ MultiReactor 24 | - C++20协程支持 co_await/co_return 25 | - 简单使用co::Task<>即可令函数成为协程 26 | - 线程池 27 | - EventManager/Reactor 28 | - TcpServer/TcpAcceptor/TcpConnection/TcpApplication 29 | - [高性能异步日志](https://github.com/Pang-GJ/plog) 30 | - 没有合并异步日志,仍在开发中,计划封装一下`fmt`或者`std::format`TODO 31 | - [json解析](https://github.com/Pang-GJ/tinyjson) 32 | - [类似`gin`的restful HTTP框架](./docs/http.md) 33 | 34 | ## 用法 35 | 36 | 参考`example/echo_server.cpp` 37 | 用户需要继承`TcpApplication`(位于`include/net/tcp_application.h`) 38 | 实现自己的Tcp应用程序, 39 | 具体来说要实现三个函数: 40 | 41 | ```cpp 42 | virtual co::Task<> OnRequest(TcpConnectionPtr conn, 43 | TcpServer &server) = 0; 44 | virtual co::Task<> OnOpen(TcpConnectionPtr conn) = 0; 45 | virtual co::Task<> OnClose(TcpConnectionPtr conn) = 0; 46 | ``` 47 | 48 | `OnRequest`表示请求到来时的回调(已经使用协程尽量减少回调的使用,但总有一些不好去除的回调,如果你有更好的想法,欢迎跟我讨论)。 49 | `OnOpen`表示连接刚开启的回调 50 | `OnClose`表示连接关闭时的回调 51 | 52 | ## 网络模型 53 | 54 | Reactor 55 | 56 | 用户所在的线程运行main_reactor,负责监听到来的请求连接,连接后交由sub_reactor进行读写 57 | 58 | ## RoadMap 59 | 60 | - 协程 61 | - 目前的协程使用感觉还不是很容易 62 | - 缺少了不少组件,例如调度器、同步原语 63 | - io_uring 64 | - HTTP封装 65 | - 实现类似`gin`的简易HTTP框架 66 | - 简单protobuf RPC实现 67 | - 更进一步可以使用自己实现的序列化 68 | - TODO: 不用写`IDL`的RPC 69 | - Zero Copy 70 | -------------------------------------------------------------------------------- /docs/bench.md: -------------------------------------------------------------------------------- 1 | # 性能测试 2 | 3 | ## echo_server性能测试 4 | 5 | 测试工具:`ab -n 10000000 -c 1000 -k http://127.0.0.1:{port}/` 6 | 本机配置: 7 | ``` 8 | Name: AMD Ryzen 7 4800U with Radeon Graphics 9 | Microarchitecture: Zen 2 10 | Technology: 7nm 11 | Max Frequency: 1.800 GHz 12 | Cores: 8 cores (16 threads) 13 | AVX: AVX,AVX2 14 | FMA: FMA3 15 | L1i Size: 32KB (256KB Total) 16 | L1d Size: 32KB (256KB Total) 17 | L2 Size: 512KB (4MB Total) 18 | L3 Size: 4MB (8MB Total) 19 | Peak Performance: 460.80 GFLOP/s 20 | ``` 21 | 上面数据来自于`cpufetch` 22 | 23 | ### this project (single thread): 24 | ``` 25 | Server Software: 26 | Server Hostname: 127.0.0.1 27 | Server Port: 10009 28 | 29 | Document Path: / 30 | Document Length: 405 bytes 31 | 32 | Concurrency Level: 1000 33 | Time taken for tests: 69.095 seconds 34 | Complete requests: 10000000 35 | Failed requests: 0 36 | Non-2xx responses: 10000000 37 | Keep-Alive requests: 10000000 38 | Total transferred: 5120000000 bytes 39 | HTML transferred: 4050000000 bytes 40 | Requests per second: 144728.66 [#/sec] (mean) 41 | Time per request: 6.909 [ms] (mean) 42 | Time per request: 0.007 [ms] (mean, across all concurrent requests) 43 | Transfer rate: 72364.33 [Kbytes/sec] received 44 | 45 | Connection Times (ms) 46 | min mean[+/-sd] median max 47 | Connect: 0 0 0.2 0 33 48 | Processing: 0 7 0.9 7 214 49 | Waiting: 0 7 0.9 7 214 50 | Total: 0 7 0.9 7 214 51 | 52 | Percentage of the requests served within a certain time (ms) 53 | 50% 7 54 | 66% 7 55 | 75% 7 56 | 80% 7 57 | 90% 8 58 | 95% 8 59 | 98% 9 60 | 99% 10 61 | 100% 214 (longest request) 62 | ``` 63 | 64 | ### tokio echo_server (single thread): 65 | ``` 66 | Server Software: 67 | Server Hostname: 127.0.0.1 68 | Server Port: 8888 69 | 70 | Document Path: / 71 | Document Length: 0 bytes 72 | 73 | Concurrency Level: 1000 74 | Time taken for tests: 70.251 seconds 75 | Complete requests: 10000000 76 | Failed requests: 0 77 | Non-2xx responses: 10000000 78 | Keep-Alive requests: 10000000 79 | Total transferred: 1060000000 bytes 80 | HTML transferred: 0 bytes 81 | Requests per second: 142347.54 [#/sec] (mean) 82 | Time per request: 7.025 [ms] (mean) 83 | Time per request: 0.007 [ms] (mean, across all concurrent requests) 84 | Transfer rate: 14735.20 [Kbytes/sec] received 85 | 86 | Connection Times (ms) 87 | min mean[+/-sd] median max 88 | Connect: 0 0 0.1 0 13 89 | Processing: 0 7 1.9 7 107 90 | Waiting: 0 7 1.9 7 107 91 | Total: 0 7 1.9 7 114 92 | 93 | Percentage of the requests served within a certain time (ms) 94 | 50% 7 95 | 66% 7 96 | 75% 7 97 | 80% 7 98 | 90% 8 99 | 95% 8 100 | 98% 9 101 | 99% 12 102 | 100% 114 (longest request) 103 | ``` 104 | 105 | ### muduo echo_server (single thread): 106 | ``` 107 | Server Software: 108 | Server Hostname: 127.0.0.1 109 | Server Port: 10009 110 | 111 | Document Path: / 112 | Document Length: 0 bytes 113 | 114 | Concurrency Level: 1000 115 | Time taken for tests: 69.185 seconds 116 | Complete requests: 10000000 117 | Failed requests: 0 118 | Non-2xx responses: 10000000 119 | Keep-Alive requests: 10000000 120 | Total transferred: 1070000000 bytes 121 | HTML transferred: 0 bytes 122 | Requests per second: 144540.68 [#/sec] (mean) 123 | Time per request: 6.918 [ms] (mean) 124 | Time per request: 0.007 [ms] (mean, across all concurrent requests) 125 | Transfer rate: 15103.37 [Kbytes/sec] received 126 | 127 | Connection Times (ms) 128 | min mean[+/-sd] median max 129 | Connect: 0 0 0.2 0 33 130 | Processing: 0 7 0.9 7 33 131 | Waiting: 0 7 0.9 7 21 132 | Total: 0 7 1.0 7 47 133 | 134 | Percentage of the requests served within a certain time (ms) 135 | 50% 7 136 | 66% 7 137 | 75% 7 138 | 80% 7 139 | 90% 8 140 | 95% 8 141 | 98% 9 142 | 99% 10 143 | 100% 47 (longest request) 144 | ``` 145 | -------------------------------------------------------------------------------- /docs/debug.md: -------------------------------------------------------------------------------- 1 | # BUG解决记录 2 | 3 | ## Task异常 4 | 5 | gdb调试bt查看堆栈后发现是`std::future`的exception_ptr问题,应该是异常问题,但我没咋用过异常(Google 6 | style不允许使用异常,就没关注过)。 7 | 所以我解决办法是直接把`std::future`去掉了,换成朴素点的实现 8 | 9 | ## echo_server莫名挂掉 10 | 11 | gdb启动调试,发现挂掉是因为`SITPIPE`信号。 12 | 具体原因参考资料: 13 | 14 | 1. https://www.cnblogs.com/lit10050528/p/5116566.html 15 | 2. http://senlinzhan.github.io/2017/03/02/sigpipe/ 16 | 解决办法是忽略`SIGPIPE`信号 17 | 18 | ## echo_server进行bench的时候,测试工具显示echo_server返回的数据长度不对 19 | 20 | 测试工具发送的是512bytes的数据,但接收到的部分数据大小有40-490的都有。 21 | 原因:TCP粘包问题 22 | 最开始echo_server的buffer大小设置为了1024字节 23 | 24 | ## 性能测试低 25 | 26 | 跟muduo一起对比了echo_server的性能,发现我这个demo性能跟muduo单线程的性能差不多( 27 | 14w/s),但muduo开10个线程时性能是单线程的接近5倍。 28 | 分析了一下目前我这个IO库,是单线程的,CPU利用率上不去,所以性能低 29 | 可以考虑增加一个“协程池”?就是各个协程放在不同的线程去跑 30 | 31 | ## Arch Linux core dump文件在哪? 32 | 33 | 详细教程:https://www.christian-schneider.it/blog/analyzing-linux-coredump/ 34 | 35 | ## Interrupted system call 36 | 37 | epoll_await返回-1时出现了EINTR。 38 | 我们在利用 gdb 调试带有 epoll_wait select sem_wat 的多线程代码的时候可能会出现非正常返回 39 | -1 的情况,错误原因是:Interrupted system call。 40 | 这是由于 gdb调试的时候会在断点处插入一条中断指令,当程序执行到该断点处的时候会发送一个SIGTRAP信号,程序转去执行中断相应,进而gdb让程序停下来进行调试. 41 | 对于sem_wait\wait\read等会阻塞的函数在调试时,如果阻塞,都可能会收到调试器发送的信号,而返回非0值. -------------------------------------------------------------------------------- /docs/http.md: -------------------------------------------------------------------------------- 1 | # 类似gin的restful HTTP框架 2 | ## 简介 3 | API设计参考`gin`,作为`luce`的应用示例,暂时不考虑性能优化。 4 | 5 | ## 功能实现 6 | - GET 7 | - POST 8 | - 简单POST报文解析,目前请求体的解析只支持了`param1=value1¶m2=value2`这种格式 9 | - PUT 10 | 11 | ## example 12 | 监听`/add/`,POST报文请求体中带有两个参数,返回这两个参数之和 13 | ``` 14 | http_app.POST("/add/", [](const net::http::ContextPtr &ctx) { 15 | LOG_INFO("POST run: {}", ctx->req_->body_.c_str()); 16 | 17 | auto param1 = ctx->QueryBody("param1"); 18 | auto param2 = ctx->QueryBody("param2"); 19 | if (param1.empty() || param2.empty()) { 20 | ctx->HTML(404, "Error Param"); 21 | return; 22 | } 23 | auto res = atoi(param1.c_str()) + atoi(param2.c_str()); 24 | ctx->HTML(200, Format("res: {}\n", res)); 25 | }); 26 | 27 | ``` 28 | -------------------------------------------------------------------------------- /docs/interface.md: -------------------------------------------------------------------------------- 1 | # 接口设计 2 | 3 | ## 协程网络库应用层接口 4 | ```cpp 5 | void OnRequest(TcpConnectoin *conn, TcpServer &server) { 6 | char buf[1024]; 7 | co_await conn->recv(&buf); 8 | 9 | // 对buf进行一些业务逻辑操作 10 | do_job(&buf); 11 | 12 | co_await conn->send(&buf); 13 | } 14 | ``` 15 | -------------------------------------------------------------------------------- /example/echo_server.cpp: -------------------------------------------------------------------------------- 1 | // same as tests/net/echo_server2.cpp 2 | 3 | #include "luce/common/logger.h" 4 | #include "luce/common/thread_pool.h" 5 | #include "luce/io/io_awaiter.h" 6 | #include "luce/net/tcp_all.h" 7 | 8 | class EchoServer : public net::TcpApplication { 9 | private: 10 | co::Task<> OnRequest(net::TcpConnectionPtr conn, 11 | net::TcpServer& server) override { 12 | while (true) { 13 | net::IOBuffer buffer(512); 14 | ssize_t recv_len = co_await conn->AsyncRead(&buffer); 15 | if (recv_len < 0) { 16 | LOG_ERROR("EchoServer read error"); 17 | break; 18 | } 19 | if (recv_len == 0) { 20 | LOG_INFO("client closed"); 21 | break; 22 | } 23 | 24 | LOG_DEBUG("Done send\n"); 25 | auto res = co_await conn->AsyncWrite(buffer); 26 | if (res != recv_len) { 27 | LOG_ERROR("EchoServer write error"); 28 | } 29 | } 30 | } 31 | }; 32 | 33 | int main(int argc, char* argv[]) { 34 | net::InetAddress addr{12345}; 35 | EchoServer app; 36 | net::TcpServer server(addr, &app, 8); 37 | server.Start(); 38 | LOG_INFO("all down"); 39 | } 40 | -------------------------------------------------------------------------------- /example/xmake.lua: -------------------------------------------------------------------------------- 1 | target("echo_server") 2 | set_kind("binary") 3 | add_files("$(projectdir)/example/echo_server.cpp") 4 | add_deps("luce") 5 | add_cxxflags("-O3") 6 | -------------------------------------------------------------------------------- /luce/co/co_thread_pool.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/co/co_thread_pool.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "luce/common/logger.h" 8 | 9 | namespace co { 10 | 11 | namespace { 12 | 13 | void GetCurrentCpus(std::vector* ids) { 14 | cpu_set_t set; 15 | ids->clear(); 16 | if (sched_getaffinity(0, sizeof(set), &set) == 0) { 17 | for (uint32_t i = 0; i < CPU_SETSIZE; ++i) { 18 | if (CPU_ISSET(i, &set)) { 19 | ids->emplace_back(i); 20 | } 21 | } 22 | } 23 | } 24 | 25 | } // namespace 26 | 27 | ThreadPool::ThreadPool(size_t thread_num, bool enable_work_steal, 28 | bool enable_core_bindings) 29 | : thread_num_(thread_num != 0U ? thread_num 30 | : std::thread::hardware_concurrency()), 31 | task_queues_(thread_num), 32 | stopped_(false), 33 | enable_core_bindings_(enable_core_bindings), 34 | enable_work_steal_(enable_work_steal) { 35 | auto worker = [this](size_t id) { 36 | auto* current = GetCurrent(); 37 | current->first = id; 38 | current->second = this; 39 | while (true) { 40 | TaskItem task_item{}; 41 | if (enable_work_steal_) { 42 | // 首先尝试去 work steal 43 | for (auto i = 0; i < thread_num_; ++i) { 44 | if (task_queues_[(id + i) % thread_num_].try_pop_if( 45 | &task_item, [](auto& item) { return item.can_steal; })) { 46 | break; 47 | } 48 | } 49 | } 50 | 51 | if (!task_item.handle && !task_queues_[id].pop(&task_item)) { 52 | // 如果一个线程已经停止,不要再等待新任务 53 | // 否则等待 pop 如果不允许抢占,或者抢占失败 54 | if (stopped_) { 55 | break; 56 | } 57 | continue; 58 | } 59 | 60 | if (task_item.handle) { 61 | task_item.handle.resume(); 62 | } 63 | } 64 | }; 65 | 66 | workers_.reserve(thread_num_); 67 | 68 | // 获取可用的 CPU 69 | std::vector cpu_ids; 70 | if (enable_core_bindings_) { 71 | GetCurrentCpus(&cpu_ids); 72 | } 73 | const auto cpu_num = cpu_ids.size(); 74 | 75 | for (auto i = 0; i < thread_num_; ++i) { 76 | workers_.emplace_back(worker, i); 77 | 78 | if (!enable_core_bindings_) { 79 | continue; 80 | } 81 | 82 | // run threads per core 83 | cpu_set_t cpuset; 84 | CPU_ZERO(&cpuset); 85 | CPU_SET(cpu_ids[i % cpu_num], &cpuset); 86 | int res = sched_setaffinity(static_cast(workers_[i].native_handle()), 87 | sizeof(cpu_set_t), &cpuset); 88 | if (res != 0) { 89 | LOG_ERROR("error calling sched_setaffinity: {}", res); 90 | } 91 | } 92 | } 93 | 94 | ThreadPool::~ThreadPool() { 95 | stopped_ = true; 96 | for (auto& queue : task_queues_) { 97 | queue.stop(); 98 | } 99 | for (auto& worker : workers_) { 100 | worker.join(); 101 | } 102 | } 103 | 104 | void ThreadPool::ScheduleById(TaskItem::TaskType coro, int32_t id) { 105 | if (nullptr == coro) { 106 | return; 107 | } 108 | if (stopped_) { 109 | return; 110 | } 111 | if (id == -1) { 112 | if (enable_work_steal_) { 113 | // try to push to a non-block queue firstly 114 | TaskItem task_item{coro, true}; 115 | for (auto i = 0; i < thread_num_ * 2; ++i) { 116 | if (task_queues_.at(i % thread_num_).try_push(task_item)) { 117 | return; 118 | } 119 | } 120 | } 121 | 122 | id = rand() % thread_num_; 123 | task_queues_[id].push(TaskItem{coro, enable_work_steal_}); 124 | } else { 125 | assert(id < thread_num_); 126 | task_queues_[id].push(TaskItem{coro, false}); 127 | } 128 | } 129 | 130 | int32_t ThreadPool::GetCurrentId() const { 131 | auto* current = GetCurrent(); 132 | if (this == current->second) { 133 | return current->first; 134 | } 135 | return -1; 136 | } 137 | 138 | size_t ThreadPool::GetItemCount() const { 139 | size_t res = 0; 140 | for (auto i = 0; i < thread_num_; ++i) { 141 | res += task_queues_.at(i).size(); 142 | } 143 | return res; 144 | } 145 | 146 | std::pair* ThreadPool::GetCurrent() const { 147 | static thread_local std::pair current(-1, nullptr); 148 | return ¤t; 149 | } 150 | 151 | } // namespace co -------------------------------------------------------------------------------- /luce/co/co_thread_pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "luce/common/blocking_queue.h" 11 | 12 | namespace co { 13 | 14 | class ThreadPool { 15 | public: 16 | struct TaskItem { 17 | using TaskType = std::coroutine_handle<>; 18 | 19 | TaskType handle{nullptr}; 20 | bool can_steal{false}; 21 | }; 22 | 23 | explicit ThreadPool(size_t thread_num = std::thread::hardware_concurrency(), 24 | bool enable_work_steal = false, 25 | bool enable_core_bindings = false); 26 | 27 | ~ThreadPool(); 28 | 29 | void ScheduleById(TaskItem::TaskType coro, int32_t id = -1); 30 | 31 | int32_t GetCurrentId() const; 32 | size_t GetItemCount() const; 33 | size_t GetThreadNum() const { return thread_num_; }; 34 | 35 | private: 36 | std::pair* GetCurrent() const; 37 | size_t thread_num_; 38 | 39 | std::vector> task_queues_; 40 | std::vector workers_; 41 | 42 | std::atomic stopped_; 43 | bool enable_core_bindings_; 44 | bool enable_work_steal_; 45 | }; 46 | 47 | } // namespace co -------------------------------------------------------------------------------- /luce/co/scheduler.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/co/scheduler.h" 2 | #include "luce/co/co_thread_pool.h" 3 | 4 | namespace co { 5 | 6 | void Scheduler::co_spawn(Task<>&& task) noexcept { 7 | auto handle = task.get_handle(); 8 | task.detach(); 9 | tp_.ScheduleById(handle); 10 | } 11 | 12 | } // namespace co -------------------------------------------------------------------------------- /luce/co/scheduler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "luce/co/co_thread_pool.h" 5 | #include "luce/co/task.h" 6 | #include "luce/common/singleton.h" 7 | namespace co { 8 | 9 | class Scheduler { 10 | public: 11 | Scheduler() noexcept = default; 12 | 13 | void co_spawn(Task<>&& task) noexcept; 14 | 15 | private: 16 | co::ThreadPool tp_; 17 | }; 18 | 19 | void co_spawn(Task<>&& task) noexcept { 20 | Singleton::GetInstance()->co_spawn(std::forward>(task)); 21 | } 22 | 23 | } // namespace co -------------------------------------------------------------------------------- /luce/co/task.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "luce/common/logger.h" 10 | #include "luce/common/noncopyable.h" 11 | 12 | namespace co { 13 | 14 | template 15 | class Result { 16 | public: 17 | void return_value(T&& value) { value_ = T(std::move(value)); } 18 | void return_value(const T& value) { value_ = value; } 19 | 20 | std::suspend_always yield_value(T&& value) { 21 | value_ = T(std::move(value)); 22 | return {}; 23 | } 24 | 25 | std::suspend_always yield_value(const T& value) { 26 | value_ = value; 27 | return {}; 28 | } 29 | 30 | void set_value(T&& value) { value_ = T(std::move(value)); } 31 | void set_value(const T& value) { value_ = value; } 32 | 33 | T result() { return value_; } 34 | 35 | T value_; 36 | }; 37 | 38 | template <> 39 | class Result { 40 | public: 41 | void return_void() {} 42 | void result() {} 43 | }; 44 | 45 | template 46 | struct PromiseBase : public Result { 47 | std::suspend_always initial_suspend() { return {}; } 48 | 49 | decltype(auto) final_suspend() noexcept { 50 | struct Awaiter { 51 | bool await_ready() noexcept { return false; } 52 | 53 | std::coroutine_handle<> await_suspend( 54 | CoroHandle suspended_coro) noexcept { 55 | // let coroutine_handle go out, it comes from await_suspend 56 | auto& promise = suspended_coro.promise(); 57 | std::coroutine_handle<> continuation = promise.continuation_; 58 | if (promise.is_detached_) { 59 | LOG_DEBUG("suspended_coro.destroy();"); 60 | suspended_coro.destroy(); 61 | } 62 | return continuation; 63 | } 64 | 65 | // won't never resume 66 | constexpr void await_resume() const noexcept {} 67 | }; 68 | return Awaiter{}; 69 | } 70 | 71 | void set_continuation(std::coroutine_handle<> continuation) { 72 | continuation_ = continuation; 73 | } 74 | 75 | void set_thrd(int32_t thrd_id) { thrd_id_ = thrd_id; } 76 | 77 | void detach() noexcept { is_detached_ = true; } 78 | 79 | std::coroutine_handle<> continuation_{std::noop_coroutine()}; 80 | bool is_detached_{false}; 81 | int32_t thrd_id_{-1}; 82 | }; 83 | 84 | template 85 | struct Task : noncopyable { 86 | struct promise_type 87 | : public PromiseBase> { 88 | promise_type() = default; 89 | 90 | Task get_return_object() { 91 | return Task{std::coroutine_handle::from_promise(*this)}; 92 | } 93 | 94 | void unhandled_exception() { LOG_FATAL("unhandled exception"); } 95 | }; 96 | 97 | struct TaskAwaiterBase { 98 | explicit TaskAwaiterBase(std::coroutine_handle handle) 99 | : handle_(handle) {} 100 | 101 | bool await_ready() { 102 | bool res = !handle_ || handle_.done(); 103 | LOG_DEBUG("TaskAwaitter await_ready: {}", res); 104 | return res; 105 | } 106 | 107 | std::coroutine_handle<> await_suspend( 108 | std::coroutine_handle<> continuation) noexcept { 109 | LOG_DEBUG("set continuation"); 110 | handle_.promise().set_continuation(continuation); 111 | return handle_; 112 | } 113 | 114 | std::coroutine_handle handle_; 115 | }; 116 | 117 | using CoroHandle = std::coroutine_handle; 118 | 119 | explicit Task(CoroHandle handle) : handle_(handle) { 120 | LOG_DEBUG("new Task: {}", handle.address()); 121 | } 122 | 123 | Task(Task&& other) noexcept 124 | : handle_(std::exchange(other.handle_, nullptr)) {} 125 | 126 | ~Task() { 127 | LOG_DEBUG("~Task: {}", handle_.address()); 128 | if (handle_) { 129 | handle_.destroy(); 130 | } 131 | } 132 | 133 | auto operator co_await() const& noexcept { 134 | struct Awaiter : TaskAwaiterBase { 135 | using TaskAwaiterBase::TaskAwaiterBase; 136 | 137 | decltype(auto) await_resume() { return this->handle_.promise().result(); } 138 | }; 139 | return Awaiter(handle_); 140 | } 141 | 142 | auto operator co_await() const&& noexcept { 143 | struct Awaiter : TaskAwaiterBase { 144 | using TaskAwaiterBase::TaskAwaiterBase; 145 | 146 | decltype(auto) await_resume() { 147 | return std::move(this->handle_.promise()).result(); 148 | } 149 | }; 150 | return Awaiter(handle_); 151 | } 152 | 153 | bool ready() const noexcept { return !handle_ || handle_.done(); } 154 | 155 | T result() const { return handle_.promise().result(); } 156 | 157 | // NOTE(pgj): 这里的实现比较粗糙,只是为了能够在 main 函数执行一个 coroutine 158 | T run() { 159 | if (ready()) { 160 | return result(); 161 | } 162 | auto handle = handle_; 163 | detach(); 164 | handle.resume(); 165 | return handle.promise().result(); 166 | } 167 | 168 | void resume() { handle_.resume(); } 169 | 170 | // explicit operator CoroHandle() const { return handle_; } 171 | 172 | CoroHandle get_handle() noexcept { return handle_; } 173 | 174 | void detach() noexcept { 175 | handle_.promise().detach(); 176 | handle_ = nullptr; 177 | } 178 | 179 | friend void swap(Task& lhs, Task& rhs) noexcept { 180 | std::swap(lhs.handle_, rhs.handle_); 181 | } 182 | 183 | CoroHandle handle_; 184 | }; 185 | 186 | } // namespace co 187 | -------------------------------------------------------------------------------- /luce/codec/abstract_coder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "luce/codec/abstract_protocol.h" 5 | namespace codec { 6 | 7 | using Buffer = std::vector; 8 | 9 | class AbstractCoder { 10 | public: 11 | virtual ~AbstractCoder() = default; 12 | 13 | virtual void Encode(AbstractProtocol::Ptr& in_message, 14 | Buffer* out_buffer) = 0; 15 | 16 | virtual void Decode(const Buffer& in_buffer, 17 | AbstractProtocol::Ptr* out_messages) = 0; 18 | }; 19 | 20 | } // namespace codec -------------------------------------------------------------------------------- /luce/codec/abstract_protocol.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | namespace codec { 6 | 7 | class AbstractProtocol : std::enable_shared_from_this { 8 | public: 9 | using Ptr = std::shared_ptr; 10 | 11 | virtual ~AbstractProtocol() = default; 12 | 13 | // 请求号,标识唯一一个请求或者响应 14 | std::string msg_id; // NOLINT 15 | }; 16 | 17 | } // namespace codec -------------------------------------------------------------------------------- /luce/codec/serializer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "luce/codec/type_helper.h" 15 | 16 | namespace codec { 17 | 18 | class Serializer { 19 | public: 20 | Serializer() = default; 21 | explicit Serializer(const std::string& data) 22 | : buffer_(data.begin(), data.end()) {} 23 | 24 | using IterType = std::vector::iterator; 25 | using ConstIterType = std::vector::const_iterator; 26 | 27 | Serializer(IterType begin, IterType end) : buffer_(begin, end) {} 28 | Serializer(ConstIterType begin, ConstIterType end) : buffer_(begin, end) {} 29 | 30 | ConstIterType cbegin() const { return buffer_.cbegin(); } 31 | ConstIterType cend() const { return buffer_.cend(); } 32 | 33 | template 34 | std::enable_if_t, void> serialize(const T& value) { 35 | const char* data = reinterpret_cast(&value); 36 | buffer_.insert(buffer_.end(), data, data + sizeof(value)); 37 | } 38 | 39 | template 40 | std::enable_if_t, void> deserialize(T* value) { 41 | char* data = reinterpret_cast(value); 42 | const auto size = sizeof(*value); 43 | std::copy(buffer_.data() + deserialize_pos_, 44 | buffer_.data() + deserialize_pos_ + size, data); 45 | deserialize_pos_ += size; 46 | } 47 | 48 | template 49 | std::enable_if_t, void> serialize(const T& value) { 50 | value.serialize(this); 51 | } 52 | 53 | template 54 | std::enable_if_t, void> deserialize(T* value) { 55 | value->deserialize(this); 56 | } 57 | 58 | void serialize(const std::string& value) { 59 | const auto size = value.size(); 60 | serialize(size); 61 | buffer_.insert(buffer_.end(), value.begin(), value.end()); 62 | } 63 | 64 | void deserialize(std::string* value) { 65 | auto size = value->size(); 66 | deserialize(&size); 67 | value->resize(size); 68 | std::copy(buffer_.data() + deserialize_pos_, 69 | buffer_.data() + deserialize_pos_ + size, value->begin()); 70 | deserialize_pos_ += size; 71 | } 72 | 73 | // // std::vector 的序列化和反序列化 74 | // template 75 | // void serialize(const std::vector &value) { 76 | // const auto size = value.size(); 77 | // serialize(size); 78 | // std::for_each(value.cbegin(), value.cend(), 79 | // [&](const T &item) { serialize(item); }); 80 | // } 81 | 82 | // template 83 | // void deserialize(std::vector *value) { 84 | // auto size = value->size(); 85 | // deserialize(&size); 86 | // value->resize(size); 87 | // std::for_each(value->begin(), value->end(), 88 | // [&](T &item) { deserialize(&item); }); 89 | // } 90 | 91 | // 顺序容器的序列化和反序列化 92 | template 93 | std::enable_if_t, void> serialize( 94 | const Container& value) { 95 | uint32_t size = 0; 96 | if constexpr (has_size_method_v) { 97 | size = value.size(); 98 | } else { 99 | for (auto iter = value.cbegin(); iter != value.cend(); ++iter) { 100 | ++size; 101 | } 102 | } 103 | serialize(size); 104 | std::for_each( 105 | value.cbegin(), value.cend(), 106 | [&](const typename Container::value_type& item) { serialize(item); }); 107 | } 108 | 109 | template 110 | std::enable_if_t, void> deserialize( 111 | Container* value) { 112 | uint32_t size = 0; 113 | deserialize(&size); 114 | value->resize(size); 115 | std::for_each( 116 | value->begin(), value->end(), 117 | [&](typename Container::value_type& item) { deserialize(&item); }); 118 | } 119 | 120 | // std::forward_list: (为什么没有size方法.....) 121 | // template 122 | // void serialize(const std::forward_list &value) { 123 | // std::list tmp{value.cbegin(), value.cend()}; 124 | // serialize(tmp); 125 | // } 126 | 127 | // template 128 | // void deserialize(std::forward_list *value) { 129 | // std::list tmp; 130 | // deserialize(&tmp); 131 | // value->assign(tmp.begin(), tmp.end()); 132 | // } 133 | 134 | // std::map 的序列化和反序列化 135 | template 136 | void serialize(const std::map& value) { 137 | const auto size = value.size(); 138 | serialize(size); 139 | for (const auto& [key, val] : value) { 140 | serialize(key); 141 | serialize(val); 142 | } 143 | } 144 | 145 | template 146 | void deserialize(std::map* value) { 147 | auto size = value->size(); 148 | deserialize(&size); 149 | for (auto i = 0; i < size; ++i) { 150 | Key key; 151 | Value val; 152 | deserialize(key); 153 | deserialize(val); 154 | (*value)[key] = val; 155 | } 156 | } 157 | 158 | // std::tuple 的序列化和反序列化 159 | template 160 | void serialize(const std::tuple& value) { 161 | std::apply([&](const Args&... args) { (serialize(args), ...); }, value); 162 | } 163 | 164 | template 165 | void deserialize(std::tuple* value) { 166 | std::apply([&](Args&... args) { (deserialize(&args), ...); }, *value); 167 | } 168 | 169 | std::string str() const { return {buffer_.begin(), buffer_.end()}; } 170 | 171 | size_t size() const { return buffer_.size(); } 172 | 173 | void clear() { 174 | buffer_.clear(); 175 | deserialize_pos_ = 0; 176 | } 177 | 178 | private: 179 | std::vector buffer_; 180 | int deserialize_pos_ = 0; 181 | }; 182 | 183 | } // namespace codec -------------------------------------------------------------------------------- /luce/codec/tinypb_coder.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/codec/tinypb_coder.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "luce/codec/tinypb_protocol.h" 8 | #include "luce/common/logger.h" 9 | #include "luce/net/util.h" 10 | 11 | namespace codec { 12 | 13 | void TinyPBCoder::Encode(AbstractProtocol::Ptr& in_message, 14 | Buffer* out_buffer) { 15 | std::shared_ptr msg = 16 | std::dynamic_pointer_cast(in_message); 17 | int len = 0; 18 | const char* buf = EncodeTinyPB(msg, &len); 19 | if (buf != nullptr && len != 0) { 20 | for (auto i = 0; i < len; ++i) { 21 | out_buffer->emplace_back(buf[i]); 22 | } 23 | } 24 | if (buf != nullptr) { 25 | delete buf; 26 | buf = nullptr; 27 | } 28 | } 29 | 30 | void TinyPBCoder::Decode(const Buffer& in_buffer, 31 | AbstractProtocol::Ptr* out_message) { 32 | const auto buffer_size = in_buffer.size(); 33 | int start_index = 0; 34 | // PB_START 是第一个位置 35 | if (buffer_size <= start_index || 36 | in_buffer[start_index] != TinyPBProtocol::PB_START) { 37 | LOG_ERROR("decode tinypb error, cannot found PB_START"); 38 | return; 39 | } 40 | // 读4个字符获取长度 41 | int pk_len = net::GetInt32ForNetByte(&in_buffer[start_index + 1]); 42 | LOG_DEBUG("pk_len = {}", pk_len); 43 | int end_index = start_index + pk_len - 1; 44 | if (buffer_size - 1 != end_index || 45 | in_buffer[end_index] != TinyPBProtocol::PB_END) { 46 | LOG_ERROR("decode tinypb error, cannot found PB_END"); 47 | return; 48 | } 49 | 50 | std::shared_ptr message = std::make_shared(); 51 | message->pk_len = pk_len; 52 | const auto msg_id_len_index = 53 | start_index + sizeof(char) + sizeof(message->pk_len); 54 | if (msg_id_len_index >= end_index) { 55 | message->parse_success = false; 56 | LOG_ERROR("parse error, msg_id_len_index[{}] >= end_index[{}]", 57 | msg_id_len_index, end_index); 58 | return; 59 | } 60 | message->msg_id_len = net::GetInt32ForNetByte(&in_buffer[msg_id_len_index]); 61 | LOG_DEBUG("parse msg_id_len = {}", message->msg_id_len); 62 | 63 | const auto msg_id_index = msg_id_len_index + sizeof(message->msg_id_len); 64 | char msg_id[100] = {0}; 65 | std::memcpy(&msg_id[0], &in_buffer[msg_id_index], message->msg_id_len); 66 | message->msg_id = std::string(msg_id); 67 | LOG_DEBUG("parse msg_id = {}", message->msg_id); 68 | 69 | const auto method_name_len_index = msg_id_index + message->msg_id_len; 70 | if (method_name_len_index >= end_index) { 71 | message->parse_success = false; 72 | LOG_ERROR("parse error, method_name_len_index[{}] >= end_index[{}]", 73 | method_name_len_index, end_index); 74 | return; 75 | } 76 | message->method_name_len = 77 | net::GetInt32ForNetByte(&in_buffer[method_name_len_index]); 78 | 79 | const auto method_name_index = 80 | method_name_len_index + sizeof(message->method_name_len); 81 | char method_name[512] = {0}; 82 | std::memcpy(&method_name[0], &in_buffer[method_name_index], 83 | message->method_name_len); 84 | message->method_name = std::string(method_name); 85 | LOG_DEBUG("parse method_name = {}", method_name); 86 | 87 | const auto err_code_index = method_name_index + message->method_name_len; 88 | if (err_code_index >= end_index) { 89 | message->parse_success = false; 90 | LOG_ERROR("parse error, err_code_index[{}] >= end_index[{}]", 91 | err_code_index, end_index); 92 | return; 93 | } 94 | message->err_code = net::GetInt32ForNetByte(&in_buffer[err_code_index]); 95 | 96 | const auto err_info_len_index = err_code_index + sizeof(message->err_code); 97 | if (err_info_len_index >= end_index) { 98 | message->parse_success = false; 99 | LOG_ERROR("parse error, err_info_len_index[{}] >= end_index[{}]", 100 | err_info_len_index, end_index); 101 | return; 102 | } 103 | message->err_info_len = 104 | net::GetInt32ForNetByte(&in_buffer[err_info_len_index]); 105 | 106 | const auto err_info_index = 107 | err_info_len_index + sizeof(message->err_info_len); 108 | char err_info[512] = {0}; 109 | std::memcpy(&err_info[0], &in_buffer[err_info_index], message->err_info_len); 110 | message->err_info = std::string(err_info); 111 | LOG_DEBUG("parse err_info = {}", message->err_info); 112 | 113 | const auto pb_data_len = message->pk_len - message->method_name_len - 114 | message->msg_id_len - message->err_info_len - 2 - 24; 115 | const auto pb_data_index = err_info_index + message->err_info_len; 116 | message->pb_data = std::string(&in_buffer[pb_data_index], pb_data_len); 117 | 118 | // TODO(pgj): check sum 119 | message->parse_success = true; 120 | *out_message = message; 121 | } 122 | 123 | const char* TinyPBCoder::EncodeTinyPB( 124 | const std::shared_ptr& message, int* len) { 125 | if (message->msg_id.empty()) { 126 | message->msg_id = "123456789"; 127 | } 128 | LOG_DEBUG("msg_id = {}", message->msg_id); 129 | const int pk_len = 2 + 24 + message->msg_id.length() + // NOLINT 130 | message->method_name.length() + 131 | message->err_info.length() + message->pb_data.length(); 132 | LOG_DEBUG("pk_len = {}", pk_len); 133 | 134 | char* buf = new char[pk_len]; 135 | char* tmp = buf; 136 | 137 | *tmp = TinyPBProtocol::PB_START; 138 | tmp++; 139 | 140 | int32_t pk_len_net = net::GetNetInt32(pk_len); 141 | std::memcpy(tmp, &pk_len_net, sizeof(pk_len_net)); 142 | tmp += sizeof(pk_len_net); 143 | 144 | int msg_id_len = static_cast(message->msg_id.length()); 145 | int32_t msg_id_len_net = net::GetNetInt32(msg_id_len); 146 | std::memcpy(tmp, &msg_id_len_net, sizeof(msg_id_len_net)); 147 | tmp += sizeof(msg_id_len_net); 148 | 149 | if (!message->msg_id.empty()) { 150 | std::memcpy(tmp, message->msg_id.data(), msg_id_len); 151 | tmp += msg_id_len; 152 | } 153 | 154 | int method_name_len = static_cast(message->method_name.length()); 155 | int32_t method_name_len_net = net::GetNetInt32(method_name_len); 156 | std::memcpy(tmp, &method_name_len_net, sizeof(method_name_len_net)); 157 | tmp += sizeof(method_name_len_net); 158 | 159 | if (!message->method_name.empty()) { 160 | std::memcpy(tmp, message->method_name.data(), method_name_len); 161 | tmp += method_name_len; 162 | } 163 | 164 | int32_t err_code_net = net::GetNetInt32(message->err_code); 165 | std::memcpy(tmp, &err_code_net, sizeof(err_code_net)); 166 | tmp += sizeof(err_code_net); 167 | 168 | int err_info_len = static_cast(message->err_info.length()); 169 | int32_t err_info_len_net = net::GetNetInt32(err_info_len); 170 | std::memcpy(tmp, &err_info_len_net, sizeof(err_info_len_net)); 171 | tmp += sizeof(err_info_len_net); 172 | 173 | if (!message->err_info.empty()) { 174 | std::memcpy(tmp, message->err_info.data(), err_info_len); 175 | tmp += err_info_len; 176 | } 177 | 178 | if (!message->pb_data.empty()) { 179 | std::memcpy(tmp, message->pb_data.data(), message->pb_data.length()); 180 | tmp += message->pb_data.length(); 181 | } 182 | 183 | int32_t check_sum_net = net::GetNetInt32(1); 184 | std::memcpy(tmp, &check_sum_net, sizeof(check_sum_net)); 185 | tmp += sizeof(check_sum_net); 186 | 187 | *tmp = TinyPBProtocol::PB_END; 188 | 189 | message->pk_len = pk_len; 190 | message->msg_id_len = msg_id_len; 191 | message->method_name_len = method_name_len; 192 | message->err_info_len = err_info_len; 193 | message->parse_success = true; 194 | *len = pk_len; 195 | return buf; 196 | } 197 | 198 | } // namespace codec -------------------------------------------------------------------------------- /luce/codec/tinypb_coder.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "luce/codec/abstract_coder.h" 5 | #include "luce/codec/tinypb_protocol.h" 6 | namespace codec { 7 | 8 | class TinyPBCoder : public AbstractCoder { 9 | public: 10 | TinyPBCoder() = default; 11 | ~TinyPBCoder() override = default; 12 | 13 | void Encode(AbstractProtocol::Ptr& in_message, Buffer* out_buffer) override; 14 | 15 | void Decode(const Buffer& in_buffer, 16 | AbstractProtocol::Ptr* out_message) override; 17 | 18 | private: 19 | const char* EncodeTinyPB(const std::shared_ptr& message, 20 | int* len); 21 | }; 22 | 23 | } // namespace codec -------------------------------------------------------------------------------- /luce/codec/tinypb_protocol.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/codec/tinypb_protocol.h" 2 | 3 | namespace codec { 4 | 5 | char TinyPBProtocol::PB_START = 0x02; 6 | char TinyPBProtocol::PB_END = 0x03; 7 | 8 | } // namespace codec -------------------------------------------------------------------------------- /luce/codec/tinypb_protocol.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "luce/codec/abstract_protocol.h" 4 | namespace codec { 5 | 6 | class TinyPBProtocol : public AbstractProtocol { 7 | public: 8 | TinyPBProtocol() = default; 9 | ~TinyPBProtocol() override = default; 10 | 11 | public: 12 | static char PB_START; 13 | static char PB_END; 14 | 15 | public: 16 | int32_t pk_len{0}; 17 | int32_t msg_id_len{0}; 18 | 19 | int32_t method_name_len{0}; 20 | std::string method_name; 21 | int32_t err_code{0}; 22 | int32_t err_info_len{0}; 23 | std::string err_info; 24 | std::string pb_data; 25 | int32_t check_sum{0}; 26 | 27 | bool parse_success{false}; 28 | }; 29 | 30 | } // namespace codec -------------------------------------------------------------------------------- /luce/codec/type_helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | namespace codec { 9 | 10 | class Serializer; 11 | 12 | // 利用 SFINAE 13 | // 即在模板实例化过程中,如果某个表达式的推导无效(例如,调用不存在的方法),模板不会被认为是错误,而是选择另一条候选路径。 14 | // 通过使用decltype和std::declval来模拟方法调用,并使用SFINAE技术通过重载解析来进行检查。 15 | // 注意,该方法仅适用于检查成员方法,无法用于检查非成员函数、静态方法或独立函数。 16 | // 判断一个类有没有 'serialize' 方法 17 | template 18 | struct HasSerializeMethod { 19 | private: 20 | template 21 | static constexpr auto test(int) 22 | -> decltype(std::declval().serialize(std::declval()), 23 | std::true_type()); 24 | 25 | template 26 | static constexpr auto test(...) -> decltype(std::false_type()); 27 | 28 | public: 29 | static constexpr bool value = 30 | std::is_same_v(0)), std::true_type>; 31 | }; 32 | 33 | template 34 | constexpr bool has_serialize_method_v = HasSerializeMethod::value; 35 | 36 | // 判断一个类有没有 'deserialize' 方法 37 | template 38 | struct HasDeserializeMethod { 39 | private: 40 | template 41 | static constexpr auto test(int) 42 | -> decltype(std::declval().deserialize(std::declval()), 43 | std::true_type()); 44 | 45 | template 46 | static constexpr auto test(...) -> decltype(std::false_type()); 47 | 48 | public: 49 | static constexpr bool value = 50 | std::is_same_v(0)), std::true_type>; 51 | }; 52 | 53 | template 54 | constexpr bool has_deserialize_method_v = HasDeserializeMethod::value; 55 | 56 | // 判断一个类有没有 'size' 方法 57 | template 58 | struct HasSizeMethod { 59 | private: 60 | template 61 | static constexpr auto test(int) 62 | -> decltype(std::declval().size(), std::true_type()); 63 | 64 | template 65 | static constexpr auto test(...) -> decltype(std::false_type()); 66 | 67 | public: 68 | static constexpr bool value = 69 | std::is_same_v(0)), std::true_type>; 70 | }; 71 | 72 | template 73 | constexpr bool has_size_method_v = HasSizeMethod::value; 74 | 75 | // 默认类型为false 76 | template 77 | struct IsSequenceContainerType { 78 | static constexpr bool value = false; 79 | }; 80 | 81 | // Vector类型为true 82 | template 83 | struct IsSequenceContainerType> { 84 | static constexpr bool value = true; 85 | }; 86 | 87 | // deque类型 88 | template 89 | struct IsSequenceContainerType> { 90 | static constexpr bool value = true; 91 | }; 92 | 93 | // list 类型 94 | template 95 | struct IsSequenceContainerType> { 96 | static constexpr bool value = true; 97 | }; 98 | 99 | // forward_list 类型 100 | template 101 | struct IsSequenceContainerType> { 102 | static constexpr bool value = true; 103 | }; 104 | 105 | // 定义获取容器类型的模板 106 | template 107 | constexpr bool is_sequence_container_type_v = IsSequenceContainerType::value; 108 | 109 | } // namespace codec -------------------------------------------------------------------------------- /luce/common/Timestamp.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/common/Timestamp.h" 2 | 3 | #include 4 | 5 | Timestamp::Timestamp() : microSecondsSinceEpoch_(0) {} 6 | 7 | Timestamp::Timestamp(int64_t microSecondsSinceEpoch) 8 | : microSecondsSinceEpoch_(microSecondsSinceEpoch) {} 9 | 10 | Timestamp Timestamp::now() { return Timestamp(time(nullptr)); } 11 | 12 | std::string Timestamp::toString() const { 13 | char buf[128] = {0}; 14 | tm* tm_time = localtime(µSecondsSinceEpoch_); 15 | snprintf(buf, 128, "%4d/%02d/%02d %02d:%02d:%02d", 16 | tm_time->tm_year + 1900, // 年要加1900 17 | tm_time->tm_mon + 1, // 月要加1 18 | tm_time->tm_mday, tm_time->tm_hour, tm_time->tm_min, 19 | tm_time->tm_sec); 20 | 21 | return buf; 22 | } 23 | 24 | // 测试 25 | // int main() { 26 | // std::cout << Timestamp::now().toString() << std::endl; 27 | // return 0; 28 | // } 29 | -------------------------------------------------------------------------------- /luce/common/Timestamp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class Timestamp { 7 | public: 8 | Timestamp(); 9 | explicit Timestamp(int64_t microSecondsSinceEpoch); 10 | 11 | static Timestamp now(); 12 | 13 | std::string toString() const; 14 | 15 | private: 16 | int64_t microSecondsSinceEpoch_; 17 | }; 18 | -------------------------------------------------------------------------------- /luce/common/blocking_queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | template 9 | class BlockingQueue { 10 | public: 11 | void push(T&& value) { 12 | { 13 | std::scoped_lock lock(mtx_); 14 | queue_.push(std::move(value)); 15 | } 16 | cond_.notify_one(); 17 | } 18 | 19 | bool try_push(const T& value) { 20 | { 21 | std::unique_lock lock(mtx_, std::try_to_lock); 22 | if (!lock) { 23 | return false; 24 | } 25 | queue_.push(value); 26 | } 27 | cond_.notify_one(); 28 | return true; 29 | } 30 | 31 | bool pop(T* item) { 32 | std::unique_lock lock(mtx_); 33 | cond_.wait(lock, [this]() { return !this->queue_.empty() || this->stop_; }); 34 | if (queue_.empty()) { 35 | return false; 36 | } 37 | *item = std::move(queue_.front()); 38 | queue_.pop(); 39 | return true; 40 | } 41 | 42 | // non-blocking pop an item, maybe failed 43 | bool try_pop_if(T* item, bool (*predict)(T&) = nullptr) { 44 | std::unique_lock lock(mtx_, std::try_to_lock); 45 | if (!lock || queue_.empty()) { 46 | return false; 47 | } 48 | 49 | if (predict && !predict(queue_.front())) { 50 | return false; 51 | } 52 | 53 | *item = std::move(queue_.front()); 54 | queue_.pop(); 55 | return true; 56 | } 57 | 58 | std::size_t size() const { 59 | std::scoped_lock lock(mtx_); 60 | return queue_.size(); 61 | } 62 | 63 | bool empty() const { 64 | std::scoped_lock lock(mtx_); 65 | return queue_.empty(); 66 | } 67 | 68 | void stop() { 69 | { 70 | std::scoped_lock lock(mtx_); 71 | stop_ = true; 72 | } 73 | cond_.notify_all(); 74 | } 75 | 76 | private: 77 | mutable std::mutex mtx_; 78 | std::condition_variable cond_; 79 | std::queue queue_; 80 | 81 | // std::atomic stop_{false}; 82 | bool stop_{false}; 83 | }; 84 | -------------------------------------------------------------------------------- /luce/common/blocking_queue_thread_pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "luce/common/blocking_queue.h" 13 | #include "luce/common/logger.h" 14 | 15 | // 线程池 16 | class ThreadPool { 17 | public: 18 | explicit ThreadPool(size_t thread_num); 19 | ~ThreadPool(); 20 | 21 | template 22 | auto Commit(F&& f, Args&&... args) -> std::future; 23 | 24 | bool CommitById(std::function func, size_t id = -1); 25 | 26 | void Shutdown(); 27 | 28 | size_t Size() const { return threads_.size(); } 29 | 30 | size_t TaskNum() const { 31 | size_t sum = 0; 32 | for (size_t i = 0; i < thread_num_; ++i) { 33 | sum += queues_[i].size(); 34 | } 35 | return sum; 36 | } 37 | 38 | private: 39 | using TaskType = std::function; 40 | 41 | std::vector threads_; 42 | // 采用多个队列减少锁竞争 43 | std::vector> queues_; 44 | size_t thread_num_; 45 | std::atomic stop_; 46 | }; 47 | 48 | inline ThreadPool::ThreadPool(size_t thread_num) 49 | : queues_(thread_num), thread_num_(thread_num), stop_(false) { 50 | auto worker = [this](size_t id) { 51 | while (true) { 52 | TaskType task{}; 53 | if (!queues_[id].pop(&task)) { 54 | if (stop_) { 55 | return; 56 | } 57 | continue; 58 | } 59 | if (task) { 60 | task(); 61 | } 62 | } 63 | }; 64 | 65 | threads_.reserve(thread_num); 66 | 67 | for (size_t i = 0; i < thread_num; ++i) { 68 | threads_.emplace_back(worker, i); 69 | } 70 | 71 | // init srand 72 | srand(time(nullptr)); 73 | } 74 | 75 | inline ThreadPool::~ThreadPool() { 76 | if (!stop_) { 77 | Shutdown(); 78 | } 79 | } 80 | 81 | // add new work item to thead pool 82 | template 83 | inline auto ThreadPool::Commit(F&& f, Args&&... args) 84 | -> std::future { 85 | using return_type = decltype(f(args...)); 86 | 87 | auto task = std::make_shared>( 88 | std::bind(std::forward(f), std::forward(args)...)); 89 | std::future future = task->get_future(); 90 | 91 | // 随机负载均衡 92 | size_t id = rand() % thread_num_; 93 | queues_[id].push([task]() { 94 | assert(task != nullptr); 95 | (*task)(); 96 | }); 97 | 98 | return future; 99 | } 100 | 101 | inline bool ThreadPool::CommitById(std::function func, size_t id) { 102 | if (func == nullptr || stop_) { 103 | return false; 104 | } 105 | if (id < 0) { 106 | id = rand() % thread_num_; 107 | } 108 | assert(id < thread_num_); 109 | queues_[id].push(std::move(func)); 110 | return true; 111 | } 112 | 113 | inline void ThreadPool::Shutdown() { 114 | stop_ = true; 115 | for (auto& queue : queues_) { 116 | queue.stop(); 117 | } 118 | for (auto& thread : threads_) { 119 | thread.join(); 120 | } 121 | LOG_INFO("thread pool shutdown"); 122 | } 123 | 124 | // 可以增长 method: AddThread 125 | // 可选支持自动扩缩容 126 | // 支持多参数 127 | /** 128 | class ThreadPool { 129 | using TaskType = std::function; 130 | 131 | public: 132 | explicit ThreadPool(size_t init_size = 1, bool auto_grow = false, 133 | size_t max_threads = 134 | std::thread::hardware_concurrency()) : max_threads_num_(max_threads), 135 | init_threads_num_(init_size), 136 | auto_grow_(auto_grow) { 137 | AddThread(init_threads_num_); 138 | } 139 | 140 | ~ThreadPool() { 141 | if (!is_shutdown_) { 142 | Shutdown(); 143 | } 144 | } 145 | 146 | void Shutdown() { 147 | is_shutdown_ = true; 148 | // 唤醒所有线程 149 | tasks_cv_.notify_all(); 150 | for (auto &worker : workers_) { 151 | if (worker.joinable()) { 152 | worker.join(); 153 | } 154 | } 155 | } 156 | 157 | void AddThread(size_t num) { 158 | if (is_shutdown_) { 159 | return; 160 | } 161 | 162 | std::unique_lock grow_lock(grow_mtx_); 163 | if (!auto_grow_) { 164 | grow_lock.unlock(); 165 | } 166 | 167 | for (size_t i = 0; i < num && workers_.size() < max_threads_num_; ++i) { 168 | workers_.emplace_back([this] { this->work(); }); 169 | 170 | std::lock_guard lock(mtx_); 171 | free_thread_num_++; 172 | } 173 | } 174 | 175 | // 提交一个任务 176 | // 调用.get()获取返回值会等待任务执行完,获取返回值 177 | template 178 | auto Commit(F &&f, Args &&...args) -> std::future { 179 | if (is_shutdown_) { 180 | LOG_FATAL("ThreadPool is Shutdown"); 181 | } 182 | 183 | using ReturnType = decltype(f(args...)); 184 | auto task = std::make_shared>( 185 | std::bind(std::forward(f), std::forward(args)...)); 186 | auto future = task->get_future(); 187 | { 188 | // 将任务添加到任务队列 189 | std::lock_guard lock(mtx_); 190 | tasks_.emplace([task]() { (*task)(); }); 191 | } 192 | 193 | if (auto_grow_) { 194 | // 判断是否需要自动扩容 195 | if (free_thread_num_ < 1 && workers_.size() < max_threads_num_) { 196 | AddThread(1); 197 | } 198 | } 199 | 200 | // 唤醒一个线程执行 201 | tasks_cv_.notify_one(); 202 | return future; 203 | } 204 | 205 | // 空闲线程数量 206 | size_t FreeSize() { return free_thread_num_; } 207 | 208 | // 线程数量 209 | size_t Size() { 210 | std::lock_guard lock(grow_mtx_); 211 | auto sz = workers_.size(); 212 | return sz; 213 | } 214 | 215 | private: 216 | void work() { 217 | // 防止 is_shutdown_==true 时立即结束,此时任务队列可能不为空 218 | while (true) { 219 | TaskType task; 220 | { 221 | std::unique_lock lock(mtx_); 222 | tasks_cv_.wait(lock, [this]() { 223 | return this->is_shutdown_ || !this->tasks_.empty(); 224 | }); 225 | 226 | if (is_shutdown_ && tasks_.empty()) { 227 | // is_shutdown_ 并且任务为空了之后才可以结束 228 | return; 229 | } 230 | 231 | free_thread_num_--; 232 | task = std::move(tasks_.front()); 233 | tasks_.pop(); 234 | } 235 | task(); 236 | 237 | // 任务结束之后 238 | if (auto_grow_) { 239 | // 支持自动释放空闲线程,避免峰值过后大量空闲线程 240 | if (free_thread_num_ > 0 && workers_.size() > init_threads_num_) { 241 | return; 242 | } 243 | } 244 | 245 | { 246 | std::lock_guard lock(mtx_); 247 | free_thread_num_++; 248 | } 249 | } 250 | } 251 | 252 | std::vector workers_; 253 | std::queue tasks_; 254 | 255 | std::mutex mtx_; // 任务队列互斥锁 256 | std::condition_variable tasks_cv_; // 任务队列条件变量 257 | 258 | const size_t max_threads_num_; 259 | size_t init_threads_num_; 260 | std::atomic free_thread_num_; // 空闲线程数量 261 | 262 | std::atomic is_shutdown_{false}; 263 | 264 | const bool auto_grow_; // 是否支持自动扩缩容 265 | std::mutex grow_mtx_; // 线程池增长互斥锁 266 | }; 267 | **/ 268 | -------------------------------------------------------------------------------- /luce/common/count_down_latch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // CountDownLatch 是个计数器锁,用于阻塞当前线程,等待其他前程都执行完毕 8 | // 再继续执行。 9 | class CountDownLatch { 10 | public: 11 | explicit CountDownLatch(int32_t count) : count_(count) {} 12 | 13 | void Wait() { 14 | std::unique_lock lock(mtx_); 15 | 16 | while (count_ > 0) { 17 | condition_.wait(lock); 18 | } 19 | } 20 | 21 | void CountDown() { 22 | std::lock_guard lock(mtx_); 23 | 24 | --count_; 25 | if (count_ == 0) { 26 | condition_.notify_one(); 27 | } 28 | } 29 | 30 | auto GetCount() const -> int32_t { 31 | std::unique_lock lock(mtx_); 32 | return count_; 33 | } 34 | 35 | private: 36 | int32_t count_{0}; 37 | mutable std::mutex mtx_; 38 | std::condition_variable condition_; 39 | }; 40 | -------------------------------------------------------------------------------- /luce/common/file_util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | size_t FileSize(std::string_view path) { 8 | struct stat info {}; 9 | stat(path.data(), &info); 10 | return info.st_size; 11 | } 12 | -------------------------------------------------------------------------------- /luce/common/json.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "luce/common/json/JObject.h" 4 | #include "luce/common/json/Parser.h" 5 | -------------------------------------------------------------------------------- /luce/common/json/JObject.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/common/json/JObject.h" 2 | #include 3 | 4 | namespace tinyjson { 5 | using std::get_if; 6 | 7 | void* JObject::Value() { 8 | switch (type_) { 9 | case T_NULL: 10 | return get_if(&value_); 11 | case T_BOOL: 12 | return get_if(&value_); 13 | case T_INT: 14 | return get_if(&value_); 15 | case T_DOUBLE: 16 | return get_if(&value_); 17 | case T_LIST: 18 | return get_if(&value_); 19 | case T_DICT: 20 | return get_if(&value_); 21 | case T_STR: 22 | return get_if(&value_); 23 | default: 24 | return nullptr; 25 | } 26 | } 27 | 28 | // 用于简化指针类型转换的宏 29 | #define GET_VALUE(type, value) *((type*)(value)) 30 | 31 | string JObject::ToString() { 32 | void* value = this->Value(); 33 | std::ostringstream os; 34 | 35 | switch (type_) { 36 | case T_NULL: 37 | os << "null"; 38 | break; 39 | case T_BOOL: 40 | if (GET_VALUE(bool_t, value)) { 41 | os << "true"; 42 | } else { 43 | os << "false"; 44 | } 45 | break; 46 | case T_INT: 47 | os << GET_VALUE(int_t, value); 48 | break; 49 | case T_DOUBLE: 50 | os << GET_VALUE(double_t, value); 51 | break; 52 | case T_STR: 53 | os << '\"' << GET_VALUE(str_t, value) << '\"'; 54 | break; 55 | case T_LIST: { 56 | list_t& list = GET_VALUE(list_t, value); 57 | os << '['; 58 | for (auto i = 0; i < list.size(); ++i) { 59 | if (i != list.size() - 1) { 60 | os << ((list[i]).ToString()); 61 | os << ','; 62 | } else { 63 | os << ((list[i]).ToString()); 64 | } 65 | } 66 | os << ']'; 67 | break; 68 | } 69 | case T_DICT: { 70 | dict_t& dict = GET_VALUE(dict_t, value); 71 | os << '{'; 72 | for (auto it = dict.begin(); it != dict.end(); ++it) { 73 | if (it != dict.begin()) { 74 | os << ','; 75 | } 76 | os << '\"' << it->first << "\":" << it->second.ToString(); 77 | } 78 | os << '}'; 79 | break; 80 | } 81 | default: 82 | return ""; 83 | } 84 | return os.str(); 85 | } 86 | 87 | } // namespace tinyjson 88 | -------------------------------------------------------------------------------- /luce/common/json/JObject.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace tinyjson { 12 | 13 | using std::get_if; 14 | using std::map; 15 | using std::string; 16 | using std::string_view; 17 | using std::variant; 18 | using std::vector; 19 | 20 | enum TYPE { T_NULL, T_BOOL, T_INT, T_DOUBLE, T_STR, T_LIST, T_DICT }; 21 | 22 | class JObject; 23 | 24 | using null_t = string; 25 | using int_t = int32_t; 26 | using bool_t = bool; 27 | using double_t = double; 28 | using str_t = string; 29 | using list_t = vector; 30 | using dict_t = map; 31 | 32 | #define IS_TYPE(typea, typeb) std::is_same::value 33 | 34 | template 35 | constexpr bool IsBasicType() { 36 | return static_cast(IS_TYPE(T, str_t) || IS_TYPE(T, bool_t) || 37 | IS_TYPE(T, double_t) || IS_TYPE(T, int_t)); 38 | } 39 | 40 | class JObject { 41 | public: 42 | using value_t = variant; 43 | JObject() { 44 | type_ = T_NULL; 45 | value_ = "null"; 46 | } 47 | 48 | explicit JObject(int_t value) { Int(value); } 49 | 50 | explicit JObject(bool_t value) { Bool(value); } 51 | 52 | explicit JObject(double_t value) { Double(value); } 53 | 54 | explicit JObject(const str_t& value) { Str(value); } 55 | 56 | explicit JObject(list_t value) { List(std::move(value)); } 57 | 58 | explicit JObject(dict_t value) { Dict(std::move(value)); } 59 | 60 | void Null() { 61 | type_ = T_NULL; 62 | value_ = "null"; 63 | } 64 | 65 | void Int(int_t value) { 66 | type_ = T_INT; 67 | value_ = value; 68 | } 69 | 70 | void Bool(bool_t value) { 71 | type_ = T_BOOL; 72 | value_ = value; 73 | } 74 | 75 | void Double(double_t value) { 76 | type_ = T_DOUBLE; 77 | value_ = value; 78 | } 79 | 80 | void Str(string_view value) { 81 | type_ = T_STR; 82 | value_ = string(value); 83 | } 84 | 85 | void List(list_t value) { 86 | type_ = T_LIST; 87 | value_ = std::move(value); 88 | } 89 | 90 | void Dict(dict_t value) { 91 | type_ = T_DICT; 92 | value_ = std::move(value); 93 | } 94 | 95 | string ToString(); 96 | 97 | JObject& operator[](const string& key) { 98 | if (type_ == T_DICT) { 99 | auto& dict = Value(); 100 | return dict[key]; 101 | } 102 | throw std::logic_error("not dict type! JObject::operator[]"); 103 | } 104 | 105 | void push_back(JObject item) { 106 | if (type_ == T_LIST) { 107 | auto& list = Value(); 108 | list.push_back(std::move(item)); 109 | return; 110 | } 111 | throw std::logic_error("not list type! JObject::push_back()"); 112 | } 113 | 114 | void pop_back() { 115 | if (type_ == T_LIST) { 116 | auto& list = Value(); 117 | list.pop_back(); 118 | return; 119 | } 120 | throw std::logic_error("not list type! JObject::push_back()"); 121 | } 122 | 123 | #define THROW_GET_ERROR(type) \ 124 | throw std::logic_error("type error in get " #type " value!") 125 | 126 | template 127 | V& Value() { 128 | // 添加安全检查 129 | if constexpr (IS_TYPE(V, str_t)) { 130 | if (type_ != T_STR) { 131 | THROW_GET_ERROR(string); 132 | } 133 | } else if constexpr (IS_TYPE(V, int_t)) { 134 | if (type_ != T_INT) { 135 | THROW_GET_ERROR(INT); 136 | } 137 | } else if constexpr (IS_TYPE(V, bool_t)) { 138 | if (type_ != T_BOOL) { 139 | THROW_GET_ERROR(BOOL); 140 | } 141 | } else if constexpr (IS_TYPE(V, double_t)) { 142 | if (type_ != T_DOUBLE) { 143 | THROW_GET_ERROR(DOUBLE); 144 | } 145 | } else if constexpr (IS_TYPE(V, list_t)) { 146 | if (type_ != T_LIST) { 147 | THROW_GET_ERROR(LIST); 148 | } 149 | } else if constexpr (IS_TYPE(V, dict_t)) { 150 | if (type_ != T_DICT) { 151 | THROW_GET_ERROR(DICT); 152 | } 153 | } 154 | 155 | void* v = Value(); 156 | if (v == nullptr) { 157 | throw std::logic_error("unknown type in JObject::Value()"); 158 | } 159 | return *static_cast(v); 160 | } 161 | 162 | TYPE Type() { return type_; } 163 | 164 | private: 165 | void* Value(); 166 | 167 | TYPE type_; 168 | value_t value_; 169 | }; 170 | 171 | } // namespace tinyjson 172 | -------------------------------------------------------------------------------- /luce/common/json/Parser.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/common/json/Parser.h" 2 | #include 3 | #include "luce/common/json/JObject.h" 4 | 5 | namespace tinyjson { 6 | 7 | JObject Parser::FromString(string_view content) { 8 | static Parser instance; 9 | instance.Init(content); 10 | return instance.Parse(); 11 | } 12 | 13 | JObject Parser::Parse() { 14 | char token = GetNextToken(); 15 | if (token == 'n') { 16 | return ParseNull(); 17 | } 18 | if (token == 't' || token == 'f') { 19 | return ParseBool(); 20 | } 21 | if (token == '-' || (std::isdigit(token) != 0)) { 22 | return ParseNumber(); 23 | } 24 | if (token == '\"') { 25 | return ParseString(); 26 | } 27 | if (token == '[') { 28 | return ParseList(); 29 | } 30 | if (token == '{') { 31 | return ParseDict(); 32 | } 33 | 34 | throw std::logic_error("Unexpected character in parse json"); 35 | } 36 | 37 | char Parser::GetNextToken() { 38 | while (std::isspace(str_[idx_]) != 0) { 39 | ++idx_; 40 | } 41 | 42 | if (idx_ >= str_.size()) { 43 | throw std::logic_error("Unexpected character in parse json"); 44 | } 45 | // 如果是注释,需要跳过 46 | SkipComment(); 47 | return str_[idx_]; 48 | } 49 | 50 | JObject Parser::ParseNull() { 51 | if (str_.compare(idx_, 4, "null") == 0) { 52 | idx_ += 4; 53 | return {}; 54 | } 55 | throw std::logic_error("parse null error"); 56 | } 57 | 58 | JObject Parser::ParseBool() { 59 | if (str_.compare(idx_, 4, "true") == 0) { 60 | idx_ += 4; 61 | return JObject{true}; 62 | } 63 | if (str_.compare(idx_, 5, "false") == 0) { 64 | idx_ += 5; 65 | return JObject{false}; 66 | } 67 | throw std::logic_error("parse bool error"); 68 | } 69 | 70 | JObject Parser::ParseNumber() { 71 | // 记录起始下标 72 | auto begin = idx_; 73 | // 处理整数部分 74 | if (isdigit(str_[idx_]) != 0) { 75 | while (isdigit(str_[idx_]) != 0) { 76 | ++idx_; 77 | } 78 | } else { 79 | throw std::logic_error("invalid character in number"); 80 | } 81 | // 判断有没有小数部分,没有就直接返回 82 | if (str_[idx_] != '.') { 83 | return JObject{static_cast(strtol(str_.c_str() + begin, nullptr, 10))}; 84 | } 85 | // 处理小数部分 86 | if (str_[idx_] == '.') { 87 | idx_++; 88 | if (isdigit(str_[idx_]) == 0) { 89 | throw std::logic_error( 90 | "at least one digit required in parse float part!"); 91 | } 92 | while (isdigit(str_[idx_]) != 0) { 93 | ++idx_; 94 | } 95 | } 96 | return JObject{strtof64(str_.c_str() + begin, nullptr)}; 97 | } 98 | 99 | JObject Parser::ParseString() { 100 | auto pre_pos = ++idx_; 101 | auto pos = str_.find('"', idx_); 102 | if (pos != string::npos) { 103 | // 解析还没有结束,判断是否是转义符号 104 | while (true) { 105 | if (str_[pos - 1] != '\\') { // 如果不是转义则结束解析 106 | break; 107 | } 108 | // 如果是转义字符,则判断是否已经被抵消,抵消完则跳出,否则继续寻找下个字符串结束符 109 | if (IsEscConsume(pos - 1)) { 110 | break; 111 | } 112 | 113 | pos = str_.find('"', pos + 1); 114 | if (pos == string::npos) { 115 | throw std::logic_error(R"(expected left '"' in parse string)"); 116 | } 117 | } 118 | idx_ = pos + 1; 119 | return JObject{str_.substr(pre_pos, pos - pre_pos)}; 120 | } 121 | throw std::logic_error("parse string error"); 122 | } 123 | 124 | JObject Parser::ParseList() { 125 | // 得到list类型的JObject 126 | JObject arr((list_t())); 127 | ++idx_; 128 | 129 | char ch = GetNextToken(); 130 | if (ch == ']') { 131 | ++idx_; 132 | return arr; 133 | } 134 | 135 | while (true) { 136 | arr.push_back(Parse()); 137 | ch = GetNextToken(); 138 | if (ch == ']') { 139 | ++idx_; 140 | break; 141 | } 142 | // 如果不是逗号 143 | if (ch != ',') { 144 | throw std::logic_error("expected ',' in parse list"); 145 | } 146 | // 跳过逗号 147 | ++idx_; 148 | } 149 | 150 | return arr; 151 | } 152 | 153 | JObject Parser::ParseDict() { 154 | // 得到dict类型的JObject 155 | JObject dict((dict_t())); 156 | ++idx_; 157 | char ch = GetNextToken(); 158 | if (ch == '}') { 159 | ++idx_; 160 | return dict; 161 | } 162 | 163 | while (true) { 164 | // 解析key 165 | string key = std::move(Parse().Value()); 166 | ch = GetNextToken(); 167 | if (ch != ':') { 168 | throw std::logic_error("expected ':' in parse dict"); 169 | } 170 | ++idx_; 171 | 172 | // 解析value 173 | dict[key] = Parse(); 174 | ch = GetNextToken(); 175 | if (ch == '}') { 176 | ++idx_; 177 | break; // 解析完成 178 | } 179 | // 没有结束,要用逗号分隔 180 | if (ch != ',') { 181 | throw std::logic_error("expected ':' in parse dict"); 182 | } 183 | // 跳过逗号 184 | ++idx_; 185 | } 186 | 187 | return dict; 188 | } 189 | 190 | void Parser::SkipComment() { 191 | // 跳过注释,标准json里是没有的,但VSCode的json会有注释 192 | if (str_.compare(idx_, 2, R"(//)") == 0) { 193 | while (true) { 194 | auto next_pos = str_.find('\n', idx_); 195 | if (next_pos == str_.size()) { 196 | throw std::logic_error("invalid comment area!"); 197 | } 198 | // 查看下一行是否还是注释 199 | idx_ = next_pos + 1; 200 | while (isspace(str_[idx_]) != 0) { 201 | ++idx_; 202 | } 203 | if (str_.compare(idx_, 2, R"(//)") != 0) { 204 | // 注释结束了 205 | break; 206 | } 207 | } 208 | } 209 | } 210 | 211 | void Parser::Init(string_view src) { 212 | str_ = src; 213 | idx_ = 0; 214 | TrimRight(); // 去除右侧多余空格 215 | } 216 | 217 | void Parser::TrimRight() { 218 | // 去除尾部空字符,方便最后的结束检查 219 | str_.erase(std::find_if(str_.rbegin(), str_.rend(), 220 | [](char ch) { return std::isspace(ch) == 0; }) 221 | .base(), 222 | str_.end()); 223 | } 224 | 225 | bool Parser::IsEscConsume(size_t pos) { 226 | size_t end_pos = pos; 227 | while (str_[pos] == '\\') { 228 | --pos; 229 | } 230 | auto cnt = end_pos - pos; 231 | // 如果'\'的个数为偶数,则成功抵消,如果为奇数,则未抵消 232 | return cnt % 2 == 0; 233 | } 234 | } // namespace tinyjson 235 | -------------------------------------------------------------------------------- /luce/common/json/Parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "luce/common/json/JObject.h" 8 | 9 | namespace tinyjson { 10 | 11 | using std::string; 12 | using std::string_view; 13 | using std::stringstream; 14 | 15 | #define FUNC_TO_NAME _to_json 16 | #define FUNC_FROM_NAME _from_json 17 | 18 | #define START_TO_JSON void FUNC_TO_NAME(tinyjson::JObject& obj) const { 19 | #define to(key) obj[key] 20 | // push 一个自定义类型成员 21 | #define to_struct(key, struct_member) \ 22 | do { \ 23 | tinyjson::JObject tmp((tinyjson::dict_t())); \ 24 | (struct_member).FUNC_TO_NAME(tmp); \ 25 | obj[key] = tmp; \ 26 | } while (0) 27 | #define END_TO_JSON } 28 | 29 | #define START_FROM_JSON void FUNC_FROM_NAME(tinyjson::JObject& obj) { 30 | #define from(key, type) obj[key].Value() 31 | #define from_struct(key, struct_member) struct_member.FUNC_FROM_NAME(obj[key]) 32 | #define END_FROM_JSON } 33 | 34 | class Parser { 35 | public: 36 | Parser() = default; 37 | 38 | static JObject FromString(string_view content); 39 | 40 | template 41 | static string ToJSON(const T& src) { 42 | // 如果是基本类型 43 | if constexpr (IS_TYPE(T, int_t) || IS_TYPE(T, bool_t) || 44 | IS_TYPE(T, double_t) || IS_TYPE(T, str_t)) { 45 | JObject object(src); 46 | return object.ToString(); 47 | } 48 | // 如果是自定义类型,调用方法完成dict的赋值,然后to_string即可 49 | JObject obj((dict_t())); 50 | src.FUNC_TO_NAME(obj); 51 | return obj.ToString(); 52 | } 53 | 54 | template 55 | static T FromJson(string_view src) { 56 | JObject obj = FromString(src); 57 | // 如果是基本类型 58 | if constexpr (IsBasicType()) { 59 | return obj.template Value(); // 疑问:加不加template有区别吗?? 60 | } 61 | 62 | if (obj.Type() != T_DICT) { 63 | throw std::logic_error("not dict type fromjson"); 64 | } 65 | T ret; 66 | ret.FUNC_FROM_NAME(obj); 67 | return ret; 68 | } 69 | 70 | void Init(string_view src); 71 | char GetNextToken(); 72 | JObject Parse(); 73 | JObject ParseNull(); 74 | JObject ParseBool(); 75 | JObject ParseNumber(); 76 | JObject ParseString(); 77 | JObject ParseList(); 78 | JObject ParseDict(); 79 | 80 | private: 81 | string str_; 82 | size_t idx_{}; 83 | 84 | void SkipComment(); 85 | 86 | void TrimRight(); 87 | 88 | bool IsEscConsume(size_t pos); 89 | }; 90 | 91 | } // namespace tinyjson 92 | -------------------------------------------------------------------------------- /luce/common/logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "luce/common/noncopyable.h" 8 | 9 | #ifdef USE_ORIGINAL 10 | #define LOG_INFO(LogmsgFormat, ...) \ 11 | do { \ 12 | Logger &logger = Logger::instance(); \ 13 | logger.setLogLevel(INFO); \ 14 | char buf[1024] = {0}; \ 15 | snprintf(buf, 1024, LogmsgFormat, ##__VA_ARGS__); \ 16 | logger.log(buf); \ 17 | } while (0) 18 | 19 | #define LOG_ERROR(LogmsgFormat, ...) \ 20 | do { \ 21 | Logger &logger = Logger::instance(); \ 22 | logger.setLogLevel(ERROR); \ 23 | char buf[1024] = {0}; \ 24 | snprintf(buf, 1024, LogmsgFormat, ##__VA_ARGS__); \ 25 | logger.log(buf); \ 26 | } while (0) 27 | 28 | #define LOG_FATAL(LogmsgFormat, ...) \ 29 | do { \ 30 | Logger &logger = Logger::instance(); \ 31 | logger.setLogLevel(FATAL); \ 32 | char buf[1024] = {0}; \ 33 | snprintf(buf, 1024, LogmsgFormat, ##__VA_ARGS__); \ 34 | logger.log(buf); \ 35 | exit(-1); \ 36 | } while (0) 37 | 38 | #ifdef LUCEDEBUG 39 | #define LOG_DEBUG(LogmsgFormat, ...) \ 40 | do { \ 41 | Logger &logger = Logger::instance(); \ 42 | logger.setLogLevel(DEBUG); \ 43 | char buf[1024] = {0}; \ 44 | snprintf(buf, 1024, LogmsgFormat, ##__VA_ARGS__); \ 45 | logger.log(buf); \ 46 | } while (0) 47 | #else 48 | #define LOG_DEBUG(LogmsgFormat, ...) 49 | #endif 50 | 51 | // 定义日志的级别 INFO ERROR FATAL DEBUG 52 | enum LogLevel { 53 | INFO, // 普通信息 54 | ERROR, // 错误信息 55 | FATAL, // core信息 56 | DEBUG, // 调试信息 57 | }; 58 | 59 | // 输出一个日志类(单例模式) 60 | class Logger : noncopyable { 61 | public: 62 | // 获取日志唯一的实例对象 63 | static Logger &instance(); 64 | 65 | // 设置日志级别 66 | void setLogLevel(int Level); 67 | 68 | // 写日志的接口 69 | void log(const std::string &msg); 70 | 71 | private: 72 | Logger() = default; 73 | 74 | int logLevel_; 75 | }; 76 | #else 77 | 78 | #include 79 | 80 | #define LOG_INFO(LogmsgFormat, ...) \ 81 | do { \ 82 | spdlog::info(LogmsgFormat, ##__VA_ARGS__); \ 83 | } while (0) 84 | 85 | #define LOG_DEBUG(LogmsgFormat, ...) \ 86 | do { \ 87 | spdlog::debug(LogmsgFormat, ##__VA_ARGS__); \ 88 | } while (0) 89 | 90 | #define LOG_ERROR(LogmsgFormat, ...) \ 91 | do { \ 92 | spdlog::error(LogmsgFormat, ##__VA_ARGS__); \ 93 | } while (0) 94 | 95 | #define LOG_FATAL(LogmsgFormat, ...) \ 96 | do { \ 97 | spdlog::warn(LogmsgFormat, ##__VA_ARGS__); \ 98 | exit(-1); \ 99 | } while (0) 100 | 101 | #define LOG_WARN(LogmsgFormat, ...) \ 102 | do { \ 103 | spdlog::warn(LogmsgFormat, ##__VA_ARGS__); \ 104 | } while (0) 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /luce/common/noncopyable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * noncopyable 被继承以后,派生类对象可以正常地构造和析构,但 5 | * 派生类对象无法进行拷贝构造和析构 6 | */ 7 | class noncopyable { 8 | public: 9 | noncopyable(const noncopyable&) = delete; 10 | noncopyable& operator=(const noncopyable&) = delete; 11 | 12 | protected: 13 | noncopyable() = default; 14 | ~noncopyable() = default; 15 | }; 16 | -------------------------------------------------------------------------------- /luce/common/singleton.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | template 7 | class Singleton { 8 | public: 9 | static T* GetInstance() { 10 | static T instance; 11 | return &instance; 12 | } 13 | 14 | Singleton(T&&) = delete; 15 | Singleton(const T&) = delete; 16 | Singleton&& operator=(const T&&) = delete; 17 | Singleton& operator=(const T&) = delete; 18 | 19 | protected: 20 | Singleton() = default; 21 | virtual ~Singleton() = default; 22 | }; 23 | 24 | /** 25 | template 26 | class Singleton { 27 | public: 28 | Singleton() = delete; 29 | virtual ~Singleton() = delete; 30 | 31 | Singleton(const Singleton &) = delete; 32 | Singleton &operator=(const Singleton &) = delete; 33 | 34 | template 35 | static T *instance(Args &&...args) { 36 | if (instance_ == nullptr) { 37 | instance_ = new T(std::forward(args)...); 38 | } 39 | return instance_; 40 | } 41 | 42 | static T *GetInstance() { 43 | if (instance_ == nullptr) { 44 | exit(-1); 45 | } 46 | return instance_; 47 | } 48 | 49 | static void DestroyInstance() { 50 | if (instance_) { 51 | delete instance_; 52 | instance_ = nullptr; 53 | } 54 | } 55 | 56 | private: 57 | static T *instance_; 58 | }; 59 | **/ 60 | -------------------------------------------------------------------------------- /luce/common/string_util.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/common/string_util.h" 2 | 3 | namespace String { 4 | 5 | template 6 | void JoinImpl(const T& parts, std::string_view delim, std::string* result) { 7 | size_t size = 0; 8 | for (auto&& item : parts) { 9 | size += item.size() + delim.size(); 10 | } 11 | result->clear(); 12 | if (size == 0) { 13 | return; 14 | } 15 | size -= delim.size(); // 去除最后一个delim 16 | result->reserve(size); 17 | 18 | for (auto it = parts.begin(); it != parts.end(); ++it) { 19 | if (it != parts.begin()) { 20 | result->append(delim.begin(), delim.end()); 21 | } 22 | result->append(it->begin(), it->end()); 23 | } 24 | } 25 | 26 | std::string Join(const std::vector& parts, 27 | std::string_view delim) { 28 | std::string result; 29 | JoinImpl(parts, delim, &result); 30 | return result; 31 | } 32 | 33 | std::string Join(const std::vector& parts, 34 | std::string_view delim) { 35 | std::string result; 36 | JoinImpl(parts, delim, &result); 37 | return result; 38 | } 39 | 40 | std::string Join(const std::initializer_list& parts, 41 | std::string_view delim) { 42 | std::string result; 43 | JoinImpl(parts, delim, &result); 44 | return result; 45 | } 46 | 47 | bool StartsWith(std::string_view str, std::string_view prefix) { 48 | return str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix; 49 | } 50 | 51 | bool EndsWith(std::string_view str, std::string_view suffix) { 52 | return str.size() >= suffix.size() && 53 | str.substr(str.size() - suffix.size()) == suffix; 54 | } 55 | 56 | void Replace(std::string& target, std::string_view from, std::string_view to, 57 | std::size_t count) { 58 | auto p = target.find(from); 59 | while (p != std::string::npos && ((count--) != 0U)) { 60 | target.replace(p, from.size(), to); 61 | p = target.find(from, p + to.size()); 62 | } 63 | } 64 | 65 | std::string Replace(std::string_view target, std::string_view from, 66 | std::string_view to, std::size_t count) { 67 | std::string tmp(target); 68 | Replace(tmp, from, to, count); 69 | return tmp; 70 | } 71 | 72 | std::string_view Trim(std::string_view str) { 73 | std::size_t start = 0; 74 | std::size_t end = str.size(); 75 | if (str.empty()) { 76 | return {}; 77 | } 78 | if (str.front() == ' ') { 79 | start = str.find_first_not_of(' '); 80 | } 81 | if (start == std::string_view::npos) { 82 | return {}; 83 | } 84 | if (str.back() == ' ') { 85 | end = str.find_last_not_of(' '); 86 | } 87 | return str.substr(start, end - start + 1); 88 | } 89 | 90 | std::vector Split(std::string_view str, 91 | std::string_view delim, bool keep_empty) { 92 | std::vector result; 93 | if (str.empty()) { 94 | return result; 95 | } 96 | auto current = str; 97 | while (true) { 98 | auto pos = current.find(delim); 99 | if (pos != 0 || keep_empty) { 100 | result.emplace_back(current.substr(0, pos)); 101 | } 102 | if (pos == std::string_view::npos) { 103 | break; 104 | } 105 | current = current.substr(pos + delim.size()); 106 | if (current.empty()) { 107 | if (keep_empty) { 108 | result.emplace_back(""); 109 | } 110 | break; 111 | } 112 | } 113 | return result; 114 | } 115 | 116 | std::vector Split(std::string_view str, char delim, 117 | bool keep_empty) { 118 | return Split(str, std::string_view(&delim, 1), keep_empty); 119 | } 120 | 121 | bool Contains(std::string_view str, std::string_view target) { 122 | if (str == target) { 123 | return true; 124 | } 125 | if (str.empty()) { 126 | return false; 127 | } 128 | if (target.empty()) { 129 | return true; 130 | } 131 | auto pos = str.find(target); 132 | return pos != std::string_view::npos; 133 | } 134 | 135 | } // namespace String 136 | -------------------------------------------------------------------------------- /luce/common/string_util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // learn from https://github.com/Tencent/flare/blob/master/flare/base/string.cc 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | namespace String { 16 | 17 | std::string Join(const std::vector& parts, 18 | std::string_view delim); 19 | 20 | std::string Join(const std::vector& parts, std::string_view delim); 21 | 22 | std::string Join(const std::initializer_list& parts, 23 | std::string_view delim); 24 | 25 | bool StartsWith(std::string_view str, std::string_view prefix); 26 | 27 | bool EndsWith(std::string_view str, std::string_view suffix); 28 | 29 | void Replace(std::string& target, std::string_view from, std::string_view to, 30 | std::size_t count); 31 | 32 | std::string Replace(std::string_view target, std::string_view from, 33 | std::string_view to, std::size_t count); 34 | 35 | std::string_view Trim(std::string_view str); 36 | 37 | std::vector Split(std::string_view str, 38 | std::string_view delim, 39 | bool keep_empty = true); 40 | 41 | std::vector Split(std::string_view str, char delim, 42 | bool keep_empty = true); 43 | 44 | bool Contains(std::string_view str, std::string_view target); 45 | 46 | // std::format in linux could not use now 47 | // use fmtlib instead 48 | template 49 | std::string Format(std::string_view real_time_fmt, Args&&... args) { 50 | return fmt::vformat(real_time_fmt, fmt::make_format_args(args...)); 51 | } 52 | 53 | } // namespace String 54 | -------------------------------------------------------------------------------- /luce/common/thread_pool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "luce/common/logger.h" 12 | 13 | // 线程池 14 | class ThreadPool { 15 | public: 16 | explicit ThreadPool(size_t thread_num); 17 | ~ThreadPool(); 18 | 19 | template 20 | auto Commit(F&& f, Args&&... args) -> std::future; 21 | 22 | void Shutdown(); 23 | 24 | size_t Size() const { return workers_.size(); } 25 | 26 | private: 27 | std::vector workers_; 28 | std::queue> tasks_; 29 | 30 | // sync 31 | std::mutex queue_mtx_; 32 | std::condition_variable condition; 33 | std::condition_variable condition_producers_; 34 | std::atomic stop_; 35 | }; 36 | 37 | inline ThreadPool::ThreadPool(size_t thread_num) : stop_(false) { 38 | for (size_t i = 0; i < thread_num; ++i) { 39 | workers_.emplace_back([this] { 40 | for (;;) { 41 | std::packaged_task task; 42 | { 43 | std::unique_lock lock(queue_mtx_); 44 | this->condition.wait( 45 | lock, [this] { return this->stop_ || !this->tasks_.empty(); }); 46 | if (this->stop_) { 47 | return; 48 | } 49 | if (this->tasks_.empty()) { 50 | continue; 51 | } 52 | task = std::move(this->tasks_.front()); 53 | this->tasks_.pop(); 54 | if (this->tasks_.empty()) { 55 | // notify that the queue is empty 56 | condition_producers_.notify_one(); 57 | } 58 | } 59 | task(); 60 | } 61 | }); 62 | } 63 | } 64 | 65 | inline ThreadPool::~ThreadPool() { 66 | if (!stop_) { 67 | Shutdown(); 68 | } 69 | } 70 | 71 | // add new work item to thead pool 72 | template 73 | inline auto ThreadPool::Commit(F&& f, Args&&... args) 74 | -> std::future { 75 | using return_type = decltype(f(args...)); 76 | 77 | std::packaged_task task( 78 | std::bind(std::forward(f), std::forward(args)...)); 79 | std::future future = task.get_future(); 80 | { 81 | std::unique_lock lock(queue_mtx_); 82 | if (!stop_) { 83 | tasks_.emplace(std::move(task)); 84 | } 85 | } 86 | condition.notify_one(); 87 | return future; 88 | } 89 | 90 | inline void ThreadPool::Shutdown() { 91 | LOG_INFO("thread pool shutdown"); 92 | { 93 | std::unique_lock lock(queue_mtx_); 94 | condition_producers_.wait(lock, [this] { return this->tasks_.empty(); }); 95 | stop_ = true; 96 | } 97 | condition.notify_all(); 98 | for (std::thread& worker : workers_) { 99 | worker.join(); 100 | } 101 | } 102 | 103 | // 可以增长 method: AddThread 104 | // 可选支持自动扩缩容 105 | // 支持多参数 106 | /** 107 | class ThreadPool { 108 | using TaskType = std::function; 109 | 110 | public: 111 | explicit ThreadPool(size_t init_size = 1, bool auto_grow = false, 112 | size_t max_threads = 113 | std::thread::hardware_concurrency()) : max_threads_num_(max_threads), 114 | init_threads_num_(init_size), 115 | auto_grow_(auto_grow) { 116 | AddThread(init_threads_num_); 117 | } 118 | 119 | ~ThreadPool() { 120 | if (!is_shutdown_) { 121 | Shutdown(); 122 | } 123 | } 124 | 125 | void Shutdown() { 126 | is_shutdown_ = true; 127 | // 唤醒所有线程 128 | tasks_cv_.notify_all(); 129 | for (auto &worker : workers_) { 130 | if (worker.joinable()) { 131 | worker.join(); 132 | } 133 | } 134 | } 135 | 136 | void AddThread(size_t num) { 137 | if (is_shutdown_) { 138 | return; 139 | } 140 | 141 | std::unique_lock grow_lock(grow_mtx_); 142 | if (!auto_grow_) { 143 | grow_lock.unlock(); 144 | } 145 | 146 | for (size_t i = 0; i < num && workers_.size() < max_threads_num_; ++i) { 147 | workers_.emplace_back([this] { this->work(); }); 148 | 149 | std::lock_guard lock(mtx_); 150 | free_thread_num_++; 151 | } 152 | } 153 | 154 | // 提交一个任务 155 | // 调用.get()获取返回值会等待任务执行完,获取返回值 156 | template 157 | auto Commit(F &&f, Args &&...args) -> std::future { 158 | if (is_shutdown_) { 159 | LOG_FATAL("ThreadPool is Shutdown"); 160 | } 161 | 162 | using ReturnType = decltype(f(args...)); 163 | auto task = std::make_shared>( 164 | std::bind(std::forward(f), std::forward(args)...)); 165 | auto future = task->get_future(); 166 | { 167 | // 将任务添加到任务队列 168 | std::lock_guard lock(mtx_); 169 | tasks_.emplace([task]() { (*task)(); }); 170 | } 171 | 172 | if (auto_grow_) { 173 | // 判断是否需要自动扩容 174 | if (free_thread_num_ < 1 && workers_.size() < max_threads_num_) { 175 | AddThread(1); 176 | } 177 | } 178 | 179 | // 唤醒一个线程执行 180 | tasks_cv_.notify_one(); 181 | return future; 182 | } 183 | 184 | // 空闲线程数量 185 | size_t FreeSize() { return free_thread_num_; } 186 | 187 | // 线程数量 188 | size_t Size() { 189 | std::lock_guard lock(grow_mtx_); 190 | auto sz = workers_.size(); 191 | return sz; 192 | } 193 | 194 | private: 195 | void work() { 196 | // 防止 is_shutdown_==true 时立即结束,此时任务队列可能不为空 197 | while (true) { 198 | TaskType task; 199 | { 200 | std::unique_lock lock(mtx_); 201 | tasks_cv_.wait(lock, [this]() { 202 | return this->is_shutdown_ || !this->tasks_.empty(); 203 | }); 204 | 205 | if (is_shutdown_ && tasks_.empty()) { 206 | // is_shutdown_ 并且任务为空了之后才可以结束 207 | return; 208 | } 209 | 210 | free_thread_num_--; 211 | task = std::move(tasks_.front()); 212 | tasks_.pop(); 213 | } 214 | task(); 215 | 216 | // 任务结束之后 217 | if (auto_grow_) { 218 | // 支持自动释放空闲线程,避免峰值过后大量空闲线程 219 | if (free_thread_num_ > 0 && workers_.size() > init_threads_num_) { 220 | return; 221 | } 222 | } 223 | 224 | { 225 | std::lock_guard lock(mtx_); 226 | free_thread_num_++; 227 | } 228 | } 229 | } 230 | 231 | std::vector workers_; 232 | std::queue tasks_; 233 | 234 | std::mutex mtx_; // 任务队列互斥锁 235 | std::condition_variable tasks_cv_; // 任务队列条件变量 236 | 237 | const size_t max_threads_num_; 238 | size_t init_threads_num_; 239 | std::atomic free_thread_num_; // 空闲线程数量 240 | 241 | std::atomic is_shutdown_{false}; 242 | 243 | const bool auto_grow_; // 是否支持自动扩缩容 244 | std::mutex grow_mtx_; // 线程池增长互斥锁 245 | }; 246 | **/ 247 | -------------------------------------------------------------------------------- /luce/io/io_awaiter.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/io/io_awaiter.h" 2 | #include 3 | 4 | namespace net { 5 | co::Task AsyncRead(TcpConnection* conn, IOBuffer& buffer) { 6 | size_t init_read_size = buffer.size(); 7 | size_t already_read_size = 0; 8 | bool need_read = true; 9 | while (need_read) { 10 | auto res = co_await detail::ReadInnerAwaiter( 11 | conn, buffer.data() + already_read_size, init_read_size); 12 | need_read = false; 13 | if (res == 0) { 14 | // co_return false; 15 | break; 16 | } 17 | if (res < 0) { 18 | if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) { 19 | need_read = true; 20 | continue; 21 | } 22 | break; 23 | } 24 | already_read_size += res; 25 | } 26 | buffer.resize(already_read_size); 27 | co_return already_read_size; 28 | } 29 | 30 | co::Task AsyncWrite(TcpConnection* conn, const IOBuffer& buffer) { 31 | size_t total_write_size = buffer.size(); 32 | size_t already_write_size = 0; 33 | while (total_write_size != 0) { 34 | auto res = co_await detail::WriteInnerAwaiter( 35 | conn, buffer.data() + already_write_size, total_write_size); 36 | if (res == 0) { 37 | break; 38 | } 39 | if (res < 0) { 40 | if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) { 41 | continue; 42 | } 43 | break; 44 | } 45 | already_write_size += res; 46 | total_write_size -= res; 47 | } 48 | co_return already_write_size; 49 | } 50 | 51 | co::Task AsyncReadPacket(TcpConnection* conn, IOBuffer& buffer) { 52 | char head_buffer[detail::HEADER_SIZE]; 53 | co_await detail::ReadInnerAwaiter(conn, head_buffer, detail::HEADER_SIZE); 54 | uint32_t total_read_size = *reinterpret_cast(head_buffer); 55 | buffer.resize(total_read_size); 56 | uint32_t already_read_size = 0; 57 | while (total_read_size != 0) { 58 | auto res = co_await detail::ReadInnerAwaiter( 59 | conn, buffer.data() + already_read_size, total_read_size); 60 | if (res == 0) { 61 | LOG_DEBUG("AsyncReadPacket, client close"); 62 | co_return false; 63 | } 64 | if (res < 0) { 65 | if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) { 66 | continue; 67 | } 68 | LOG_DEBUG("AsyncReadPacket error, errno: {}", errno); 69 | co_return false; 70 | } 71 | total_read_size -= res; 72 | already_read_size += res; 73 | } 74 | co_return true; 75 | } 76 | 77 | co::Task AsyncWritePacket(TcpConnection* conn, const IOBuffer& buffer) { 78 | uint32_t total_write_size = buffer.size(); 79 | char head_buffer[detail::HEADER_SIZE]; 80 | std::memcpy(head_buffer, reinterpret_cast(&total_write_size), 81 | detail::HEADER_SIZE); 82 | co_await detail::WriteInnerAwaiter(conn, head_buffer, detail::HEADER_SIZE); 83 | uint32_t already_write_size = 0; 84 | while (total_write_size != 0) { 85 | auto res = co_await detail::WriteInnerAwaiter( 86 | conn, buffer.data() + already_write_size, total_write_size); 87 | if (res == 0) { 88 | LOG_DEBUG("AsyncWritePacket, client close"); 89 | co_return false; 90 | } 91 | if (res < 0) { 92 | if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) { 93 | continue; 94 | } 95 | LOG_DEBUG("AsyncWritePacket error, errno: {}", errno); 96 | co_return false; 97 | } 98 | total_write_size -= res; 99 | already_write_size += res; 100 | } 101 | co_return true; 102 | } 103 | } // namespace net -------------------------------------------------------------------------------- /luce/io/io_awaiter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "luce/co/task.h" 12 | #include "luce/common/logger.h" 13 | #include "luce/net/event_manager.h" 14 | #include "luce/net/socket.h" 15 | #include "luce/net/tcp/tcp_acceptor.h" 16 | #include "luce/net/tcp/tcp_connection.h" 17 | 18 | namespace net { 19 | 20 | namespace detail { 21 | 22 | constexpr int HEADER_SIZE = 4; 23 | 24 | // example only 25 | class ReadInnerAwaiter { 26 | public: 27 | ReadInnerAwaiter(TcpConnection* conn, void* buffer, size_t len) 28 | : conn_(conn), buffer_(buffer), len_(len) {} 29 | 30 | // if IO is ready (recv_ > 0), then we should not suspend 31 | auto await_ready() -> bool { 32 | LOG_DEBUG("recv ready"); 33 | if (!conn_->IsClosed()) { 34 | recv_ = ::read(conn_->GetSocket()->GetFd(), buffer_, len_); 35 | } 36 | return recv_ >= 0; 37 | } 38 | 39 | void await_suspend(std::coroutine_handle<> handle) { 40 | LOG_DEBUG("await_suspend recv"); 41 | if (!conn_->IsClosed()) { 42 | conn_->GetEventManager().AddRecv(conn_->GetSocket(), handle); 43 | } 44 | } 45 | 46 | auto await_resume() -> ssize_t { 47 | LOG_DEBUG("recv resume"); 48 | if (recv_ < 0) { 49 | if (!conn_->IsClosed()) { 50 | conn_->GetEventManager().DelRecv(conn_->GetSocket()); 51 | recv_ = ::read(conn_->GetSocket()->GetFd(), buffer_, len_); 52 | } 53 | } 54 | return recv_; 55 | } 56 | 57 | private: 58 | TcpConnection* conn_; 59 | void* buffer_; 60 | ssize_t recv_{0}; 61 | size_t len_; 62 | }; 63 | 64 | class WriteInnerAwaiter { 65 | public: 66 | WriteInnerAwaiter(TcpConnection* conn, const void* buffer, size_t len) 67 | : conn_(conn), buffer_(buffer), len_(len) {} 68 | 69 | auto await_ready() -> bool { 70 | LOG_DEBUG("send ready"); 71 | if (!conn_->IsClosed()) { 72 | send_ = ::write(conn_->GetSocket()->GetFd(), buffer_, len_); 73 | } 74 | return send_ >= 0; 75 | } 76 | 77 | void await_suspend(std::coroutine_handle<> handle) { 78 | LOG_DEBUG("send suspend"); 79 | if (!conn_->IsClosed()) { 80 | conn_->GetEventManager().AddSend(conn_->GetSocket(), handle); 81 | } 82 | } 83 | 84 | auto await_resume() -> ssize_t { 85 | LOG_DEBUG("send resume"); 86 | if (send_ < 0) { 87 | if (!conn_->IsClosed()) { 88 | conn_->GetEventManager().DelSend(conn_->GetSocket()); 89 | send_ = ::write(conn_->GetSocket()->GetFd(), buffer_, len_); 90 | } 91 | } 92 | return send_; 93 | } 94 | 95 | private: 96 | TcpConnection* conn_; 97 | const void* buffer_; 98 | ssize_t send_{0}; 99 | size_t len_; 100 | }; 101 | 102 | } // namespace detail 103 | 104 | class AcceptAwaiter { 105 | public: 106 | explicit AcceptAwaiter(TcpAcceptor* acceptor) : acceptor_(acceptor) {} 107 | 108 | bool await_ready() { 109 | conn_fd_ = do_accept(acceptor_->GetSocket()->GetFd()); 110 | return conn_fd_ >= 0; 111 | } 112 | 113 | void await_suspend(std::coroutine_handle<> handle) { 114 | LOG_DEBUG("await_suspend accept, handle: {}", handle.address()); 115 | acceptor_->GetEventManager().AddRecv(acceptor_->GetSocket(), handle); 116 | } 117 | 118 | int await_resume() { 119 | LOG_DEBUG("resume accept"); 120 | acceptor_->GetEventManager().DelRecv(acceptor_->GetSocket()); 121 | if (conn_fd_ < 0) { 122 | conn_fd_ = do_accept(acceptor_->GetSocket()->GetFd()); 123 | } 124 | LOG_DEBUG("accept {}", conn_fd_); 125 | return conn_fd_; 126 | } 127 | 128 | private: 129 | int do_accept(int fd) { 130 | struct sockaddr_in addr {}; 131 | socklen_t len = sizeof addr; 132 | bzero(&addr, len); 133 | // 设置为非阻塞 134 | return ::accept4(acceptor_->GetSocket()->GetFd(), (struct sockaddr*)&addr, 135 | &len, SOCK_NONBLOCK | SOCK_CLOEXEC); 136 | } 137 | 138 | TcpAcceptor* acceptor_; 139 | int conn_fd_{-1}; // the coming connection fd 140 | }; 141 | 142 | co::Task AsyncRead(TcpConnection* conn, IOBuffer& buffer); 143 | 144 | co::Task AsyncWrite(TcpConnection* conn, const IOBuffer& buffer); 145 | 146 | co::Task AsyncReadPacket(TcpConnection* conn, IOBuffer& buffer); 147 | 148 | co::Task AsyncWritePacket(TcpConnection* conn, const IOBuffer& buffer); 149 | 150 | } // namespace net 151 | -------------------------------------------------------------------------------- /luce/io/io_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/io/io_buffer.h" 2 | #include 3 | #include 4 | #include "luce/common/logger.h" 5 | 6 | namespace net { 7 | 8 | IOBuf::IOBuf(size_t size) : next(nullptr), capacity(size), length(0), head(0) { 9 | data = new char[size]; 10 | assert(data); 11 | } 12 | 13 | // 清空数据 14 | void IOBuf::Clear() { length = head = 0; } 15 | 16 | void IOBuf::Adjust() { 17 | if (head != 0) { 18 | if (length != 0) { 19 | std::memmove(data, data + head, length); 20 | } 21 | head = 0; 22 | } 23 | } 24 | 25 | void IOBuf::Copy(const IOBuf* other) { 26 | std::memcpy(data, other->data + other->head, other->length); 27 | head = 0; 28 | length = other->length; 29 | } 30 | 31 | void IOBuf::pop(size_t len) { 32 | length -= len; 33 | head += len; 34 | } 35 | 36 | BufPool::BufPool() : total_mem_(0) { 37 | // 4K的IOBuf开辟5000个,约20MB 38 | AllocPoolMem(m4K, 5000); 39 | total_mem_ += 4L * 5000; 40 | 41 | // 16K的IOBuf开辟1000个,约16MB 42 | AllocPoolMem(m16K, 1000); 43 | total_mem_ += 16L * 1000; 44 | 45 | // 64K开辟500个,约32MB 46 | AllocPoolMem(m64K, 500); 47 | total_mem_ += 64L * 500; 48 | 49 | // 256K开辟200个, 约50MB 50 | AllocPoolMem(m256K, 200); 51 | total_mem_ += 256L * 200; 52 | 53 | // 1M开辟50个,50MB 54 | AllocPoolMem(m1M, 50); 55 | total_mem_ += 1024L * 50; 56 | 57 | // 4M开辟20个,80MB 58 | AllocPoolMem(m4M, 20); 59 | total_mem_ += 4L * 1024 * 20; 60 | 61 | // 8M开辟10个, 80MB 62 | AllocPoolMem(m8M, 10); 63 | total_mem_ += 8L * 1024 * 10; 64 | } 65 | 66 | void BufPool::AllocPoolMem(MemCap mem_cap, size_t num) { 67 | IOBuf* prev; 68 | // 开辟mem_cap大小的 buf 内存池 69 | pool_[mem_cap] = new IOBuf(mem_cap); 70 | if (pool_[mem_cap] == nullptr) { 71 | // LOG_ERROR("new IOBuf error, target size: {}", mem_cap); 72 | exit(1); 73 | } 74 | 75 | prev = pool_[mem_cap]; 76 | // 开辟num个 77 | for (size_t i = 1; i < num; ++i) { 78 | prev->next = new IOBuf(mem_cap); 79 | if (prev->next == nullptr) { 80 | // LOG_ERROR("new IOBuf error, target size: {}", mem_cap); 81 | exit(1); 82 | } 83 | prev = prev->next; 84 | } 85 | } 86 | 87 | // 开辟一个IOBuf 88 | // 1. 如果上层需要N个字节大小的空间,找到与N最近的buf hash组,取出 89 | // 2. 如果该组已经没有节点可以使用,可以额外申请 90 | // 3. 总申请长度不能超过最大的限制大小 MEM_LIMIT 91 | // 4. 如果有该节点需要的内存块, 直接取出,并将该内存块从pool中摘除 92 | IOBuf* BufPool::AllocBuf(int N) { 93 | // 找到N最接近哪个hash组 94 | int key; 95 | if (N <= m4K) { 96 | key = m4K; 97 | } else if (N <= m16K) { 98 | key = m16K; 99 | } else if (N <= m64K) { 100 | key = m64K; 101 | } else if (N <= m256K) { 102 | key = m256K; 103 | } else if (N <= m1M) { 104 | key = m1M; 105 | } else if (N <= m4M) { 106 | key = m4M; 107 | } else if (N <= m8M) { 108 | key = m8M; 109 | } else { 110 | return nullptr; 111 | } 112 | 113 | // 如果该组已经没有节点,需要额外申请 114 | std::lock_guard lock(list_mtx_); 115 | if (pool_[key] == nullptr) { 116 | if (total_mem_ + key / 1024 >= MEM_LIMIT) { 117 | // 要开辟的空间超过最大限制 118 | LOG_ERROR("use too many memory!"); 119 | exit(1); 120 | } 121 | 122 | IOBuf* new_buf = new IOBuf(key); 123 | if (new_buf == nullptr) { 124 | LOG_ERROR("new buf error"); 125 | exit(1); 126 | } 127 | total_mem_ += key / 1024; 128 | return new_buf; 129 | } 130 | 131 | // 如果有,从pool_中摘除该内存块 132 | IOBuf* target = pool_[key]; 133 | pool_[key] = target->next; 134 | target->next = nullptr; 135 | return target; 136 | } 137 | 138 | // 重置一个IOBuf, 上层不再使用或者使用完成之后 139 | // 需要将该Buf放回pool中 140 | void BufPool::Revert(IOBuf* buffer) { 141 | // 每个buf的容量是固定的,它们的大小就是pool中的key 142 | auto key = buffer->capacity; 143 | // 重置IOBuf内的位置指针 144 | buffer->head = 0; 145 | buffer->length = 0; 146 | 147 | std::lock_guard lock(list_mtx_); 148 | assert(pool_.find(key) != pool_.end()); 149 | // 将buffer插回链表头部 150 | buffer->next = pool_[key]; 151 | pool_[key] = buffer; 152 | } 153 | 154 | } // namespace net 155 | -------------------------------------------------------------------------------- /luce/io/io_buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "luce/common/noncopyable.h" 8 | 9 | namespace net { 10 | 11 | // 定义一个buffer存放数据的结构 12 | class IOBuf { 13 | public: 14 | explicit IOBuf(size_t size); 15 | 16 | // 清空数据 17 | void Clear(); 18 | 19 | // 将已经处理的数据清空 20 | // 未处理的数据移动至数据首地址 21 | void Adjust(); 22 | 23 | // 将其它IOBuf的内容拷贝 24 | void Copy(const IOBuf* other); 25 | 26 | // 处理长度为len的数据,移动head和修正length 27 | void pop(size_t len); 28 | 29 | // 如果存在多个buffer,用链表连接起来 30 | IOBuf* next; 31 | // 当前buffer的容量大小 32 | size_t capacity; 33 | // 当前buffer的有效长度 34 | size_t length; 35 | // 未处理数据的头部位置索引 36 | size_t head; 37 | // 当前IOBuf保存的数据 38 | char* data; 39 | }; 40 | 41 | using Pool = std::unordered_map; 42 | 43 | // 总内存池最大限制 单位是kb 目前设为5GB 44 | constexpr size_t MEM_LIMIT = static_cast(5U * 1024 * 1024); 45 | 46 | // Buf内存池 采用单例模式 47 | class BufPool : public noncopyable { 48 | public: 49 | enum MemCap { 50 | m4K = 4096, 51 | m16K = 16384, 52 | m64K = 65536, 53 | m256K = 262144, 54 | m1M = 1048576, 55 | m4M = 4194304, 56 | m8M = 8388608 57 | }; 58 | 59 | static BufPool* GetInstance() { 60 | static BufPool buf_pool; 61 | return &buf_pool; 62 | } 63 | 64 | // 开辟一个IOBuf 65 | IOBuf* AllocBuf(int N = MemCap::m4K); 66 | 67 | // 重置一个IOBUf 68 | void Revert(IOBuf* buffer); 69 | 70 | private: 71 | BufPool(); 72 | 73 | void AllocPoolMem(MemCap mem_cap, size_t num); 74 | 75 | Pool pool_; 76 | size_t total_mem_; 77 | // 单例对象 78 | // static BufPool *instance; 79 | std::mutex list_mtx_; 80 | }; 81 | 82 | } // namespace net 83 | -------------------------------------------------------------------------------- /luce/net/event_manager.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "luce/common/logger.h" 8 | #include "luce/common/thread_pool.h" 9 | #include "luce/net/event_manager.h" 10 | #include "luce/net/socket.h" 11 | 12 | namespace net { 13 | 14 | EventManager::EventManager(std::shared_ptr work_thread_pool, 15 | size_t init_size) 16 | : epfd_(epoll_create1(EPOLL_CLOEXEC)), 17 | events_(init_size), 18 | work_thread_pool_(std::move(work_thread_pool)) { 19 | if (epfd_ == -1) { 20 | LOG_FATAL("epoll_create1 error"); 21 | } 22 | } 23 | 24 | void EventManager::Start() { 25 | while (true) { 26 | if (is_shutdown_) { 27 | break; 28 | } 29 | LOG_DEBUG("epoll_wait"); 30 | int event_num = 31 | epoll_wait(epfd_, events_.data(), static_cast(events_.size()), -1); 32 | if (event_num == -1) { 33 | if (errno == EINTR) { 34 | continue; 35 | } 36 | LOG_ERROR("epoll_wait error, info: {}", strerror(errno)); 37 | LOG_FATAL("epoll_wait error"); 38 | } 39 | 40 | if (event_num == static_cast(events_.size())) { 41 | events_.resize(events_.size() * 2); 42 | } 43 | 44 | for (int i = 0; i < event_num; ++i) { 45 | // TODO(pgj): check more situation 46 | if ((events_[i].events & EPOLLIN) != 0U) { 47 | auto handle = reinterpret_cast(events_[i].data.ptr); 48 | auto& recv_coro = handle->recv_coro; 49 | LOG_DEBUG("epoll_await resume recv handle"); 50 | if (work_thread_pool_) { 51 | work_thread_pool_->Commit([&]() { recv_coro.resume(); }); 52 | } else { 53 | recv_coro.resume(); 54 | } 55 | 56 | } else if ((events_[i].events & EPOLLOUT) != 0U) { 57 | auto handle = reinterpret_cast(events_[i].data.ptr); 58 | auto& send_coro = handle->send_coro; 59 | LOG_DEBUG("epoll_await resume send handle"); 60 | if (work_thread_pool_) { 61 | work_thread_pool_->Commit([&]() { send_coro.resume(); }); 62 | } else { 63 | send_coro.resume(); 64 | } 65 | } 66 | } 67 | } 68 | } 69 | 70 | void EventManager::AddRecv(const std::shared_ptr& socket, 71 | std::coroutine_handle<> recv_coro) { 72 | if (is_shutdown_) { 73 | return; 74 | } 75 | if (!socket->Attached()) { 76 | auto events = EPOLLIN | EPOLLET; 77 | Attach(socket, events); 78 | } 79 | socket->SetRecvCoro(recv_coro); 80 | auto new_state = socket->GetIOState() | EPOLLIN; 81 | socket->SetIOState(new_state); 82 | UpdateEvent(socket, new_state); 83 | } 84 | 85 | void EventManager::DelRecv(const std::shared_ptr& socket) { 86 | if (is_shutdown_) { 87 | return; 88 | } 89 | socket->DelRecvCoro(); 90 | auto new_state = socket->GetIOState() & ~EPOLLIN; 91 | socket->SetIOState(new_state); 92 | UpdateEvent(socket, new_state); 93 | } 94 | 95 | void EventManager::AddSend(const std::shared_ptr& socket, 96 | std::coroutine_handle<> send_coro) { 97 | if (is_shutdown_) { 98 | return; 99 | } 100 | if (!socket->Attached()) { 101 | auto events = EPOLLOUT | EPOLLET; 102 | Attach(socket, events); 103 | } 104 | socket->SetSendCoro(send_coro); 105 | auto new_state = socket->GetIOState() | EPOLLOUT; 106 | socket->SetIOState(new_state); 107 | UpdateEvent(socket, new_state); 108 | } 109 | 110 | void EventManager::DelSend(const std::shared_ptr& socket) { 111 | if (is_shutdown_) { 112 | return; 113 | } 114 | socket->DelSendCoro(); 115 | auto new_state = socket->GetIOState() & ~EPOLLOUT; 116 | socket->SetIOState(new_state); 117 | UpdateEvent(socket, new_state); 118 | } 119 | 120 | void EventManager::Attach(const std::shared_ptr& socket, 121 | unsigned int events) { 122 | struct epoll_event ev {}; 123 | socket->SetIOState(events); 124 | socket->EventAttach(); 125 | ev.events = events; 126 | ev.data.ptr = &socket->handle; 127 | if (epoll_ctl(epfd_, EPOLL_CTL_ADD, socket->GetFd(), &ev) == -1) { 128 | LOG_FATAL("epoll_ctl_add: add attach error!\n"); 129 | } 130 | } 131 | 132 | void EventManager::Detach(const std::shared_ptr& socket) { 133 | socket->EventDetach(); 134 | if (epoll_ctl(epfd_, EPOLL_CTL_DEL, socket->GetFd(), nullptr) == -1) { 135 | LOG_FATAL("epoll_ctl_del: detach error!"); 136 | } 137 | } 138 | 139 | void EventManager::UpdateEvent(const std::shared_ptr& socket, 140 | unsigned int new_state) { 141 | struct epoll_event ev {}; 142 | ev.events = new_state; 143 | ev.data.ptr = &socket->handle; 144 | if (epoll_ctl(epfd_, EPOLL_CTL_MOD, socket->GetFd(), &ev) == -1) { 145 | LOG_FATAL("epoll_ctl_mod: error"); 146 | } 147 | } 148 | 149 | } // namespace net 150 | -------------------------------------------------------------------------------- /luce/net/event_manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "luce/co/task.h" 9 | #include "luce/common/singleton.h" 10 | #include "luce/common/thread_pool.h" 11 | 12 | namespace net { 13 | 14 | class Socket; 15 | 16 | // a epoll manager for coroutine awaitable 17 | class EventManager { 18 | public: 19 | explicit EventManager(std::shared_ptr work_thread_pool, 20 | size_t init_size = 16); 21 | 22 | ~EventManager() = default; 23 | 24 | void Start(); 25 | 26 | void Attach(const std::shared_ptr& socket, 27 | unsigned int events = EPOLLIN | EPOLLET); 28 | 29 | void Detach(const std::shared_ptr& socket); 30 | 31 | void AddRecv(const std::shared_ptr& socket, 32 | std::coroutine_handle<> recv_coro); 33 | void DelRecv(const std::shared_ptr& socket); 34 | 35 | void AddSend(const std::shared_ptr& socket, 36 | std::coroutine_handle<> send_coro); 37 | void DelSend(const std::shared_ptr& socket); 38 | 39 | void Shutdown() { is_shutdown_ = true; } 40 | 41 | private: 42 | void UpdateEvent(const std::shared_ptr& socket, 43 | unsigned int new_state); 44 | int epfd_; 45 | std::atomic is_shutdown_{false}; 46 | std::vector events_; 47 | std::shared_ptr work_thread_pool_; 48 | }; 49 | 50 | } // namespace net 51 | -------------------------------------------------------------------------------- /luce/net/http/http_context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "luce/common/json.h" 4 | #include "luce/common/logger.h" 5 | #include "luce/common/string_util.h" 6 | #include "luce/net/http/http_request.h" 7 | #include "luce/net/http/http_response.h" 8 | 9 | #include 10 | #include 11 | 12 | namespace net::http { 13 | 14 | struct HttpContext { 15 | HttpContext(RequestPtr req, ResponsePtr res) 16 | : req_(std::move(req)), 17 | res_(std::move(res)), 18 | path_(req_->url_), 19 | method_(req_->method_) {} 20 | 21 | std::string QueryURL(const std::string& key) { 22 | if (req_->url_params_.contains(key)) { 23 | return req_->url_params_[key]; 24 | } 25 | return {}; 26 | } 27 | 28 | std::string QueryBody(const std::string& key) { 29 | if (req_->body_params_.contains(key)) { 30 | return req_->body_params_[key]; 31 | } 32 | return {}; 33 | } 34 | 35 | void SetHeader(std::string_view key, std::string_view value) { 36 | res_->SetHeader(key, value); 37 | } 38 | 39 | void Status(int code) { 40 | status_code_ = code; 41 | res_->SetStatusCode(code); 42 | } 43 | 44 | template 45 | void String(int code, std::string_view fmt, Args&&... args) { 46 | SetHeader("Content-Type", "text/plain"); 47 | Status(code); 48 | res_->SetBody(String::Format(fmt, std::forward(args)...)); 49 | } 50 | 51 | void JSON(int code, tinyjson::JObject& json_object) { 52 | SetHeader("Content-Type", "application/json"); 53 | Status(code); 54 | // TODO(pgj): impl json 55 | // res_->SetBody(tinyjson::Parser::ToJSON(json_object)); 56 | LOG_ERROR("ctx.JSON didn't impl"); 57 | } 58 | 59 | void HTML(int code, std::string_view html) { 60 | SetHeader("Content-Type", "text/html"); 61 | Status(code); 62 | res_->SetBody(html); 63 | } 64 | 65 | // request and response 66 | RequestPtr req_; 67 | ResponsePtr res_; 68 | 69 | // request info 70 | std::string path_; 71 | std::string method_; 72 | 73 | // response info 74 | int status_code_{500}; 75 | }; 76 | 77 | using ContextPtr = std::shared_ptr; 78 | 79 | } // namespace net::http 80 | -------------------------------------------------------------------------------- /luce/net/http/http_request.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/net/http/http_request.h" 2 | #include "luce/common/logger.h" 3 | #include "luce/common/string_util.h" 4 | 5 | namespace net::http { 6 | 7 | void HttpRequest::Parse(std::string_view data) { 8 | // 分割出请求头和请求体 9 | auto head_and_body = String::Split(data, "\r\n\r\n", true); 10 | if (head_and_body.size() != 2) { 11 | LOG_ERROR( 12 | "HTTP Request Data Error, dosen't have RequestBody, the split data " 13 | "size: {}", 14 | head_and_body.size()); 15 | } 16 | if (!head_and_body[1].empty()) { 17 | this->body_ = head_and_body[1]; 18 | } 19 | 20 | // 分割出请求行和请求头 21 | auto line_and_head = String::Split(head_and_body[0], "\r\n", false); 22 | // 解析请求行 23 | ParseLine(line_and_head[0]); 24 | // 解析请求头 25 | ParseHeaders(line_and_head); 26 | // TODO(pgj): 解析请求体 27 | ParseBody(head_and_body[1]); 28 | } 29 | 30 | // 解析请求头 31 | void HttpRequest::ParseHeaders(std::vector& headers) { 32 | for (size_t i = 1; i < headers.size(); ++i) { 33 | if (!headers[i].empty() && String::Contains(headers[i], ":")) { 34 | auto key_value = String::Split(headers[i], ':', false); 35 | if (key_value.size() == 2) { 36 | // 去除空白 37 | std::string key{key_value[0]}; 38 | std::string value{key_value[1]}; 39 | String::Trim(key); 40 | String::Trim(value); 41 | this->headers_[key] = value; 42 | } 43 | } 44 | } 45 | } 46 | 47 | // 解析请求行 48 | void HttpRequest::ParseLine(std::string_view line) { 49 | auto line_datas = String::Split(line, ' '); 50 | if (line_datas.size() != 3) { 51 | LOG_ERROR("HTTP Request Line error, it's size != 3"); 52 | } 53 | this->method_ = line_datas[0]; 54 | this->url_ = line_datas[1]; 55 | this->http_version_ = line_datas[2]; 56 | 57 | // 判断有没有参数 58 | bool has_url_params = false; 59 | auto url_datas = String::Split(line_datas[1], '&'); 60 | if (url_datas.size() == 2) { 61 | has_url_params = true; 62 | this->uri_ = std::string{url_datas[0]}; 63 | } else { 64 | this->uri_ = this->url_; 65 | } 66 | 67 | if (has_url_params) { 68 | ParseURLParams(url_datas[1]); 69 | } 70 | } 71 | 72 | // 解析请求行中的URL 73 | void HttpRequest::ParseURLParams(std::string_view url) { 74 | auto params = String::Split(url, '&'); 75 | for (auto param : params) { 76 | auto key_value = String::Split(param, '='); 77 | if (key_value.size() == 2) { 78 | // 去除空白 79 | std::string key{key_value[0]}; 80 | std::string value{key_value[1]}; 81 | String::Trim(key); 82 | String::Trim(value); 83 | this->url_params_[key] = value; 84 | } 85 | } 86 | } 87 | 88 | void HttpRequest::ParseBody(std::string_view body) { 89 | auto params = String::Split(body, '&'); 90 | for (auto param : params) { 91 | auto key_value = String::Split(param, '='); 92 | if (key_value.size() == 2) { 93 | // 去除空白 94 | std::string key{key_value[0]}; 95 | std::string value{key_value[1]}; 96 | String::Trim(key); 97 | String::Trim(value); 98 | this->body_params_[key] = value; 99 | } 100 | } 101 | } 102 | 103 | void HttpRequest::Debug() { 104 | LOG_INFO("method: {}", method_.c_str()); 105 | LOG_INFO("http version: {}", http_version_.c_str()); 106 | LOG_INFO("url: {}", url_.c_str()); 107 | LOG_INFO("uri: {}", uri_.c_str()); 108 | LOG_INFO("body: {}", body_.c_str()); 109 | } 110 | 111 | } // namespace net::http 112 | -------------------------------------------------------------------------------- /luce/net/http/http_request.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "luce/common/noncopyable.h" 11 | 12 | namespace net::http { 13 | 14 | // 单次最大请求报文大小限制 64K 15 | constexpr int MAX_REQUEST_SIZE = 1024 * 64; 16 | 17 | struct HttpRequest : noncopyable { 18 | void Parse(std::string_view data); 19 | 20 | void Debug(); 21 | 22 | std::string method_; // GET or POST 23 | std::string url_; // eg: /foo?name=pgj 24 | std::string uri_; // eg: /foo 25 | std::string http_version_; // 1.1 or 1.0 26 | std::string body_; 27 | std::unordered_map headers_; 28 | std::unordered_map url_params_; 29 | std::unordered_map body_params_; 30 | 31 | private: 32 | // 解析请求头 33 | void ParseHeaders(std::vector& headers); 34 | // 解析请求行 35 | void ParseLine(std::string_view line); 36 | // 解析请求行中的URL 37 | void ParseURLParams(std::string_view url); 38 | // 简单的解析请求体 39 | void ParseBody(std::string_view body); 40 | }; 41 | 42 | using RequestPtr = std::shared_ptr; 43 | 44 | } // namespace net::http 45 | -------------------------------------------------------------------------------- /luce/net/http/http_response.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/net/http/http_response.h" 2 | #include "luce/common/string_util.h" 3 | 4 | namespace net::http { 5 | 6 | void HttpResponse::SetBody(std::string_view body) { 7 | buffer_.clear(); 8 | buffer_ = http_version_ + " " + std::to_string(status_code_) + " " + 9 | GetCodeDescript(status_code_) + "\r\n"; 10 | for (auto& header : headers_) { 11 | buffer_ += header.first + ":" + header.second + "\r\n"; 12 | } 13 | buffer_ += "\r\n"; 14 | buffer_ += body; 15 | buffer_ += "\r\n"; 16 | } 17 | 18 | // HTTP状态码解释出处:https://www.runoob.com/http/http-status-codes.html 19 | std::string HttpResponse::GetCodeDescript(int status_code) { 20 | std::string result; 21 | switch (status_code) { 22 | case 100: { 23 | // 继续。客户端应继续其请求 24 | result = "Continue"; 25 | break; 26 | } 27 | case 101: { 28 | // 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议 29 | result = "Switching Protocols"; 30 | break; 31 | } 32 | case 200: { 33 | // 请求成功。一般用于GET与POST请求 34 | result = "OK"; 35 | break; 36 | } 37 | case 201: { 38 | // 已创建。成功请求并创建了新的资源 39 | result = "Created"; 40 | break; 41 | } 42 | case 202: { 43 | // 已接受。已经接受请求,但未处理完成 44 | result = "Accepted"; 45 | break; 46 | } 47 | case 203: { 48 | // 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本 49 | result = "Non-Authoritative Information"; 50 | break; 51 | } 52 | case 204: { 53 | // 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档 54 | result = "No Content"; 55 | break; 56 | } 57 | case 205: { 58 | // 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域 59 | result = "Reset Content"; 60 | break; 61 | } 62 | case 206: { 63 | // 部分内容。服务器成功处理了部分GET请求 64 | result = "Partial Content"; 65 | break; 66 | } 67 | case 300: { 68 | // 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择 69 | result = "Multiple Choices"; 70 | break; 71 | } 72 | case 301: { 73 | // 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。 74 | // 今后任何新的请求都应使用新的URI代替 75 | result = "Moved Permanently"; 76 | break; 77 | } 78 | case 302: { 79 | // 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI 80 | result = "Found"; 81 | break; 82 | } 83 | case 303: { 84 | // 查看其它地址。与301类似。使用GET和POST请求查看 85 | result = "See Other"; 86 | break; 87 | } 88 | case 304: { 89 | // 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。 90 | // 客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 91 | result = "Not Modified"; 92 | break; 93 | } 94 | case 305: { 95 | // 使用代理。所请求的资源必须通过代理访问 96 | result = "Use Proxy"; 97 | break; 98 | } 99 | case 306: { 100 | // 已经被废弃的HTTP状态码 101 | result = "Unused"; 102 | break; 103 | } 104 | case 307: { 105 | // 临时重定向。与302类似。使用GET请求重定向 106 | result = "Temporary Redirect"; 107 | break; 108 | } 109 | case 400: { 110 | // 客户端请求的语法错误,服务器无法理解 111 | result = "Bad Request"; 112 | break; 113 | } 114 | case 401: { 115 | // 请求要求用户的身份认证 116 | result = "Unauthorized"; 117 | break; 118 | } 119 | case 403: { 120 | // 服务器理解请求客户端的请求,但是拒绝执行此请求 121 | result = "Forbidden"; 122 | break; 123 | } 124 | case 404: { 125 | // 服务器无法根据客户端的请求找到资源(网页)。 126 | result = "Not Found"; 127 | break; 128 | } 129 | case 405: { 130 | // 客户端请求中的方法被禁止 131 | result = "Method Not Allowed"; 132 | break; 133 | } 134 | case 500: { 135 | // 服务器内部错误,无法完成请求 136 | result = "Internal Server Error"; 137 | break; 138 | } 139 | case 501: { 140 | // 服务器不支持请求的功能,无法完成请求 141 | result = "Not Implemented"; 142 | break; 143 | } 144 | case 502: { 145 | // 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应 146 | result = "Bad Gateway"; 147 | break; 148 | } 149 | case 503: { 150 | // 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中 151 | result = "Service Unavailable"; 152 | break; 153 | } 154 | case 504: { 155 | // 充当网关或代理的服务器,未及时从远端服务器获取请求 156 | result = "Gateway Time-out"; 157 | break; 158 | } 159 | case 505: { 160 | // 服务器不支持请求的HTTP协议的版本,无法完成处理 161 | result = "HTTP Version not supported"; 162 | break; 163 | } 164 | default: 165 | result = "Unknow Code or Unsupported now"; 166 | } 167 | return result; 168 | } 169 | 170 | std::string HttpResponse::GetFileType(std::string_view file_name) { 171 | auto type = String::Split(file_name, "."); 172 | if (type.size() != 2) { 173 | return "text/plain;charset=utf-8"; 174 | } 175 | 176 | // string_view 不能使用switch?? 177 | if (type[1] == "html") { 178 | return "text/html; charset=utf-8"; 179 | } 180 | if (type[1] == "jpg") { 181 | return "image/jpeg"; 182 | } 183 | if (type[1] == "gif") { 184 | return "image/gif"; 185 | } 186 | if (type[1] == "png") { 187 | return "image/png"; 188 | } 189 | if (type[1] == "css") { 190 | return "text/css"; 191 | } 192 | if (type[1] == "au") { 193 | return "audio/basic"; 194 | } 195 | if (type[1] == "wav") { 196 | return "audio/wav"; 197 | } 198 | if (type[1] == "avi") { 199 | return "video/x-msvideo"; 200 | } 201 | if (type[1] == "mov") { 202 | return "video/quicktime"; 203 | } 204 | if (type[1] == "mpeg") { 205 | return "video/mpeg"; 206 | } 207 | if (type[1] == "vrml") { 208 | return "model/vrml"; 209 | } 210 | if (type[1] == "midi") { 211 | return "audio/midi"; 212 | } 213 | if (type[1] == "mp3") { 214 | return "audio/mpeg"; 215 | } 216 | if (type[1] == "ogg") { 217 | return "application/ogg"; 218 | } 219 | if (type[1] == "pac") { 220 | return "application/x-ns-proxy-autoconfig"; 221 | } 222 | 223 | return ""; 224 | } 225 | 226 | } // namespace net::http 227 | -------------------------------------------------------------------------------- /luce/net/http/http_response.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace net::http { 9 | 10 | class HttpResponse { 11 | public: 12 | // could only call once, otherwise error 13 | void SetBody(std::string_view body); 14 | 15 | void SetStatusCode(int status_code) { status_code_ = status_code; } 16 | void SetHeader(std::string_view key, std::string_view value) { 17 | headers_[std::string(key)] = value; 18 | } 19 | void SetHTTPVersion(std::string_view http_version) { 20 | http_version_ = http_version; 21 | } 22 | 23 | std::string GetFileType(std::string_view file_name); 24 | 25 | std::string& GetData() { return buffer_; } 26 | 27 | void Clear() { buffer_.clear(); } 28 | 29 | private: 30 | std::string GetCodeDescript(int status_code); 31 | 32 | // TODO(pgj): impl high performance buffer 33 | std::string buffer_; 34 | int status_code_; 35 | std::string http_version_; // it comes from HttpRequset 36 | std::unordered_map headers_; 37 | // bool finish_{false}; // 相应数据是否完成 38 | }; 39 | 40 | using ResponsePtr = std::shared_ptr; 41 | 42 | } // namespace net::http 43 | -------------------------------------------------------------------------------- /luce/net/http/http_router.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "luce/common/logger.h" 4 | #include "luce/common/string_util.h" 5 | #include "luce/net/http/http_context.h" 6 | 7 | #include 8 | #include 9 | 10 | namespace net::http { 11 | 12 | using HandleFunc = std::function; 13 | 14 | struct Router { 15 | void AddRouter(std::string_view method, std::string_view url, 16 | const HandleFunc& handler) { 17 | if (method == "DELETE") { 18 | LOG_ERROR("could not DELETE now"); 19 | return; 20 | } 21 | if (!String::EndsWith(url, "/")) { 22 | LOG_ERROR("register handler for {}: {} failed, url must ends with '/'", 23 | method.data(), url.data()); 24 | return; 25 | } 26 | LOG_INFO("method: {}, url: {}", method.data(), url.data()); 27 | auto key = String::Join({method, url}, "-"); 28 | LOG_INFO("AddRouter key: {}", key.c_str()); 29 | handlers_[key] = handler; 30 | } 31 | 32 | void Handle(const ContextPtr& ctx) { 33 | auto key = String::Join({ctx->method_, ctx->path_}, "-"); 34 | if (handlers_.contains(key)) { 35 | const auto& handler = handlers_[key]; 36 | handler(ctx); 37 | } else { 38 | ctx->HTML(404, String::Format("404 Not Found: {} not at {}\n", 39 | ctx->method_, ctx->path_)); 40 | } 41 | } 42 | 43 | std::unordered_map handlers_{}; 44 | }; 45 | 46 | } // namespace net::http 47 | -------------------------------------------------------------------------------- /luce/net/http/http_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "luce/common/file_util.h" 6 | #include "luce/common/logger.h" 7 | #include "luce/common/singleton.h" 8 | #include "luce/io/io_awaiter.h" 9 | #include "luce/net/http/http_request.h" 10 | #include "luce/net/http/http_response.h" 11 | #include "luce/net/http/http_server.h" 12 | #include "luce/net/tcp/tcp_connection.h" 13 | #include "luce/timer/timer.h" 14 | 15 | namespace net::http { 16 | 17 | co::Task<> HttpServer::OnRequest(TcpConnectionPtr conn, TcpServer& server) { 18 | for (;;) { 19 | IOBuffer buffer(MAX_REQUEST_SIZE); 20 | auto timer_id = 21 | Singleton::GetInstance()->AddTimer(1000, [=] { 22 | LOG_DEBUG("timer work "); 23 | conn->Close(); 24 | }); 25 | auto recv_len = co_await conn->AsyncRead(&buffer); 26 | if (recv_len < 0) { 27 | break; 28 | } 29 | if (recv_len == 0) { 30 | continue; 31 | } 32 | // 截断buffer 33 | buffer[recv_len] = '\0'; 34 | 35 | // 解析请求 36 | auto http_request = std::make_shared(); 37 | http_request->Parse(buffer.data()); 38 | 39 | // 是否未读完 40 | if (http_request->headers_.contains("Content-Length")) { 41 | auto content_len = std::stoi(http_request->headers_["Content-Length"]); 42 | while (http_request->body_.size() != content_len) { 43 | buffer.resize(MAX_REQUEST_SIZE); 44 | recv_len = co_await conn->AsyncRead(&buffer); 45 | if (recv_len < 0) { 46 | co_return; 47 | } 48 | buffer[recv_len] = '\0'; 49 | http_request->body_ += buffer.data(); 50 | } 51 | } 52 | Singleton::GetInstance()->RemoveTimer(timer_id); 53 | 54 | // http_request->Debug(); 55 | // LOG_INFO("got a {} request on {}", http_request->method_.c_str(), 56 | // http_request->url_.c_str()); 57 | 58 | auto http_response = std::make_shared(); 59 | http_response->SetHTTPVersion(http_request->http_version_); 60 | 61 | // 运行Router回调 62 | co_await ServerHTTP(conn, http_request, http_response); 63 | 64 | // TODO(pgj): check keep alive 65 | if (http_request->headers_.contains("Connection")) { 66 | // LOG_DEBUG("Connection: {}", 67 | // http_request->headers_["Connection"].c_str()); 68 | if (http_request->headers_["Connection"] == "close") { 69 | break; 70 | } 71 | } 72 | // break; 73 | } 74 | co_return; 75 | } 76 | 77 | co::Task<> HttpServer::SendFile(std::string_view path, RequestPtr request, 78 | ResponsePtr response, TcpConnectionPtr conn) { 79 | response->SetHeader("Content-Type:", response->GetFileType(path)); 80 | auto file_size = FileSize(path); 81 | response->SetHeader("Content-Length:", std::to_string(file_size)); 82 | response->SetBody("\r\n"); 83 | co_await SendResponse(response, conn); 84 | 85 | // 发送文件 86 | int fd = open(path.data(), O_RDONLY); 87 | if (fd == -1) { 88 | LOG_ERROR("open file failed"); 89 | co_return; 90 | } 91 | IOBuffer buffer; 92 | buffer.resize(file_size); 93 | ssize_t ret = 0; 94 | while ((ret = read(fd, &*buffer.begin(), buffer.size())) > 0) { 95 | co_await conn->AsyncWrite(buffer); 96 | } 97 | if (ret == -1) { 98 | LOG_ERROR("read file failed"); 99 | co_return; 100 | } 101 | close(fd); 102 | } 103 | 104 | co::Task<> HttpServer::SendResponse(ResponsePtr response, 105 | TcpConnectionPtr conn) { 106 | // 先发送请求头 107 | auto total_size = response->GetData().size(); 108 | IOBuffer buffer(response->GetData().begin(), response->GetData().end()); 109 | auto res = co_await conn->AsyncWrite(buffer); 110 | if (res != total_size) { 111 | LOG_ERROR("HttpServer::SendResponse, did'nt finish, send: {}, total: {}", 112 | res, total_size); 113 | } 114 | response->Clear(); 115 | } 116 | 117 | co::Task<> HttpServer::ServerHTTP(TcpConnectionPtr conn, 118 | RequestPtr http_request, 119 | ResponsePtr http_response) { 120 | router_.Handle(std::make_shared(http_request, http_response)); 121 | // LOG_INFO("debug -> HTTP Response: {}", http_response->GetData().c_str()); 122 | co_await SendResponse(http_response, conn); 123 | } 124 | 125 | } // namespace net::http 126 | -------------------------------------------------------------------------------- /luce/net/http/http_server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "luce/co/task.h" 4 | #include "luce/common/logger.h" 5 | #include "luce/common/string_util.h" 6 | #include "luce/net/http/http_context.h" 7 | #include "luce/net/http/http_request.h" 8 | #include "luce/net/http/http_response.h" 9 | #include "luce/net/http/http_router.h" 10 | #include "luce/net/tcp_all.h" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace net::http { 17 | 18 | class HttpServer : public TcpApplication { 19 | public: 20 | void GET(std::string_view url, const HandleFunc& handler) { 21 | router_.AddRouter("GET", url, handler); 22 | } 23 | 24 | void POST(std::string_view url, const HandleFunc& handler) { 25 | router_.AddRouter("POST", url, handler); 26 | } 27 | 28 | void PUT(std::string_view url, const HandleFunc& handler) { 29 | router_.AddRouter("PUT", url, handler); 30 | } 31 | 32 | void DELETE(std::string_view url, const HandleFunc& handler) { 33 | router_.AddRouter("DELETE", url, handler); 34 | } 35 | 36 | void SetStaticPath(std::string_view path) { 37 | if (path.front() != '/') { 38 | LOG_FATAL("static path must start with '/'"); 39 | } 40 | static_path_ = path; 41 | } 42 | 43 | private: 44 | co::Task<> OnRequest(TcpConnectionPtr conn, TcpServer& server) override; 45 | 46 | co::Task<> ServerHTTP(TcpConnectionPtr conn, RequestPtr http_request, 47 | ResponsePtr http_response); 48 | 49 | co::Task<> SendFile(std::string_view path, RequestPtr request, 50 | ResponsePtr response, TcpConnectionPtr conn); 51 | co::Task<> SendResponse(ResponsePtr response, TcpConnectionPtr conn); 52 | 53 | Router router_; // method-url -> handle 54 | std::string static_path_; 55 | // std::string start_path_; // 程序启动的路径 56 | }; 57 | 58 | } // namespace net::http 59 | -------------------------------------------------------------------------------- /luce/net/http_all.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "luce/net/http/http_context.h" 4 | #include "luce/net/http/http_request.h" 5 | #include "luce/net/http/http_response.h" 6 | #include "luce/net/http/http_router.h" 7 | #include "luce/net/http/http_server.h" 8 | -------------------------------------------------------------------------------- /luce/net/inet_address.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace net { 9 | class InetAddress { 10 | public: 11 | explicit InetAddress(uint16_t port, std::string_view ipv4 = "127.0.0.1") { 12 | bzero(&addr_, sizeof addr_); 13 | addr_.sin_family = AF_INET; 14 | addr_.sin_port = htons(port); 15 | addr_.sin_addr.s_addr = inet_addr(ipv4.data()); 16 | } 17 | 18 | explicit InetAddress(sockaddr_in addr) : addr_(addr) {} 19 | 20 | void SetSockAddr(const sockaddr_in& addr) { addr_ = addr; } 21 | 22 | struct sockaddr* GetSockAddr() const { return (struct sockaddr*)&addr_; } 23 | 24 | private: 25 | sockaddr_in addr_{}; 26 | }; 27 | 28 | } // namespace net -------------------------------------------------------------------------------- /luce/net/rpc/invoke_helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "luce/codec/serializer.h" 8 | 9 | namespace net::rpc { 10 | 11 | template 12 | struct type_xx { 13 | using type = T; 14 | }; 15 | 16 | template <> 17 | struct type_xx { 18 | using type = int8_t; 19 | }; 20 | 21 | // 用tuple做参数调用函数模板类 22 | template 23 | decltype(auto) invoke_impl(Function&& func, Tuple&& t, 24 | std::index_sequence index_sequence) { 25 | return func(std::get(std::forward(t))...); 26 | } 27 | 28 | template 29 | decltype(auto) invoke(Function&& func, Tuple&& t) { 30 | constexpr auto size = 31 | std::tuple_size::type>::value; 32 | return invoke_impl(std::forward(func), std::forward(t), 33 | std::make_index_sequence{}); 34 | } 35 | 36 | // 调用帮助 37 | template 38 | typename std::enable_if::value, 39 | typename type_xx::type>::type 40 | call_helper(F f, ArgsTuple args) { 41 | invoke(f, args); 42 | // return 0; 43 | } 44 | 45 | template 46 | typename std::enable_if::value, 47 | typename type_xx::type>::type 48 | call_helper(F f, ArgsTuple args) { 49 | return invoke(f, args); 50 | } 51 | 52 | } // namespace net::rpc -------------------------------------------------------------------------------- /luce/net/rpc/rpc_client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "luce/co/task.h" 13 | #include "luce/codec/serializer.h" 14 | #include "luce/common/logger.h" 15 | #include "luce/io/io_awaiter.h" 16 | #include "luce/net/rpc/invoke_helper.h" 17 | #include "luce/net/rpc/rpc_err_code.h" 18 | #include "luce/net/rpc/rpc_value.h" 19 | #include "luce/net/tcp/tcp_application.h" 20 | #include "luce/net/tcp/tcp_connection.h" 21 | namespace net::rpc { 22 | 23 | class BlockingRpcClient { 24 | public: 25 | BlockingRpcClient(const std::string& server_ip, int server_port) { 26 | client_fd_ = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); 27 | 28 | struct sockaddr_in server_addr; 29 | server_addr.sin_family = AF_INET; 30 | server_addr.sin_port = htons(server_port); 31 | server_addr.sin_addr.s_addr = inet_addr(server_ip.c_str()); 32 | 33 | int retry_times = 50; 34 | while ((retry_times--) != 0) { 35 | int res = connect(client_fd_, (struct sockaddr*)&server_addr, 36 | sizeof(server_addr)); 37 | if (res == 0) { 38 | break; 39 | } 40 | if (res < 0) { 41 | if (errno == EINPROGRESS) { 42 | continue; 43 | } 44 | LOG_FATAL("blocking rpc client connect failed, errno: {}", errno); 45 | } 46 | } 47 | } 48 | 49 | ~BlockingRpcClient() { 50 | if (client_fd_ != -1) { 51 | ::close(client_fd_); 52 | } 53 | } 54 | 55 | template 56 | RpcResponse Call(const std::string& name, Params... params) { 57 | using args_type = std::tuple::type...>; 58 | args_type args = std::make_tuple(params...); 59 | 60 | codec::Serializer serializer; 61 | serializer.serialize(name); 62 | serializer.serialize(args); 63 | return NetCall(serializer); 64 | } 65 | 66 | template 67 | RpcResponse Call(const std::string& name) { 68 | codec::Serializer serializer; 69 | serializer.serialize(name); 70 | return NetCall(serializer); 71 | } 72 | 73 | private: 74 | template 75 | RpcResponse NetCall(codec::Serializer& serializer) { 76 | const auto serialized_data = serializer.str(); 77 | 78 | IOBuffer request_buffer(serialized_data.cbegin(), serialized_data.cend()); 79 | if (err_code_ != RPC_ERR_RECV_TIMEOUT) { 80 | auto res = WritePacket(request_buffer); 81 | if (res != serializer.size()) { 82 | LOG_FATAL("rpc client send error, errno: {}", errno); 83 | } 84 | } 85 | 86 | IOBuffer reply_buffer; 87 | auto recv_res = ReadPacket(reply_buffer); 88 | if (recv_res < 0) { 89 | LOG_ERROR("NetCall get response failed"); 90 | } 91 | 92 | RpcResponse value; 93 | if (recv_res == 0) { 94 | err_code_ = RPC_ERR_RECV_TIMEOUT; 95 | value.err_code = err_code_; 96 | value.err_msg = "recv timeout"; 97 | return value; 98 | } 99 | err_code_ = RPC_SUCCECC; 100 | 101 | codec::Serializer response_serializer(reply_buffer.begin(), 102 | reply_buffer.end()); 103 | response_serializer.deserialize(&value); 104 | return value; 105 | } 106 | 107 | size_t ReadPacket(IOBuffer& buffer) { 108 | char head_buffer[net::detail::HEADER_SIZE]; 109 | size_t head_recv_size = 0; 110 | while (head_recv_size != net::detail::HEADER_SIZE) { 111 | auto res = read(client_fd_, head_buffer, net::detail::HEADER_SIZE); 112 | if (res == 0) { 113 | LOG_ERROR("recv head error, server closed"); 114 | } 115 | if (res < 0) { 116 | if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) { 117 | continue; 118 | } 119 | LOG_ERROR("recv head error, errno: {}", errno); 120 | return -1; 121 | } 122 | head_recv_size += res; 123 | } 124 | 125 | uint32_t total_read_size = *reinterpret_cast(head_buffer); 126 | buffer.resize(total_read_size); 127 | size_t already_read_size = 0; 128 | while (total_read_size != 0) { 129 | auto res = ::read(client_fd_, buffer.data() + already_read_size, 130 | total_read_size); 131 | if (res == 0) { 132 | LOG_WARN("Server cloesd"); 133 | break; 134 | } 135 | if (res < 0) { 136 | if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) { 137 | continue; 138 | } 139 | break; 140 | } 141 | total_read_size -= res; 142 | already_read_size += res; 143 | } 144 | buffer.resize(already_read_size); 145 | return already_read_size; 146 | } 147 | 148 | size_t WritePacket(const IOBuffer& buffer) { 149 | size_t total_write_size = buffer.size(); 150 | 151 | char head_buffer[net::detail::HEADER_SIZE]; 152 | std::memcpy(head_buffer, reinterpret_cast(&total_write_size), 153 | net::detail::HEADER_SIZE); 154 | auto res = write(client_fd_, head_buffer, net::detail::HEADER_SIZE); 155 | if (res <= 0) { 156 | LOG_ERROR("write head error"); 157 | return -1; 158 | } 159 | size_t already_write_size = 0; 160 | while (total_write_size != 0) { 161 | res = ::write(client_fd_, buffer.data() + already_write_size, 162 | total_write_size); 163 | if (res == 0) { 164 | LOG_WARN("Server cloesd"); 165 | break; 166 | } 167 | if (res < 0) { 168 | if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) { 169 | continue; 170 | } 171 | break; 172 | } 173 | already_write_size += res; 174 | total_write_size -= res; 175 | } 176 | return already_write_size; 177 | } 178 | 179 | int client_fd_{-1}; 180 | int err_code_; 181 | }; 182 | 183 | } // namespace net::rpc -------------------------------------------------------------------------------- /luce/net/rpc/rpc_err_code.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace net::rpc { 4 | 5 | enum RpcErrCode { 6 | RPC_SUCCECC = 0, 7 | RPC_ERR_FUNCTION_NOT_FOUND, 8 | RPC_ERR_RECV_TIMEOUT 9 | }; 10 | 11 | } -------------------------------------------------------------------------------- /luce/net/rpc/rpc_server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "luce/codec/abstract_coder.h" 14 | #include "luce/codec/serializer.h" 15 | #include "luce/codec/tinypb_coder.h" 16 | #include "luce/common/logger.h" 17 | #include "luce/io/io_awaiter.h" 18 | #include "luce/net/rpc/invoke_helper.h" 19 | #include "luce/net/rpc/rpc_err_code.h" 20 | #include "luce/net/rpc/rpc_value.h" 21 | #include "luce/net/tcp/tcp_application.h" 22 | #include "luce/net/tcp/tcp_connection.h" 23 | 24 | namespace net::rpc { 25 | 26 | class RpcServer : public TcpApplication { 27 | public: 28 | using HandleFunc = 29 | std::function; 30 | RpcServer() = default; 31 | ~RpcServer() = default; 32 | 33 | template 34 | void Bind(std::string_view name, F func) { 35 | handlers_[name.data()] = 36 | std::bind(&RpcServer::CallProxy, this, func, std::placeholders::_1, 37 | std::placeholders::_2); 38 | } 39 | 40 | template 41 | void Bind(std::string_view name, F func, S* s) { 42 | handlers_[name.data()] = 43 | std::bind(&RpcServer::CallProxy, this, func, 44 | std::placeholders::_1, std::placeholders::_2); 45 | } 46 | 47 | private: 48 | co::Task<> OnRequest(TcpConnectionPtr conn, TcpServer& server) override { 49 | while (true) { 50 | IOBuffer buffer; 51 | auto succ = co_await conn->AsyncReadPacket(&buffer); 52 | if (!succ) { 53 | LOG_WARN("RpcServer recv error"); 54 | co_return; 55 | } 56 | 57 | codec::Serializer serializer(buffer.begin(), buffer.end()); 58 | 59 | std::string func_name; 60 | serializer.deserialize(&func_name); 61 | 62 | codec::Serializer* output_serializer = 63 | this->CallImpl(func_name, serializer); 64 | 65 | co_await SendResponse(conn, output_serializer); 66 | delete output_serializer; 67 | } 68 | LOG_INFO("RpcServer OnRequest end"); 69 | co_return; 70 | } 71 | 72 | size_t WritePacket(int fd, const IOBuffer& buffer) { 73 | size_t total_write_size = buffer.size(); 74 | char head_buffer[net::detail::HEADER_SIZE]; 75 | std::memcpy(head_buffer, reinterpret_cast(&total_write_size), 76 | net::detail::HEADER_SIZE); 77 | auto res = write(fd, head_buffer, net::detail::HEADER_SIZE); 78 | if (res <= 0) { 79 | LOG_ERROR("write head error"); 80 | return -1; 81 | } 82 | size_t already_write_size = 0; 83 | while (total_write_size != 0) { 84 | res = ::write(fd, buffer.data() + already_write_size, total_write_size); 85 | if (res == 0) { 86 | LOG_WARN("Server cloesd"); 87 | break; 88 | } 89 | if (res < 0) { 90 | if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) { 91 | continue; 92 | } 93 | break; 94 | } 95 | already_write_size += res; 96 | total_write_size -= res; 97 | } 98 | return already_write_size; 99 | } 100 | 101 | co::Task<> SendResponse(TcpConnectionPtr conn, 102 | codec::Serializer* serializer) { 103 | IOBuffer buffer(serializer->cbegin(), serializer->cend()); 104 | auto succ = co_await conn->AsyncWritePacket(buffer); 105 | if (!succ) { 106 | LOG_ERROR("RpcServer SendResponse error"); 107 | } 108 | co_return; 109 | } 110 | 111 | codec::Serializer* CallImpl(const std::string& name, 112 | codec::Serializer& input_serializer) { 113 | auto* output_serializer = new codec::Serializer; 114 | if (!handlers_.contains(name)) { 115 | output_serializer->serialize( 116 | RpcResponse::code_type(RPC_ERR_FUNCTION_NOT_FOUND)); 117 | output_serializer->serialize( 118 | RpcResponse::msg_type("function not bind: " + name)); 119 | LOG_ERROR("function not bind: {}", name); 120 | return output_serializer; 121 | } 122 | auto& func = handlers_[name]; 123 | func(&input_serializer, output_serializer); 124 | return output_serializer; 125 | } 126 | 127 | template 128 | void CallProxy(F func, codec::Serializer* input_serializer, 129 | codec::Serializer* output_serializer) { 130 | CallProxy_(func, input_serializer, output_serializer); 131 | } 132 | 133 | template 134 | void CallProxy(F func, S* s, codec::Serializer* input_serializer, 135 | codec::Serializer* output_serializer) { 136 | CallProxy_(func, s, input_serializer, output_serializer); 137 | } 138 | 139 | // 函数指针 140 | template 141 | void CallProxy_(R (*func)(Params...), codec::Serializer* input_serializer, 142 | codec::Serializer* output_serializer) { 143 | CallProxy_(std::function(func), input_serializer, 144 | output_serializer); 145 | } 146 | 147 | // 类成员函数指针 148 | template 149 | void CallProxy_(R (C::*func)(Params...), S* s, 150 | codec::Serializer* input_serializer, 151 | codec::Serializer* output_serializer) { 152 | using return_type = typename type_xx::type; 153 | using args_type = std::tuple::type...>; 154 | args_type args; 155 | input_serializer->deserialize(&args); 156 | 157 | auto ff = [=](Params... params) -> R { return (s->*func)(params...); }; 158 | return_type res = call_helper(ff, args); 159 | 160 | RpcResponse value; 161 | value.err_code = RPC_SUCCECC; 162 | value.detail_value = res; 163 | output_serializer->serialize(value); 164 | } 165 | 166 | // functionnal 167 | template 168 | void CallProxy_(std::function func, 169 | codec::Serializer* input_serializer, 170 | codec::Serializer* output_serializer) { 171 | using args_type = std::tuple::type...>; 172 | using return_type = typename type_xx::type; 173 | 174 | args_type args; 175 | input_serializer->deserialize(&args); 176 | 177 | return_type res = call_helper(func, args); 178 | 179 | RpcResponse value; 180 | value.err_code = RPC_SUCCECC; 181 | value.detail_value = res; 182 | output_serializer->serialize(value); 183 | } 184 | 185 | std::unordered_map handlers_; 186 | }; 187 | 188 | } // namespace net::rpc -------------------------------------------------------------------------------- /luce/net/rpc/rpc_value.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "luce/codec/serializer.h" 5 | #include "luce/net/rpc/invoke_helper.h" 6 | namespace net::rpc { 7 | 8 | // 暂时将请求大小限制为 64K 9 | constexpr int MAX_VALUE_SIZE = 1024 * 64; 10 | 11 | template 12 | struct RpcResponse { 13 | using type = typename type_xx::type; 14 | using msg_type = std::string; 15 | using code_type = uint16_t; 16 | 17 | RpcResponse() = default; 18 | ~RpcResponse() = default; 19 | 20 | T val() const { return detail_value; } 21 | 22 | friend codec::Serializer& operator>>(codec::Serializer& in, 23 | RpcResponse* value) { 24 | in >> value->err_code >> value->err_msg; 25 | if (value->err_code == 0) { 26 | in >> value->detail_value; 27 | } 28 | return in; 29 | } 30 | 31 | friend codec::Serializer& operator<<(codec::Serializer& out, 32 | const RpcResponse& value) { 33 | out << value.err_code << value.err_msg << value.detail_value; 34 | return out; 35 | } 36 | 37 | void serialize(codec::Serializer* serializer) const { 38 | serializer->serialize(err_code); 39 | serializer->serialize(err_msg); 40 | serializer->serialize(detail_value); 41 | } 42 | 43 | void deserialize(codec::Serializer* serializer) { 44 | serializer->deserialize(&err_code); 45 | serializer->deserialize(&err_msg); 46 | serializer->deserialize(&detail_value); 47 | } 48 | 49 | code_type err_code{0}; 50 | msg_type err_msg{}; 51 | type detail_value{}; 52 | }; 53 | 54 | } // namespace net::rpc -------------------------------------------------------------------------------- /luce/net/rpc_all.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "luce/net/rpc/rpc_client.h" 4 | #include "luce/net/rpc/rpc_server.h" -------------------------------------------------------------------------------- /luce/net/socket.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "luce/common/logger.h" 8 | #include "luce/net/socket.h" 9 | 10 | namespace net { 11 | 12 | Socket::~Socket() { 13 | if (fd_ != -1 && !is_closed_) { 14 | LOG_DEBUG("close fd = {}", fd_); 15 | ::close(fd_); 16 | } 17 | } 18 | void Socket::BindAddress(const InetAddress& local_addr) { 19 | int ret = ::bind(fd_, local_addr.GetSockAddr(), sizeof(struct sockaddr)); 20 | if (ret != 0) { 21 | LOG_FATAL("bind sockfd: {} failed, errno: {}", fd_, errno); 22 | } 23 | } 24 | void Socket::Listen() { 25 | int ret = ::listen(fd_, SOMAXCONN); 26 | if (ret != 0) { 27 | LOG_FATAL("listen sockfd: {} failed, errno: {}", fd_, errno); 28 | } 29 | } 30 | 31 | void Socket::ShutdownWrite() { 32 | if (::shutdown(fd_, SHUT_WR) < 0) { 33 | LOG_ERROR("shutdown write sockfd: {} error\n", fd_); 34 | } 35 | } 36 | 37 | void Socket::SetTcpNoDelay(bool on) { 38 | int option = on ? 1 : 0; 39 | ::setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, &option, sizeof option); 40 | } 41 | 42 | void Socket::SetReuseAddr(bool on) { 43 | int option = on ? 1 : 0; 44 | ::setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &option, sizeof option); 45 | } 46 | 47 | void Socket::SetReusePort(bool on) { 48 | int option = on ? 1 : 0; 49 | ::setsockopt(fd_, SOL_SOCKET, SO_REUSEPORT, &option, sizeof option); 50 | } 51 | 52 | void Socket::SetKeepAlive(bool on) { 53 | int option = on ? 1 : 0; 54 | ::setsockopt(fd_, SOL_SOCKET, SO_KEEPALIVE, &option, sizeof option); 55 | } 56 | void Socket::SetNonblock() { 57 | auto flag = fcntl(fd_, F_GETFL); 58 | fcntl(fd_, F_SETFL, flag | O_NONBLOCK); 59 | } 60 | 61 | } // namespace net 62 | -------------------------------------------------------------------------------- /luce/net/socket.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "luce/co/task.h" 8 | #include "luce/common/noncopyable.h" 9 | #include "luce/net/event_manager.h" 10 | #include "luce/net/inet_address.h" 11 | 12 | namespace net { 13 | 14 | class Socket : noncopyable { 15 | public: 16 | explicit Socket(int sock_fd = -1) : fd_(sock_fd) {} 17 | ~Socket(); 18 | 19 | void BindAddress(const InetAddress& local_addr); 20 | 21 | void Listen(); 22 | 23 | void Close() { 24 | ::close(fd_); 25 | is_closed_ = true; 26 | } 27 | 28 | void ShutdownWrite(); 29 | 30 | void SetTcpNoDelay(bool on); 31 | void SetReuseAddr(bool on); 32 | void SetReusePort(bool on); 33 | void SetKeepAlive(bool on); 34 | void SetNonblock(); 35 | 36 | void SetIOState(unsigned int io_state) { io_state_ = io_state; } 37 | 38 | auto GetIOState() -> unsigned int { return io_state_; } 39 | 40 | auto GetFd() const -> int { return fd_; } 41 | 42 | void EventAttach() { detached_ = false; } 43 | 44 | auto Attached() -> bool { return !detached_; } 45 | 46 | void EventDetach() { detached_ = true; } 47 | 48 | void SetRecvCoro(std::coroutine_handle<> read_coro) { 49 | handle.recv_coro = read_coro; 50 | } 51 | 52 | void DelRecvCoro() { handle.recv_coro = nullptr; } 53 | 54 | void SetSendCoro(std::coroutine_handle<> write_coro) { 55 | handle.send_coro = write_coro; 56 | } 57 | 58 | void DelSendCoro() { handle.send_coro = nullptr; } 59 | 60 | struct Handle { 61 | std::coroutine_handle<> recv_coro; 62 | std::coroutine_handle<> send_coro; 63 | }; 64 | Handle handle; 65 | 66 | private: 67 | int fd_; 68 | unsigned int io_state_{0}; // for epoll 69 | bool detached_{true}; 70 | std::atomic is_closed_{false}; 71 | }; 72 | 73 | } // namespace net 74 | -------------------------------------------------------------------------------- /luce/net/tcp/tcp_acceptor.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/net/tcp/tcp_acceptor.h" 2 | #include "luce/common/logger.h" 3 | #include "luce/io/io_awaiter.h" 4 | 5 | namespace net { 6 | 7 | TcpAcceptor::TcpAcceptor(TcpServer& server, int sock_fd) : server_(server) { 8 | LOG_INFO("init acceptor"); 9 | socket_ = std::make_shared(sock_fd); 10 | socket_->SetNonblock(); 11 | socket_->SetReuseAddr(true); 12 | socket_->BindAddress(server.GetLocalAddr()); 13 | socket_->Listen(); 14 | LOG_INFO("init sockfd: {}", socket_->GetFd()); 15 | } 16 | 17 | co::Task TcpAcceptor::accept() { 18 | int peer_fd = co_await AcceptAwaiter{this}; 19 | if (peer_fd == -1) { 20 | co_return nullptr; 21 | } 22 | 23 | auto peer_sock = std::make_shared(peer_fd); 24 | co_return std::make_shared(peer_sock, 25 | server_.GetMainReactor()); 26 | } 27 | 28 | } // namespace net 29 | -------------------------------------------------------------------------------- /luce/net/tcp/tcp_acceptor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "luce/net/event_manager.h" 4 | #include "luce/net/tcp/tcp_connection.h" 5 | #include "luce/net/tcp/tcp_server.h" 6 | 7 | namespace net { 8 | 9 | class TcpAcceptor { 10 | public: 11 | explicit TcpAcceptor(TcpServer& server, int sock_fd); 12 | 13 | ~TcpAcceptor() { LOG_INFO("delete acceptor"); } 14 | 15 | co::Task accept(); 16 | 17 | auto GetEventManager() const -> EventManager& { 18 | return server_.GetMainReactor(); 19 | } 20 | 21 | auto GetSocket() -> std::shared_ptr { return socket_; } 22 | 23 | private: 24 | TcpServer& server_; 25 | std::shared_ptr socket_; 26 | }; 27 | 28 | }; // namespace net 29 | -------------------------------------------------------------------------------- /luce/net/tcp/tcp_application.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/net/tcp/tcp_application.h" 2 | #include "luce/common/logger.h" 3 | #include "luce/net/tcp/tcp_server.h" 4 | 5 | namespace net { 6 | 7 | co::Task<> TcpApplication::HandleRequest(TcpConnectionPtr conn, 8 | TcpServer& server) { 9 | LOG_DEBUG("handing request"); 10 | co_return co_await OnRequest(conn, server); 11 | } 12 | 13 | } // namespace net 14 | -------------------------------------------------------------------------------- /luce/net/tcp/tcp_application.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "luce/co/task.h" 7 | #include "luce/net/tcp/tcp_connection.h" 8 | 9 | namespace net { 10 | 11 | class Socket; 12 | class TcpServer; 13 | 14 | class TcpApplication { 15 | public: 16 | TcpApplication() = default; 17 | 18 | co::Task<> HandleRequest(TcpConnectionPtr conn, TcpServer& server); 19 | 20 | protected: 21 | virtual co::Task<> OnRequest(TcpConnectionPtr conn, TcpServer& server) = 0; 22 | 23 | private: 24 | std::mutex mtx_; 25 | std::unordered_map conn_map_; 26 | }; 27 | 28 | } // namespace net 29 | -------------------------------------------------------------------------------- /luce/net/tcp/tcp_connection.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "luce/common/logger.h" 6 | #include "luce/io/io_awaiter.h" 7 | #include "luce/net/event_manager.h" 8 | #include "luce/net/tcp/tcp_connection.h" 9 | 10 | namespace net { 11 | 12 | TcpConnection::TcpConnection(std::shared_ptr sock, 13 | EventManager& event_manager) 14 | : event_manager_(event_manager), socket_(std::move(sock)) {} 15 | 16 | TcpConnection::~TcpConnection() { 17 | if (socket_->GetFd() != -1) { 18 | if (socket_->Attached()) { 19 | event_manager_.Detach(GetSocket()); 20 | } 21 | } 22 | } 23 | 24 | co::Task TcpConnection::AsyncRead(IOBuffer* buffer) { 25 | auto res = co_await ::net::AsyncRead(this, *buffer); 26 | co_return res; 27 | } 28 | 29 | co::Task TcpConnection::AsyncWrite(const IOBuffer& buffer) { 30 | auto res = co_await ::net::AsyncWrite(this, buffer); 31 | co_return res; 32 | } 33 | 34 | co::Task TcpConnection::AsyncReadPacket(IOBuffer* buffer) { 35 | auto res = co_await ::net::AsyncReadPacket(this, *buffer); 36 | co_return res; 37 | } 38 | 39 | co::Task TcpConnection::AsyncWritePacket(const IOBuffer& buffer) { 40 | auto res = co_await ::net::AsyncWritePacket(this, buffer); 41 | co_return res; 42 | } 43 | 44 | } // namespace net 45 | -------------------------------------------------------------------------------- /luce/net/tcp/tcp_connection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "luce/net/socket.h" 6 | 7 | namespace net { 8 | 9 | using IOBuffer = std::vector; 10 | 11 | class ReadAwaiter; 12 | class WriteAwaiter; 13 | 14 | class TcpConnection : noncopyable, 15 | public std::enable_shared_from_this { 16 | public: 17 | TcpConnection(std::shared_ptr sock, EventManager& event_manager); 18 | 19 | ~TcpConnection(); 20 | 21 | co::Task AsyncRead(IOBuffer* buffer); 22 | co::Task AsyncWrite(const IOBuffer& buffer); 23 | 24 | co::Task AsyncReadPacket(IOBuffer* buffer); 25 | co::Task AsyncWritePacket(const IOBuffer& buffer); 26 | 27 | auto GetEventManager() const -> EventManager& { return event_manager_; } 28 | 29 | auto GetSocket() -> std::shared_ptr { return socket_; } 30 | 31 | void Close() { 32 | if (!is_closed_) { 33 | socket_->Close(); 34 | } 35 | is_closed_ = true; 36 | } 37 | 38 | bool IsClosed() const { return is_closed_; } 39 | 40 | private: 41 | explicit TcpConnection(EventManager& event_manager) 42 | : event_manager_(event_manager) {} 43 | 44 | EventManager& event_manager_; 45 | std::shared_ptr socket_; 46 | std::atomic is_closed_{false}; 47 | }; 48 | 49 | using TcpConnectionPtr = std::shared_ptr; 50 | using TcpConnectionWeakPtr = std::weak_ptr; 51 | 52 | } // namespace net 53 | -------------------------------------------------------------------------------- /luce/net/tcp/tcp_server.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/net/tcp/tcp_server.h" 2 | #include "luce/common/logger.h" 3 | #include "luce/common/singleton.h" 4 | #include "luce/common/thread_pool.h" 5 | #include "luce/net/tcp/tcp_acceptor.h" 6 | #include "luce/timer/timer.h" 7 | 8 | namespace net { 9 | 10 | TcpServer::TcpServer(const net::InetAddress& local_addr, 11 | net::TcpApplication* app, size_t thread_num) 12 | : local_addr_(local_addr), 13 | app_(app), 14 | reactor_thread_pool_(std::make_unique(1)), 15 | work_thread_pool_(std::make_unique(thread_num)) { 16 | LOG_INFO("TcpServer start"); 17 | auto sock_fd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 18 | IPPROTO_TCP); 19 | if (sock_fd == -1) { 20 | LOG_FATAL("create socket error"); 21 | } 22 | acceptor_ = std::make_unique(*this, sock_fd); 23 | main_reactor_ = std::make_shared(nullptr, 2048); 24 | 25 | for (size_t i = 0; i < thread_num / 2 - 1; ++i) { 26 | sub_reactors_.emplace_back( 27 | std::make_shared(work_thread_pool_)); 28 | } 29 | work_thread_pool_->Commit( 30 | []() { Singleton::GetInstance()->Tick(); }); 31 | } 32 | 33 | void TcpServer::Start(bool async_start) { 34 | if (async_start) { 35 | reactor_thread_pool_->Commit([&]() { 36 | AcceptLoop().run(); 37 | LOG_INFO("AcceptLoop().await() done"); 38 | }); 39 | } 40 | main_reactor_->Start(); 41 | } 42 | 43 | void TcpServer::Shutdown() { 44 | is_shutdown_.store(true); 45 | main_reactor_->Shutdown(); 46 | for (auto& sub_reactor : sub_reactors_) { 47 | sub_reactor->Shutdown(); 48 | } 49 | Singleton::GetInstance()->Shutdown(); 50 | reactor_thread_pool_->Shutdown(); 51 | work_thread_pool_->Shutdown(); 52 | // TODO(pgj): more component to shutdown 53 | } 54 | 55 | co::Task TcpServer::AcceptLoop() { 56 | for (;;) { 57 | if (is_shutdown_.load()) { 58 | LOG_DEBUG("TcpServer::AcceptLoop() shutdown"); 59 | break; 60 | } 61 | auto conn = co_await acceptor_->accept(); 62 | if (conn != nullptr) { 63 | work_thread_pool_->Commit( 64 | [this, conn]() { this->app_->HandleRequest(conn, *this).run(); }); 65 | } 66 | } 67 | } 68 | TcpServer::~TcpServer() { LOG_INFO("TcpServer end"); } 69 | 70 | } // namespace net 71 | -------------------------------------------------------------------------------- /luce/net/tcp/tcp_server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "luce/co/task.h" 4 | #include "luce/common/noncopyable.h" 5 | #include "luce/common/thread_pool.h" 6 | #include "luce/net/event_manager.h" 7 | #include "luce/net/tcp/tcp_application.h" 8 | #include "luce/timer/timer.h" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace net { 15 | 16 | class TcpAcceptor; 17 | 18 | class TcpServer : noncopyable { 19 | public: 20 | TcpServer(const InetAddress& local_addr, TcpApplication* app, 21 | size_t thread_num = std::thread::hardware_concurrency()); 22 | 23 | ~TcpServer(); 24 | 25 | EventManager& GetMainReactor() { return *main_reactor_; } 26 | 27 | EventManager& GetSubReactor() { 28 | // TODO(pgj): 负载均衡算法 29 | auto index = cnt_ % sub_reactors_.size(); 30 | cnt_++; 31 | return *sub_reactors_[index]; 32 | } 33 | 34 | const InetAddress& GetLocalAddr() { return local_addr_; } 35 | 36 | void Start(bool async_start = true); 37 | 38 | void Shutdown(); 39 | 40 | private: 41 | co::Task AcceptLoop(); 42 | 43 | std::shared_ptr main_reactor_; 44 | std::vector> 45 | sub_reactors_; // sub reactor有多个, 暂定和线程池数量相同 46 | 47 | std::atomic cnt_{0}; 48 | std::atomic is_shutdown_{false}; 49 | const InetAddress& local_addr_; 50 | TcpApplication* app_; 51 | std::unique_ptr acceptor_; 52 | std::unique_ptr reactor_thread_pool_; 53 | std::shared_ptr work_thread_pool_; 54 | }; 55 | 56 | } // namespace net 57 | -------------------------------------------------------------------------------- /luce/net/tcp_all.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "luce/net/tcp/tcp_acceptor.h" 4 | #include "luce/net/tcp/tcp_application.h" 5 | #include "luce/net/tcp/tcp_connection.h" 6 | #include "luce/net/tcp/tcp_server.h" -------------------------------------------------------------------------------- /luce/net/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace net { 8 | 9 | int32_t GetInt32ForNetByte(const char* buf) { 10 | int32_t res; 11 | std::memcpy(&res, buf, sizeof(res)); 12 | return static_cast(ntohl(res)); 13 | } 14 | 15 | int32_t GetNetInt32(int32_t num) { return static_cast(htonl(num)); } 16 | 17 | } // namespace net -------------------------------------------------------------------------------- /luce/timer/timer.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/timer/timer.h" 2 | 3 | namespace timer { 4 | size_t TimerManager::timer_id_ = 0; 5 | } // namespace timer -------------------------------------------------------------------------------- /luce/timer/timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace timer { 12 | using TimePoint = std::chrono::system_clock::time_point; 13 | using TimerTask = std::function; 14 | 15 | struct Timer { 16 | Timer() = default; 17 | Timer(TimePoint expiry_time, size_t id, TimerTask task) 18 | : expiry_time(expiry_time), timer_id(id), task(std::move(task)) {} 19 | TimePoint expiry_time; 20 | size_t timer_id; 21 | TimerTask task; 22 | }; 23 | 24 | // A simple timer manager 25 | class TimerManager { 26 | public: 27 | size_t AddTimer(uint32_t milliseconds, const TimerTask& callback) { 28 | TimePoint now = std::chrono::system_clock::now(); 29 | TimePoint expiry_time = now + std::chrono::milliseconds(milliseconds); 30 | std::unique_lock lock(mtx_); 31 | auto id = timer_id_++; 32 | Push(Timer{expiry_time, id, callback}); 33 | return id; 34 | } 35 | 36 | void RemoveTimer(size_t timer_id) { 37 | std::unique_lock lock(mtx_); 38 | auto iter = std::find_if( 39 | timers_.begin(), timers_.end(), 40 | [&](const Timer& timer) { return timer.timer_id == timer_id; }); 41 | if (iter != timers_.end()) { 42 | Erase(iter - timers_.begin()); 43 | } 44 | } 45 | 46 | void Tick() { 47 | while (!stop_) { 48 | auto now = std::chrono::system_clock::now(); 49 | std::unique_lock lock(mtx_); 50 | if (!Empty() && now >= top().expiry_time) { 51 | auto timer = pop(); 52 | std::thread(timer.task).detach(); 53 | } else { 54 | lock.unlock(); 55 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 56 | } 57 | } 58 | } 59 | 60 | void Shutdown() { stop_ = true; } 61 | 62 | private: 63 | void HeapifyUp(uint32_t index) { 64 | if (index == 0) { 65 | return; 66 | } 67 | uint32_t parent_index = (index - 1) / 2; 68 | if (timers_[index].expiry_time < timers_[parent_index].expiry_time) { 69 | std::swap(timers_[index], timers_[parent_index]); 70 | HeapifyUp(parent_index); 71 | } 72 | } 73 | 74 | void HeapifyDown(uint32_t index) { 75 | uint32_t left_child_index = index * 2 + 1; 76 | uint32_t right_child_index = index * 2 + 2; 77 | uint32_t smallest_index = index; 78 | if (left_child_index < timers_.size() && 79 | timers_[left_child_index].expiry_time < 80 | timers_[smallest_index].expiry_time) { 81 | smallest_index = left_child_index; 82 | } 83 | if (right_child_index < timers_.size() && 84 | timers_[right_child_index].expiry_time < 85 | timers_[smallest_index].expiry_time) { 86 | smallest_index = right_child_index; 87 | } 88 | if (smallest_index != index) { 89 | std::swap(timers_[smallest_index], timers_[index]); 90 | HeapifyDown(smallest_index); 91 | } 92 | } 93 | 94 | void Erase(uint32_t index) { 95 | std::swap(timers_[index], timers_.back()); 96 | timers_.pop_back(); 97 | HeapifyDown(index); 98 | } 99 | 100 | bool Empty() const { return timers_.empty(); } 101 | 102 | Timer& top() { return timers_.front(); } 103 | 104 | Timer pop() { 105 | if (timers_.empty()) { 106 | return {}; 107 | } 108 | auto& timer = top(); 109 | Erase(0); 110 | return timer; 111 | } 112 | 113 | void Push(const Timer& timer) { 114 | timers_.emplace_back(timer); 115 | HeapifyUp(timers_.size() - 1); 116 | } 117 | 118 | std::vector timers_; // heap 119 | static size_t timer_id_; 120 | std::atomic stop_{false}; 121 | std::mutex mtx_; 122 | }; 123 | 124 | } // namespace timer -------------------------------------------------------------------------------- /luce/xmake.lua: -------------------------------------------------------------------------------- 1 | add_requires("spdlog", {system = false, configs = {header_only = true, fmt_external = true}}) 2 | add_requireconfs("spdlog.fmt", {system = false, override = true, version = "9.1.0", configs = {header_only = true}}) 3 | 4 | target("luce") 5 | set_kind("shared") 6 | add_files("$(projectdir)/luce/**.cpp") 7 | add_includedirs("$(projectdir)/", { public = true }) 8 | add_packages("spdlog", "fmt", {public = true}) 9 | add_cxxflags("-O3") 10 | target_end() 11 | -------------------------------------------------------------------------------- /tests/codec/test_serializer.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include 3 | #include "luce/codec/serializer.h" 4 | #include "luce/net/rpc_all.h" 5 | using namespace codec; // NOLINT 6 | 7 | // 类 A 8 | class A { 9 | public: 10 | A() = default; 11 | A(const std::string& name, int age) : name(name), age(age) {} 12 | 13 | void serialize(Serializer* serializer) const { 14 | serializer->serialize(name); 15 | serializer->serialize(age); 16 | } 17 | 18 | void deserialize(Serializer* serializer) { 19 | serializer->deserialize(&name); 20 | serializer->deserialize(&age); 21 | } 22 | 23 | bool operator==(const A& rhs) { return age == rhs.age && name == rhs.name; } 24 | 25 | private: 26 | std::string name; 27 | int age; 28 | }; 29 | 30 | // 类 B 31 | class B { 32 | public: 33 | B() = default; 34 | B(const std::string& address, int salary) 35 | : address(address), salary(salary) {} 36 | 37 | void serialize(Serializer* serializer) const { 38 | serializer->serialize(address); 39 | serializer->serialize(salary); 40 | } 41 | 42 | void deserialize(Serializer* serializer) { 43 | serializer->deserialize(&address); 44 | serializer->deserialize(&salary); 45 | } 46 | 47 | bool operator==(const B& rhs) { 48 | return address == rhs.address && salary == rhs.salary; 49 | } 50 | 51 | private: 52 | std::string address; 53 | int salary; 54 | }; 55 | 56 | // 类 C 57 | class C { 58 | public: 59 | C() = default; 60 | C(bool isStudent, const std::string& school) 61 | : is_student(isStudent), school(school) {} 62 | 63 | void serialize(Serializer* serializer) const { 64 | serializer->serialize(is_student); 65 | serializer->serialize(school); 66 | } 67 | 68 | void deserialize(Serializer* serializer) { 69 | serializer->deserialize(&is_student); 70 | serializer->deserialize(&school); 71 | } 72 | 73 | bool operator==(const C& rhs) { 74 | return is_student == rhs.is_student && school == rhs.school; 75 | } 76 | 77 | private: 78 | bool is_student; 79 | std::string school; 80 | }; 81 | 82 | template 83 | typename std::enable_if::type print_tuple( 84 | const std::tuple& t) {} 85 | 86 | template 87 | typename std::enable_if < 88 | I::type print_tuple(const std::tuple& t) { 89 | auto container = std::get(t); 90 | for (auto item : container) { 91 | std::cout << item << " "; 92 | } 93 | std::cout << std::endl; 94 | print_tuple(t); 95 | } 96 | 97 | TEST_CASE("basic") { 98 | Serializer serializer; 99 | A a("Alice", 25); 100 | B b("123 Main St", 5000); 101 | C c(true, "ABC School"); 102 | 103 | // 序列化 104 | serializer.serialize(a); 105 | serializer.serialize(b); 106 | serializer.serialize(c); 107 | 108 | // 反序列化 109 | A restoredA; 110 | B restoredB; 111 | C restoredC; 112 | 113 | serializer.deserialize(&restoredA); 114 | serializer.deserialize(&restoredB); 115 | serializer.deserialize(&restoredC); 116 | 117 | CHECK(a == restoredA); 118 | CHECK(b == restoredB); 119 | CHECK(c == restoredC); 120 | } 121 | 122 | TEST_CASE("s to s") { 123 | Serializer serializer; 124 | A a("Alice", 25); 125 | B b("123 Main St", 5000); 126 | C c(true, "ABC School"); 127 | 128 | // 序列化 129 | serializer.serialize(a); 130 | serializer.serialize(b); 131 | serializer.serialize(c); 132 | 133 | // 用另一个 Serialize 反序列化 134 | Serializer out_serializer(serializer.str()); 135 | A restoredA; 136 | B restoredB; 137 | C restoredC; 138 | 139 | out_serializer.deserialize(&restoredA); 140 | out_serializer.deserialize(&restoredB); 141 | out_serializer.deserialize(&restoredC); 142 | 143 | CHECK(a == restoredA); 144 | CHECK(b == restoredB); 145 | CHECK(c == restoredC); 146 | } 147 | 148 | TEST_CASE("container") { 149 | Serializer serializer; 150 | std::vector vec = {1, 2, 3, 4, 5}; 151 | std::deque deq = {6, 7, 8, 9, 10}; 152 | std::list l = {11, 12, 13, 14, 15}; 153 | std::forward_list f_l = {16, 17, 18, 19, 20}; 154 | 155 | auto in_tuple = std::make_tuple(vec, deq, l, f_l); 156 | 157 | print_tuple(in_tuple); 158 | 159 | serializer.serialize(in_tuple); 160 | 161 | std::tuple, std::deque, std::list, 162 | std::forward_list> 163 | out_tuple; 164 | serializer.deserialize(&out_tuple); 165 | 166 | print_tuple(out_tuple); 167 | } -------------------------------------------------------------------------------- /tests/common/test_logger.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include 3 | #include "luce/common/logger.h" 4 | 5 | TEST_CASE("test LOG_INFO") { 6 | LOG_INFO("this is a info, {}, {}", "hello", "INFO"); 7 | } 8 | 9 | TEST_CASE("test LOG_WARN") { 10 | LOG_WARN("this is a warn, {}, {}", "hello", "WARN"); 11 | } 12 | 13 | TEST_CASE("test LOG_DEBUG") { 14 | LOG_DEBUG("this is a debug, {}", "hello", "DEBUG"); 15 | } 16 | 17 | TEST_CASE("test LOG_ERROR") { 18 | LOG_ERROR("this is a error, {}", "hello", "ERROR"); 19 | } 20 | -------------------------------------------------------------------------------- /tests/common/test_threadpool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "luce/common/logger.h" 5 | #include "luce/common/thread_pool.h" 6 | 7 | int main(int argc, char* argv[]) { 8 | // create thread pool with 4 worker threads 9 | ThreadPool pool(4); 10 | 11 | // enqueue and store future 12 | for (int i = 0; i < 100; ++i) { 13 | auto result = pool.Commit( 14 | [](int answer) { 15 | // TODO(pgj): use log 16 | std::stringstream ss; 17 | ss << std::this_thread::get_id(); 18 | LOG_INFO("this_thread: {}, answer: {}", ss.str(), answer); 19 | }, 20 | i); 21 | } 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /tests/coroutine/test_task.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "luce/co/scheduler.h" 6 | #include "luce/co/task.h" 7 | 8 | using co::co_spawn; 9 | 10 | co::Task simple_task2() { 11 | fmt::print("task 2 start...\n"); 12 | using namespace std::chrono_literals; 13 | std::this_thread::sleep_for(1s); 14 | fmt::print("task 2 returns after 1s\n"); 15 | co_return 2; 16 | } 17 | 18 | co::Task simple_task3() { 19 | fmt::print("task 3 start...\n"); 20 | using namespace std::chrono_literals; 21 | std::this_thread::sleep_for(2s); 22 | fmt::print("task 3 returns after 2s\n"); 23 | co_return 3; 24 | } 25 | 26 | co::Task simple_task() { 27 | fmt::print("task start...\n"); 28 | fmt::print("waiting for result2...\n"); 29 | auto result2 = co_await simple_task2(); 30 | fmt::print("result from task2: {}\n", result2); 31 | auto result3 = co_await simple_task3(); 32 | fmt::print("result from task3: {}\n", result3); 33 | co_return 1 + result2 + result3; 34 | } 35 | 36 | co::Task<> co_main() { 37 | auto res = co_await simple_task(); 38 | fmt::print("the result of simple_task: {}\n", res); 39 | } 40 | 41 | co::Task ans() { co_return 42; } 42 | 43 | int main(int argc, char* argv[]) { 44 | co_spawn([]() -> co::Task<> { 45 | auto res = co_await simple_task(); 46 | fmt::print("the result of simple_task: {}\n", res); 47 | }()); 48 | co_spawn(co_main()); 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /tests/net/echo_server2.cpp: -------------------------------------------------------------------------------- 1 | #include "luce/common/logger.h" 2 | #include "luce/common/thread_pool.h" 3 | #include "luce/io/io_awaiter.h" 4 | #include "luce/net/tcp/tcp_connection.h" 5 | #include "luce/net/tcp_all.h" 6 | 7 | class EchoServer : public net::TcpApplication { 8 | private: 9 | co::Task<> OnRequest(net::TcpConnectionPtr conn, 10 | net::TcpServer& server) override { 11 | while (true) { 12 | net::IOBuffer buffer(512); 13 | ssize_t recv_len = co_await conn->AsyncRead(&buffer); 14 | if (recv_len < 0) { 15 | LOG_ERROR("EchoServer read error"); 16 | break; 17 | } 18 | if (recv_len == 0) { 19 | LOG_INFO("client closed"); 20 | break; 21 | } 22 | LOG_DEBUG("Done send\n"); 23 | auto res = co_await conn->AsyncWrite(buffer); 24 | if (res != recv_len) { 25 | LOG_ERROR("EchoServer write error"); 26 | } 27 | } 28 | } 29 | }; 30 | 31 | int main(int argc, char* argv[]) { 32 | net::InetAddress addr{12345}; 33 | EchoServer app; 34 | net::TcpServer server(addr, &app, 8); 35 | server.Start(); 36 | LOG_INFO("all down"); 37 | } 38 | -------------------------------------------------------------------------------- /tests/net/rpc/test_rpc_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "luce/codec/serializer.h" 3 | #include "luce/common/logger.h" 4 | #include "luce/net/rpc/rpc_client.h" 5 | #include "luce/net/rpc_all.h" 6 | 7 | struct Student { 8 | std::string name; 9 | int age; 10 | 11 | void serialize(codec::Serializer* serializer) const { 12 | serializer->serialize(name); 13 | serializer->serialize(age); 14 | } 15 | 16 | void deserialize(codec::Serializer* serializer) { 17 | serializer->deserialize(&name); 18 | serializer->deserialize(&age); 19 | } 20 | }; 21 | 22 | int main(int argc, char* argv[]) { 23 | spdlog::set_level(spdlog::level::debug); 24 | net::rpc::BlockingRpcClient client("127.0.0.1", 12345); 25 | int res = client.Call("add", 2, 3).val(); 26 | LOG_INFO("call add response: {}", res); 27 | 28 | Student stu_res = client.Call("get_stu", "pgj", 21).val(); 29 | LOG_INFO("call get_stu response: name: {}, age: {}", stu_res.name, 30 | stu_res.age); 31 | return 0; 32 | } -------------------------------------------------------------------------------- /tests/net/rpc/test_rpc_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "luce/codec/type_helper.h" 3 | #include "luce/common/logger.h" 4 | #include "luce/net/inet_address.h" 5 | #include "luce/net/rpc/rpc_server.h" 6 | #include "luce/net/rpc_all.h" 7 | #include "luce/net/tcp/tcp_server.h" 8 | #include "luce/net/tcp_all.h" 9 | #include "spdlog/common.h" 10 | #include "spdlog/spdlog.h" 11 | 12 | struct Student { 13 | std::string name; 14 | int age; 15 | 16 | void serialize(codec::Serializer* serializer) const { 17 | serializer->serialize(name); 18 | serializer->serialize(age); 19 | } 20 | 21 | void deserialize(codec::Serializer* serializer) { 22 | serializer->deserialize(&name); 23 | serializer->deserialize(&age); 24 | } 25 | }; 26 | 27 | Student get_stu(const std::string& name, int age) { return {name, age}; } 28 | 29 | int add(int a, int b) { 30 | auto res = a + b; 31 | LOG_INFO("recv add request: {} + {} = {}", a, b, res); 32 | return res; 33 | } 34 | 35 | int main(int argc, char* argv[]) { 36 | spdlog::set_level(spdlog::level::debug); 37 | net::InetAddress addr{12345}; 38 | net::rpc::RpcServer rpc_app; 39 | 40 | rpc_app.Bind("add", add); 41 | rpc_app.Bind("get_stu", get_stu); 42 | 43 | net::TcpServer server(addr, &rpc_app, 8); 44 | server.Start(); 45 | LOG_INFO("server end"); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /tests/net/test_http.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "luce/common/json.h" 3 | #include "luce/common/logger.h" 4 | #include "luce/net/http_all.h" 5 | #include "luce/net/tcp_all.h" 6 | 7 | int main(int argc, char* argv[]) { 8 | net::InetAddress addr{12345}; 9 | net::http::HttpServer http_app; 10 | 11 | http_app.GET("/ping/", [](const net::http::ContextPtr& ctx) { 12 | ctx->HTML(200, "Pong"); 13 | }); 14 | 15 | http_app.POST("/add/", [](const net::http::ContextPtr& ctx) { 16 | LOG_INFO("POST run: {}", ctx->req_->body_.c_str()); 17 | 18 | auto param1 = ctx->QueryBody("param1"); 19 | auto param2 = ctx->QueryBody("param2"); 20 | if (param1.empty() || param2.empty()) { 21 | ctx->HTML(404, "Error Param"); 22 | return; 23 | } 24 | auto res = atoi(param1.c_str()) + atoi(param2.c_str()); 25 | ctx->HTML(200, String::Format("res: {}\n", res)); 26 | }); 27 | 28 | net::TcpServer server(addr, &http_app, 8); 29 | server.Start(); 30 | LOG_INFO("all down"); 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /tests/xmake.lua: -------------------------------------------------------------------------------- 1 | add_requires("doctest") 2 | add_requires("spdlog", {system = false, configs = {header_only = true, fmt_external = true}}) 3 | add_requireconfs("spdlog.fmt", {system = false, override = true, version = "9.1.0", configs = {header_only = true}}) 4 | 5 | target("test_threadpool") 6 | set_kind("binary") 7 | add_files("$(projectdir)/tests/common/test_threadpool.cpp") 8 | add_deps("luce") 9 | add_packages("spdlog") 10 | 11 | target("test_logger") 12 | set_kind("binary") 13 | set_group("tests") 14 | add_files("$(projectdir)/tests/common/test_logger.cpp") 15 | add_packages("doctest", "spdlog") 16 | add_deps("luce") 17 | 18 | target("test_task") 19 | set_kind("binary") 20 | add_files("$(projectdir)/tests/coroutine/test_task.cpp") 21 | add_deps("luce") 22 | add_packages("fmt") 23 | 24 | target("echo_server2") 25 | set_kind("binary") 26 | add_files("$(projectdir)/tests/net/echo_server2.cpp") 27 | add_deps("luce") 28 | add_packages("fmt") 29 | 30 | target("test_http") 31 | set_kind("binary") 32 | add_files("$(projectdir)/tests/net/test_http.cpp") 33 | add_deps("luce") 34 | add_packages("fmt") 35 | 36 | target("test_rpc_client") 37 | set_kind("binary") 38 | add_files("$(projectdir)/tests/net/rpc/test_rpc_client.cpp") 39 | add_deps("luce") 40 | add_packages("fmt", "spdlog") 41 | 42 | target("test_rpc_server") 43 | set_kind("binary") 44 | add_files("$(projectdir)/tests/net/rpc/test_rpc_server.cpp") 45 | add_deps("luce") 46 | add_packages("fmt", "spdlog") 47 | 48 | target("test_serializer") 49 | set_kind("binary") 50 | add_files("$(projectdir)/tests/codec/test_serializer.cpp") 51 | add_deps("luce") 52 | add_packages("doctest", "fmt", "spdlog") -------------------------------------------------------------------------------- /xmake.lua: -------------------------------------------------------------------------------- 1 | -- define project 2 | set_project("luce") 3 | set_xmakever("2.7.0") 4 | set_version("0.1.0", {build = "%Y%m%d%H%M"}) 5 | 6 | -- set common flags 7 | set_warnings("all") 8 | set_languages("cxx20") 9 | 10 | -- add build mode 11 | add_rules("mode.release", "mode.debug") 12 | -- add_rules("mode.debug") 13 | 14 | -- inclue subdirs 15 | includes("luce", "tests", "example") 16 | 17 | -- run script 18 | target("check-lint") 19 | set_kind("phony") 20 | 21 | on_run(function (target) 22 | os.run("sh $(projectdir)/script/check_lint.sh") 23 | end) 24 | 25 | target("check-tidy") 26 | set_kind("phony") 27 | 28 | on_run(function (target) 29 | os.run("xmake project -k compile_commands") 30 | --[[ os.run("sh $(projectdir)/script/check_tidy.sh") ]] 31 | os.run("python $(projectdir)/script/run_clang_tidy.py -j 12") 32 | end) 33 | --------------------------------------------------------------------------------