├── .gitattributes ├── 3rd ├── README.md └── CMakeLists.txt ├── doc ├── pics │ ├── myframe.png │ └── myframe_view.png ├── version_release.md └── development_guide.md ├── examples ├── example_worker_quit.json ├── example_actor_timer.json ├── example_thread_quit.json ├── example_worker_talk.json ├── example_worker_publish.json ├── example_actor_helloworld.json ├── example_actor_subscribe.json ├── example_trans_obj.json ├── example_worker_actor_interactive.json ├── example_node.json ├── example_worker_interactive_with_3rd_frame.json ├── example_actor_serial.json ├── example_actor_concurrent.json ├── example_config.json ├── example_worker_quit.cpp ├── example_actor_timer.cpp ├── example_worker_talk.cpp ├── example_actor_helloworld.cpp ├── example_worker_publish.cpp ├── example_thread_quit.cpp ├── example_actor_subscribe.cpp ├── example_config.cpp ├── example_trans_obj.cpp ├── example_worker_actor_interactive.cpp ├── example_actor_concurrent.cpp ├── example_actor_serial.cpp └── CMakeLists.txt ├── myframe ├── Config.cmake.in ├── config.h.in ├── macros.h ├── log.h ├── platform.h ├── event.cpp ├── poller.h ├── shared_library.cpp ├── worker_common.h ├── worker_common.cpp ├── event_conn_manager.h ├── log.cpp ├── cmd_channel.h ├── event_conn.h ├── poller.cpp ├── list.h ├── cmd_channel.cpp ├── actor_context_manager.h ├── actor_context.h ├── common.h ├── shared_library.h ├── platform │ ├── poller_generic.h │ ├── shared_library_win.h │ ├── shared_library_linux.h │ ├── cmd_channel_generic.h │ ├── poller_linux.h │ └── cmd_channel_linux.h ├── msg.cpp ├── event_manager.h ├── event.h ├── event_conn.cpp ├── worker_context_manager.h ├── actor_context.cpp ├── mailbox.h ├── worker_timer.h ├── actor.cpp ├── worker.cpp ├── mod_manager.h ├── msg.h ├── event_conn_manager.cpp ├── worker_context.h ├── event_manager.cpp ├── CMakeLists.txt ├── list.cpp ├── worker.h ├── worker_context.cpp ├── actor.h ├── mailbox.cpp ├── actor_context_manager.cpp └── app.h ├── .gitignore ├── .github └── workflows │ ├── codestyle.yml │ ├── macos.yml │ └── linux.yml ├── launcher ├── conf │ └── myframe.json ├── launcher_config.h.in ├── CMakeLists.txt ├── module_argument.h └── launcher.cpp ├── templates ├── template.json ├── cmake │ └── Packing.cmake ├── CMakeLists.txt └── template.cpp ├── test ├── performance_test_config.h.in ├── common_test.cpp ├── hello_test.cpp ├── actor_run_queue_test.cpp ├── app_send_test.cpp ├── CMakeLists.txt ├── app_send_req_test.cpp ├── performance_trans1_cost_test.cpp ├── performance_trans1_fullspeed_test.cpp ├── performance_trans20_fullspeed_test.cpp ├── performance_trans100_fullspeed_test.cpp └── performance_trans10_cost_test.cpp ├── CPPLINT.cfg ├── cpplint.bash ├── LICENSE ├── cmake ├── Packing.cmake └── InstallingConfigs.cmake ├── README.md ├── CMakeLists.txt └── tools └── gen_mod_proj.py /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-language=C++ 2 | -------------------------------------------------------------------------------- /3rd/README.md: -------------------------------------------------------------------------------- 1 | # 3rd 2 | 该目录主要用于下载/构建/安装依赖包使用 3 | -------------------------------------------------------------------------------- /doc/pics/myframe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lkpworkspace/myframe/HEAD/doc/pics/myframe.png -------------------------------------------------------------------------------- /doc/pics/myframe_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lkpworkspace/myframe/HEAD/doc/pics/myframe_view.png -------------------------------------------------------------------------------- /doc/version_release.md: -------------------------------------------------------------------------------- 1 | # 版本发布 2 | * 修改CMakeLists.txt中版本号 3 | * 提交修改 4 | * 合入master 5 | * 生成版本tag并发布 6 | * 发布tag中详细描述版本更新内容 7 | -------------------------------------------------------------------------------- /examples/example_worker_quit.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"example_worker_quit", 4 | "worker":{ 5 | "ExampleWorkerQuit":[ 6 | { 7 | "instance_name":"1" 8 | } 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /myframe/Config.cmake.in: -------------------------------------------------------------------------------- 1 | 2 | @PACKAGE_INIT@ 3 | 4 | include (CMakeFindDependencyMacro) 5 | 6 | find_dependency (Threads REQUIRED) 7 | @glog_DEPENDENCY@ 8 | @jsoncpp_DEPENDENCY@ 9 | 10 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 11 | -------------------------------------------------------------------------------- /examples/example_actor_timer.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"example_actor_timer", 4 | "actor":{ 5 | "ExampleActorTimer":[ 6 | { 7 | "instance_name":"1" 8 | } 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/example_thread_quit.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"example_thread_quit", 4 | "actor":{ 5 | "ExampleThreadQuit":[ 6 | { 7 | "instance_name":"1" 8 | } 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/example_worker_talk.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"example_worker_talk", 4 | "worker":{ 5 | "ExampleWorkerTalk":[ 6 | { 7 | "instance_name":"1" 8 | } 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/example_worker_publish.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"example_worker_publish", 4 | "worker":{ 5 | "ExampleWorkerPublic":[ 6 | { 7 | "instance_name":"1" 8 | } 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/example_actor_helloworld.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"example_actor_helloworld", 4 | "actor":{ 5 | "ExampleActorHelloWorld":[ 6 | { 7 | "instance_name":"1" 8 | } 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.ipch 3 | 4 | *.bin 5 | 6 | bin/ 7 | 8 | build* 9 | 10 | output* 11 | 12 | *.user 13 | 14 | .vscode 15 | 16 | launcher/launcher_config.h 17 | test/performance_test_config.h 18 | myframe/export.h 19 | myframe/config.h 20 | 3rd/pkg/ 21 | 3rd/src/ 22 | .DS_Store -------------------------------------------------------------------------------- /.github/workflows/codestyle.yml: -------------------------------------------------------------------------------- 1 | name: codestyle 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | Ubuntu-latest: 7 | name: codestyle 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: Check code style 13 | run: bash ${{github.workspace}}/cpplint.bash -------------------------------------------------------------------------------- /launcher/conf/myframe.json: -------------------------------------------------------------------------------- 1 | { 2 | "thread_poll_size":4, 3 | "conn_event_size":2, 4 | "warning_msg_size":10, 5 | "log_dir":"log", 6 | "lib_dir":"", 7 | "service_dir":"service", 8 | "default_pending_queue_size":-1, 9 | "default_run_queue_size":2, 10 | "log_max_size_mb":100 11 | } 12 | -------------------------------------------------------------------------------- /templates/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"@template_name@", 4 | "actor":{ 5 | "@template_name@":[ 6 | { 7 | "instance_name":"1", 8 | "instance_params":"" 9 | } 10 | ] 11 | }, 12 | "worker":{ 13 | "@template_name@":[ 14 | { 15 | "instance_name":"1" 16 | } 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /myframe/config.h.in: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | 9 | #define MYFRAME_VERSION "@PROJECT_VERSION@" 10 | 11 | #cmakedefine MYFRAME_USE_CV -------------------------------------------------------------------------------- /examples/example_actor_subscribe.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"example_actor_subscribe", 4 | "actor":{ 5 | "ExampleActorPub":[ 6 | { 7 | "instance_name":"1" 8 | } 9 | ], 10 | "ExampleActorSub":[ 11 | { 12 | "instance_name":"1" 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/example_trans_obj.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"example_trans_obj", 4 | "actor":{ 5 | "ExampleActorTransObj":[ 6 | { 7 | "instance_name":"1" 8 | } 9 | ] 10 | }, 11 | "worker":{ 12 | "ExampleWorkerTransObj":[ 13 | { 14 | "instance_name":"1" 15 | } 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/performance_test_config.h.in: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | 9 | #define MYFRAME_LIB_DIR "@MYFRAME_LIB_DIR@" 10 | #define MYFRAME_LOG_DIR "@MYFRAME_LOG_DIR@" -------------------------------------------------------------------------------- /examples/example_worker_actor_interactive.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"example_worker_actor_interactive", 4 | "actor":{ 5 | "ExampleActorInteractive":[ 6 | { 7 | "instance_name":"1" 8 | } 9 | ] 10 | }, 11 | "worker":{ 12 | "ExampleWorkerInteractive":[ 13 | { 14 | "instance_name":"1" 15 | } 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/example_node.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "library", 3 | "lib": "example_node", 4 | "actor": { 5 | "node": [ 6 | { 7 | "instance_name": "1" 8 | } 9 | ], 10 | "ExampleNodePub": [ 11 | { 12 | "instance_name": "1" 13 | } 14 | ], 15 | "ExampleNodeSub": [ 16 | { 17 | "instance_name": "1" 18 | } 19 | ] 20 | } 21 | } -------------------------------------------------------------------------------- /myframe/macros.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | 9 | #define DISALLOW_COPY_AND_ASSIGN(classname) \ 10 | classname(const classname&) = delete; \ 11 | classname& operator=(const classname&) = delete; 12 | 13 | #define CACHELINE_SIZE 64 14 | -------------------------------------------------------------------------------- /examples/example_worker_interactive_with_3rd_frame.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"example_worker_interactive_with_3rd_frame", 4 | "actor":{ 5 | "ExampleActorInteractiveWith3rdFrame":[ 6 | { 7 | "instance_name":"1" 8 | } 9 | ] 10 | }, 11 | "worker":{ 12 | "ExampleWorkerInteractiveWith3rdFrame":[ 13 | { 14 | "instance_name":"1" 15 | } 16 | ] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/example_actor_serial.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"example_actor_serial", 4 | "actor":{ 5 | "ExampleActorSerial1":[ 6 | { 7 | "instance_name":"1" 8 | } 9 | ], 10 | "ExampleActorSerial2":[ 11 | { 12 | "instance_name":"1" 13 | } 14 | ], 15 | "ExampleActorSerial3":[ 16 | { 17 | "instance_name":"1" 18 | } 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /launcher/launcher_config.h.in: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | 9 | #define MYFRAME_BIN_DIR "@MYFRAME_BIN_DIR@" 10 | #define MYFRAME_LIB_DIR "@MYFRAME_LIB_DIR@" 11 | #define MYFRAME_LOG_DIR "@MYFRAME_LOG_DIR@" 12 | #define MYFRAME_SERVICE_DIR "@MYFRAME_SERVICE_DIR@" 13 | #define MYFRAME_CONF_DIR "@MYFRAME_CONF_DIR@" -------------------------------------------------------------------------------- /examples/example_actor_concurrent.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"example_actor_concurrent", 4 | "actor":{ 5 | "ExampleActorConcurrentTrigger":[ 6 | { 7 | "instance_name":"1" 8 | } 9 | ], 10 | "ExampleActorConcurrent":[ 11 | { 12 | "instance_name":"1" 13 | }, 14 | { 15 | "instance_name":"2" 16 | }, 17 | { 18 | "instance_name":"3" 19 | } 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CPPLINT.cfg: -------------------------------------------------------------------------------- 1 | # Stop searching for additional config files. 2 | set noparent 3 | 4 | # Disable a warning about C++ features that were not in the original 5 | # C++11 specification (and so might not be well-supported). 6 | filter=-build/c++11 7 | filter=-build/include_alpha,-build/include_subdir,+build/include_order 8 | filter=+build/include_what_you_use 9 | 10 | 11 | # Disable header_guard warning 12 | # Consider using #pragma once instead 13 | filter=-build/header_guard 14 | filter=+runtime/printf,+runtime/printf_format 15 | 16 | linelength=80 17 | includeorder=standardcfirst 18 | -------------------------------------------------------------------------------- /myframe/log.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | #include 9 | 10 | #include 11 | 12 | #include "myframe/export.h" 13 | #include "myframe/common.h" 14 | 15 | namespace myframe { 16 | 17 | MYFRAME_EXPORT void InitLog( 18 | const stdfs::path& log_dir, 19 | const std::string& bin_name, 20 | int max_size_mb = 100); 21 | 22 | MYFRAME_EXPORT void ShutdownLog(); 23 | 24 | } // namespace myframe 25 | -------------------------------------------------------------------------------- /cpplint.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | readonly PWD0=$(readlink -fn "$(dirname "$0")") 3 | 4 | function main() { 5 | set -e 6 | cd "$PWD0" 7 | # shellcheck disable=SC2038 8 | find . '(' \ 9 | -name "*.c" -or \ 10 | -name "*.cc" -or \ 11 | -name "*.h" -or \ 12 | -name "*.hpp" -or \ 13 | -name "*.c++" -or \ 14 | -name "*.h++" -or \ 15 | -name "*.hh" -or \ 16 | -name "*.cu" -or \ 17 | -name "*.cpp" -or \ 18 | -name "*.hxx" -or \ 19 | -name "*.cxx" -or \ 20 | -name "*.cuh" \ 21 | ')' -and -not -path "./build*" \ 22 | -and -not -path "./output*" \ 23 | -and -not -path "./3rd/*" \ 24 | -and -not -path "./myframe/export.h" \ 25 | | xargs python3 ./cpplint.py 26 | } 27 | 28 | main "$@" 29 | -------------------------------------------------------------------------------- /myframe/platform.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | 9 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) 10 | #define MYFRAME_OS_WINDOWS 11 | #elif defined(linux) || defined(__linux) || defined(__linux__) 12 | #define MYFRAME_OS_LINUX 13 | #elif defined(ANDROID) || defined(__ANDROID__) 14 | #define MYFRAME_OS_ANDROID 15 | #elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) 16 | #define MYFRAME_OS_MACOSX 17 | #else 18 | #error Platform not supported by myframe. 19 | #endif 20 | -------------------------------------------------------------------------------- /examples/example_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "type":"library", 3 | "lib":"example_config", 4 | "actor":{ 5 | "ExampleActorConfig":[ 6 | { 7 | "instance_name":"1", 8 | "instance_config":{ 9 | "pending_queue_size":-1, 10 | "run_queue_size":-1, 11 | "key1":"hello", 12 | "key2":"world" 13 | } 14 | } 15 | ] 16 | }, 17 | "worker":{ 18 | "ExampleWorkerConfig":[ 19 | { 20 | "instance_name":"1", 21 | "instance_config":{ 22 | "key1":100, 23 | "key2":200 24 | } 25 | } 26 | ] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/common_test.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #include "myframe/common.h" 9 | #include "myframe/log.h" 10 | 11 | int main() { 12 | auto root = myframe::Common::GetWorkRoot(); 13 | LOG(INFO) << "work root is " << root.string(); 14 | 15 | auto lib_path = myframe::Common::GetAbsolutePath("lib"); 16 | LOG(INFO) << "lib path is " << lib_path.string(); 17 | 18 | auto root_files = myframe::Common::GetDirFiles(root.string()); 19 | LOG(INFO) << "root dir files:"; 20 | for (size_t i = 0; i < root_files.size(); ++i) { 21 | LOG(INFO) << " " << root_files[i].string(); 22 | } 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /myframe/event.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include "myframe/event.h" 8 | 9 | namespace myframe { 10 | 11 | #if defined(MYFRAME_OS_LINUX) || defined(MYFRAME_OS_ANDROID) 12 | #ifdef MYFRAME_USE_CV 13 | const ev_handle_t Event::DEFAULT_EV_HANDLE{nullptr}; 14 | #endif 15 | #elif defined(MYFRAME_OS_WINDOWS) || defined(MYFRAME_OS_MACOSX) 16 | #ifdef MYFRAME_USE_CV 17 | const ev_handle_t Event::DEFAULT_EV_HANDLE{nullptr}; 18 | #else 19 | #error "Support conditional variables only," 20 | " set MYFRAME_USE_CV to enable" 21 | #endif 22 | #else 23 | #error "Unsupported platform" 24 | #endif 25 | 26 | } // namespace myframe 27 | -------------------------------------------------------------------------------- /examples/example_worker_quit.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | 10 | #include "myframe/log.h" 11 | #include "myframe/actor.h" 12 | #include "myframe/msg.h" 13 | #include "myframe/worker.h" 14 | 15 | class ExampleWorkerQuit : public myframe::Worker { 16 | public: 17 | ExampleWorkerQuit() {} 18 | virtual ~ExampleWorkerQuit() {} 19 | 20 | void Run() override { 21 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 22 | Stop(); 23 | } 24 | }; 25 | 26 | /* 创建worker实例函数 */ 27 | extern "C" MYFRAME_EXPORT std::shared_ptr worker_create( 28 | const std::string& worker_name) { 29 | if (worker_name == "ExampleWorkerQuit") { 30 | return std::make_shared(); 31 | } 32 | return nullptr; 33 | } 34 | -------------------------------------------------------------------------------- /myframe/poller.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | 13 | #include "myframe/export.h" 14 | #include "myframe/macros.h" 15 | #include "myframe/event.h" 16 | 17 | namespace myframe { 18 | 19 | class MYFRAME_EXPORT Poller { 20 | public: 21 | Poller() = default; 22 | virtual ~Poller() = default; 23 | 24 | static std::shared_ptr Create(); 25 | 26 | virtual bool Init() = 0; 27 | virtual bool Add(const std::shared_ptr&) const { return true; } 28 | virtual bool Del(const std::shared_ptr&) const { return true; } 29 | virtual int Wait(std::vector* evs, int timeout_ms = 100) = 0; 30 | virtual void Notify(ev_handle_t) {} 31 | 32 | private: 33 | DISALLOW_COPY_AND_ASSIGN(Poller) 34 | }; 35 | 36 | } // namespace myframe 37 | -------------------------------------------------------------------------------- /myframe/shared_library.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include "myframe/shared_library.h" 8 | #include "myframe/platform.h" 9 | 10 | #if defined(MYFRAME_OS_LINUX) || defined(MYFRAME_OS_ANDROID) \ 11 | || defined(MYFRAME_OS_MACOSX) 12 | #include "myframe/platform/shared_library_linux.h" 13 | #elif defined(MYFRAME_OS_WINDOWS) 14 | #include "myframe/platform/shared_library_win.h" 15 | #else 16 | #error "Platform not supported" 17 | #endif 18 | 19 | namespace myframe { 20 | 21 | std::shared_ptr SharedLibrary::Create() { 22 | #if defined(MYFRAME_OS_LINUX) || defined(MYFRAME_OS_ANDROID) \ 23 | || defined(MYFRAME_OS_MACOSX) 24 | return std::make_shared(); 25 | #elif defined(MYFRAME_OS_WINDOWS) 26 | return std::make_shared(); 27 | #else 28 | return nullptr; 29 | #endif 30 | } 31 | 32 | } // namespace myframe 33 | -------------------------------------------------------------------------------- /templates/cmake/Packing.cmake: -------------------------------------------------------------------------------- 1 | include(InstallRequiredSystemLibraries) 2 | 3 | set(CPACK_PACKAGE_NAME ${PROJECT_NAME} CACHE STRING "The myframe's component name") 4 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "The myframe's component" 5 | CACHE STRING "Package description for the package metadata" 6 | ) 7 | set(CPACK_STRIP_FILES YES) 8 | set(CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS 9 | OWNER_READ OWNER_WRITE OWNER_EXECUTE 10 | GROUP_READ GROUP_EXECUTE 11 | WORLD_READ WORLD_EXECUTE 12 | ) 13 | set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) 14 | set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) 15 | set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) 16 | set(CPACK_PACKAGE_CONTACT "contributor@myframe.com") 17 | 18 | # group 19 | set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE) 20 | 21 | # deb 22 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "contributor <${CPACK_PACKAGE_CONTACT}>") 23 | set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) 24 | set(CPACK_DEB_COMPONENT_INSTALL YES) 25 | set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS YES) 26 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "myframe (>= 0.9)") 27 | 28 | include(CPack) 29 | -------------------------------------------------------------------------------- /examples/example_actor_timer.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #include "myframe/log.h" 9 | #include "myframe/msg.h" 10 | #include "myframe/actor.h" 11 | 12 | class ExampleActorTimer : public myframe::Actor { 13 | public: 14 | int Init() override { 15 | /* 设置超时时间为 100 * 10 ms */ 16 | Timeout("1000ms", 10); 17 | return 0; 18 | } 19 | 20 | void Proc(const std::shared_ptr& msg) override { 21 | if (msg->GetType() == "TIMER" && msg->GetDesc() == "1000ms") { 22 | /* 设置下一次超时时间 100 * 10 ms */ 23 | Timeout("1000ms", 100); 24 | LOG(INFO) << *msg << ": " << "timeout"; 25 | } 26 | } 27 | }; 28 | 29 | extern "C" MYFRAME_EXPORT std::shared_ptr actor_create( 30 | const std::string& actor_name) { 31 | if (actor_name == "ExampleActorTimer") { 32 | return std::make_shared(); 33 | } 34 | return nullptr; 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 李柯鹏 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cmake/Packing.cmake: -------------------------------------------------------------------------------- 1 | include(InstallRequiredSystemLibraries) 2 | 3 | set(CPACK_PACKAGE_NAME ${PROJECT_NAME} CACHE STRING "The resulting package name") 4 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "C++ application framework" 5 | CACHE STRING "Package description for the package metadata" 6 | ) 7 | set(CPACK_STRIP_FILES YES) 8 | set(CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS 9 | OWNER_READ OWNER_WRITE OWNER_EXECUTE 10 | GROUP_READ GROUP_EXECUTE 11 | WORLD_READ WORLD_EXECUTE 12 | ) 13 | set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) 14 | set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) 15 | set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) 16 | set(CPACK_PACKAGE_CONTACT "likepeng0418@163.com") 17 | set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") 18 | set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") 19 | 20 | # group 21 | set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE) 22 | 23 | # deb 24 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "likepeng <${CPACK_PACKAGE_CONTACT}>") 25 | set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT) 26 | set(CPACK_DEB_COMPONENT_INSTALL YES) 27 | set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS YES) 28 | 29 | include(CPack) 30 | -------------------------------------------------------------------------------- /myframe/worker_common.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | 11 | #include "myframe/worker.h" 12 | 13 | namespace myframe { 14 | 15 | class Msg; 16 | class ActorContext; 17 | class WorkerCommon final : public Worker { 18 | friend class App; 19 | 20 | public: 21 | WorkerCommon() = default; 22 | virtual ~WorkerCommon(); 23 | 24 | void Run() override; 25 | void Init() override; 26 | void Exit() override; 27 | 28 | Event::Type GetType() override { 29 | return Event::Type::kWorkerCommon; 30 | } 31 | 32 | void SetActorContext(std::shared_ptr context) { 33 | context_ = context; 34 | } 35 | std::shared_ptr GetActorContext() { 36 | return context_.lock(); 37 | } 38 | 39 | private: 40 | /* 工作线程消息处理 */ 41 | int Work(); 42 | /* 工作线程进入空闲链表之前进行的操作 */ 43 | void Idle(); 44 | 45 | /// 当前执行actor的指针 46 | std::weak_ptr context_; 47 | }; 48 | 49 | } // namespace myframe 50 | -------------------------------------------------------------------------------- /examples/example_worker_talk.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | 10 | #include "myframe/log.h" 11 | #include "myframe/actor.h" 12 | #include "myframe/msg.h" 13 | #include "myframe/worker.h" 14 | 15 | class ExampleWorkerTalk : public myframe::Worker { 16 | public: 17 | ExampleWorkerTalk() {} 18 | virtual ~ExampleWorkerTalk() {} 19 | 20 | void Run() override { 21 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 22 | // recv msg from some device 23 | // ... 24 | // send msg to actor/worker 25 | // eg: Send("actor.xx.xx", std::make_shared("hello,world")); 26 | LOG(INFO) << "talk worker do something"; 27 | DispatchMsg(); 28 | } 29 | }; 30 | 31 | /* 创建worker实例函数 */ 32 | extern "C" MYFRAME_EXPORT std::shared_ptr worker_create( 33 | const std::string& worker_name) { 34 | if (worker_name == "ExampleWorkerTalk") { 35 | return std::make_shared(); 36 | } 37 | return nullptr; 38 | } 39 | -------------------------------------------------------------------------------- /examples/example_actor_helloworld.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #include "myframe/log.h" 9 | #include "myframe/msg.h" 10 | #include "myframe/actor.h" 11 | 12 | /* 13 | 自己给自己发送一条消息 14 | */ 15 | class ExampleActorHelloWorld : public myframe::Actor { 16 | public: 17 | /* actor模块加载完毕后调用 */ 18 | int Init() override { 19 | auto mailbox = GetMailbox(); 20 | /* 构造 hello,world 消息发送给自己 */ 21 | mailbox->Send(mailbox->Addr(), std::string("hello,world")); 22 | return 0; 23 | } 24 | 25 | void Proc(const std::shared_ptr& msg) override { 26 | /* 获得文本消息, 打印 源actor地址 目的actor地址 消息内容*/ 27 | auto data = msg->GetAnyData(); 28 | LOG(INFO) << *msg << ": " << data; 29 | } 30 | }; 31 | 32 | /* 创建actor模块实例函数 */ 33 | extern "C" MYFRAME_EXPORT std::shared_ptr actor_create( 34 | const std::string& actor_name) { 35 | if (actor_name == "ExampleActorHelloWorld") { 36 | return std::make_shared(); 37 | } 38 | return nullptr; 39 | } 40 | -------------------------------------------------------------------------------- /examples/example_worker_publish.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | 10 | #include "myframe/log.h" 11 | #include "myframe/actor.h" 12 | #include "myframe/msg.h" 13 | #include "myframe/worker.h" 14 | 15 | class ExampleWorkerPublic : public myframe::Worker { 16 | public: 17 | ExampleWorkerPublic() {} 18 | virtual ~ExampleWorkerPublic() {} 19 | 20 | void Run() override { 21 | if (-1 == DispatchAndWaitMsg()) { 22 | return; 23 | } 24 | auto mailbox = GetMailbox(); 25 | while (!mailbox->RunEmpty()) { 26 | const auto msg = mailbox->PopRun(); 27 | // send msg by udp/tcp/zmq/... 28 | LOG(INFO) << "public msg " << msg->GetData() << " ..."; 29 | } 30 | } 31 | }; 32 | 33 | /* 创建worker实例函数 */ 34 | extern "C" MYFRAME_EXPORT std::shared_ptr worker_create( 35 | const std::string& worker_name) { 36 | if (worker_name == "ExampleWorkerPublic") { 37 | return std::make_shared(); 38 | } 39 | return nullptr; 40 | } 41 | -------------------------------------------------------------------------------- /cmake/InstallingConfigs.cmake: -------------------------------------------------------------------------------- 1 | # export cmake file 2 | install(EXPORT "${PROJECT_NAME}Targets" 3 | FILE "${PROJECT_NAME}Targets.cmake" 4 | DESTINATION lib/cmake/${PROJECT_NAME} 5 | ) 6 | 7 | include(CMakePackageConfigHelpers) 8 | # generate the config file that is includes the exports 9 | configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in 10 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 11 | INSTALL_DESTINATION "lib/cmake/${PROJECT_NAME}" 12 | NO_SET_AND_CHECK_MACRO 13 | NO_CHECK_REQUIRED_COMPONENTS_MACRO 14 | ) 15 | 16 | # generate the version file for the config file 17 | write_basic_package_version_file( 18 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" 19 | VERSION "${PROJECT_VERSION}" 20 | COMPATIBILITY SameMinorVersion 21 | ) 22 | 23 | # install the configuration file 24 | install(FILES 25 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 26 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" 27 | DESTINATION lib/cmake/${PROJECT_NAME} 28 | ) 29 | 30 | # generate the export targets for the build tree 31 | # needs to be after the install(TARGETS ) command 32 | export(EXPORT "${PROJECT_NAME}Targets" 33 | FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake" 34 | ) 35 | -------------------------------------------------------------------------------- /launcher/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.10) 2 | 3 | ### configure file 4 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/launcher_config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/launcher_config.h @ONLY) 5 | 6 | ### source 7 | get_filename_component(DIR_NAME ${CMAKE_CURRENT_LIST_DIR} NAME_WE) 8 | aux_source_directory(. __srcs) 9 | 10 | ### bin 11 | add_executable(${DIR_NAME} ${__srcs}) 12 | target_link_libraries(${DIR_NAME} PRIVATE ${PROJECT_NAME}) 13 | 14 | if (glog_lib_type STREQUAL "STATIC_LIBRARY") 15 | target_link_libraries(${DIR_NAME} 16 | PRIVATE 17 | glog::glog 18 | ) 19 | endif () 20 | if (jsoncpp_lib_type STREQUAL "STATIC_LIBRARY") 21 | target_link_libraries(${DIR_NAME} 22 | PRIVATE 23 | JsonCpp::JsonCpp 24 | ) 25 | endif () 26 | 27 | if (MYFRAME_ENABLE_ASAN) 28 | target_compile_options(${DIR_NAME} 29 | PRIVATE 30 | "$<$:-fsanitize=address>" 31 | ) 32 | target_link_options(${DIR_NAME} 33 | PRIVATE 34 | "$<$:-fsanitize=address>" 35 | ) 36 | endif () 37 | 38 | ### install 39 | install(TARGETS ${DIR_NAME} 40 | LIBRARY DESTINATION ${MYFRAME_LIB_DIR} 41 | ARCHIVE DESTINATION ${MYFRAME_LIB_DIR} 42 | RUNTIME DESTINATION ${MYFRAME_BIN_DIR} 43 | ) 44 | install(DIRECTORY conf DESTINATION .) 45 | -------------------------------------------------------------------------------- /myframe/worker_common.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #include "myframe/worker_common.h" 9 | 10 | #include "myframe/log.h" 11 | #include "myframe/msg.h" 12 | #include "myframe/actor_context.h" 13 | 14 | namespace myframe { 15 | 16 | WorkerCommon::~WorkerCommon() {} 17 | 18 | void WorkerCommon::Idle() { 19 | if (!context_.expired()) { 20 | context_.reset(); 21 | } 22 | } 23 | 24 | void WorkerCommon::Run() { 25 | if (-1 == DispatchMsg()) { 26 | return; 27 | } 28 | Work(); 29 | } 30 | 31 | void WorkerCommon::Init() { 32 | LOG(INFO) << "Worker " << GetWorkerName() << " init"; 33 | } 34 | 35 | void WorkerCommon::Exit() { 36 | LOG(INFO) << "Worker " << GetWorkerName() << " exit"; 37 | } 38 | 39 | int WorkerCommon::Work() { 40 | auto ctx = context_.lock(); 41 | if (ctx == nullptr) { 42 | LOG(ERROR) << "context is nullptr"; 43 | return -1; 44 | } 45 | auto ctx_mailbox = ctx->GetMailbox(); 46 | while (!ctx_mailbox->RunEmpty()) { 47 | ctx->Proc(ctx_mailbox->PopRun()); 48 | } 49 | 50 | return 0; 51 | } 52 | 53 | } // namespace myframe 54 | -------------------------------------------------------------------------------- /myframe/event_conn_manager.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "myframe/macros.h" 15 | #include "myframe/event.h" 16 | 17 | namespace myframe { 18 | 19 | class Msg; 20 | class Poller; 21 | class EventManager; 22 | class EventConn; 23 | class EventConnManager final { 24 | public: 25 | EventConnManager( 26 | std::shared_ptr, 27 | std::shared_ptr); 28 | virtual ~EventConnManager(); 29 | 30 | bool Init(int sz = 2); 31 | 32 | std::shared_ptr Alloc(); 33 | 34 | void Release(std::shared_ptr); 35 | 36 | void Notify(ev_handle_t, std::shared_ptr msg); 37 | 38 | void Clear(); 39 | 40 | private: 41 | void AddEventConn(); 42 | 43 | int conn_sz_{0}; 44 | std::mutex mtx_; 45 | std::list> idle_conn_; 46 | std::shared_ptr ev_mgr_; 47 | std::shared_ptr poller_; 48 | 49 | DISALLOW_COPY_AND_ASSIGN(EventConnManager) 50 | }; 51 | 52 | } // namespace myframe 53 | -------------------------------------------------------------------------------- /myframe/log.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #include "myframe/log.h" 9 | 10 | #include 11 | #include 12 | 13 | static void signal_handler(const char *data, size_t size) { 14 | std::string str = std::string(data, size); 15 | std::cerr << str; 16 | LOG(ERROR) << "\n" << str; 17 | } 18 | 19 | namespace myframe { 20 | 21 | void InitLog( 22 | const stdfs::path& log_dir, 23 | const std::string& bin_name, 24 | int max_size_mb) { 25 | google::InitGoogleLogging(bin_name.c_str()); 26 | 27 | FLAGS_logbufsecs = 0; 28 | FLAGS_max_log_size = max_size_mb; 29 | FLAGS_stop_logging_if_full_disk = true; 30 | 31 | std::string dst_str = (log_dir / bin_name).string(); 32 | google::SetLogDestination(google::GLOG_ERROR, ""); 33 | google::SetLogDestination(google::GLOG_WARNING, ""); 34 | google::SetLogDestination(google::GLOG_FATAL, ""); 35 | google::SetLogDestination(google::GLOG_INFO, dst_str.c_str()); 36 | 37 | google::InstallFailureSignalHandler(); 38 | google::InstallFailureWriter(&signal_handler); 39 | } 40 | 41 | void ShutdownLog() { 42 | google::ShutdownGoogleLogging(); 43 | } 44 | 45 | } // namespace myframe 46 | -------------------------------------------------------------------------------- /myframe/cmd_channel.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | #include 9 | 10 | #include "myframe/export.h" 11 | #include "myframe/macros.h" 12 | #include "myframe/event.h" 13 | #include "myframe/poller.h" 14 | 15 | namespace myframe { 16 | 17 | class MYFRAME_EXPORT CmdChannel { 18 | public: 19 | enum class Cmd : char { 20 | kQuit = 'q', ///< 退出 21 | kIdle = 'i', ///< 空闲 22 | kWaitForMsg = 'w', ///< 等待消息 23 | kRun = 'r', ///< 运行 24 | kRunWithMsg = 'm', ///< 运行(有消息) 25 | }; 26 | explicit CmdChannel(std::shared_ptr poller) 27 | : poller_(poller) {} 28 | virtual ~CmdChannel() = default; 29 | 30 | static std::shared_ptr Create(std::shared_ptr); 31 | 32 | virtual ev_handle_t GetOwnerHandle() const = 0; 33 | virtual ev_handle_t GetMainHandle() const = 0; 34 | 35 | virtual int SendToOwner(const Cmd& cmd) = 0; 36 | virtual int RecvFromOwner(Cmd* cmd) = 0; 37 | 38 | virtual int SendToMain(const Cmd& cmd) = 0; 39 | virtual int RecvFromMain(Cmd* cmd, int timeout_ms = -1) = 0; 40 | 41 | protected: 42 | std::shared_ptr poller_{nullptr}; 43 | 44 | private: 45 | DISALLOW_COPY_AND_ASSIGN(CmdChannel) 46 | }; 47 | 48 | } // namespace myframe 49 | -------------------------------------------------------------------------------- /templates/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | if (POLICY CMP0091) 3 | cmake_policy(SET CMP0091 NEW) 4 | endif() 5 | project(@template_name@ VERSION 1.0.0) 6 | 7 | #### cmake module 8 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") 9 | 10 | #### compile option 11 | set(CMAKE_CXX_STANDARD 17) 12 | set(CMAKE_CXX_STANDARD_REQUIRED YES) 13 | set(CMAKE_C_VISIBILITY_PRESET hidden) 14 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 15 | set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) 16 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 17 | add_compile_options("$<$:/source-charset:utf-8>") 18 | 19 | #### deps lib 20 | find_package(myframe REQUIRED) 21 | 22 | #### lib 23 | add_library(${PROJECT_NAME} SHARED 24 | ${PROJECT_NAME}.cpp 25 | ) 26 | target_link_libraries(${PROJECT_NAME} 27 | myframe 28 | ) 29 | 30 | #### export 31 | include (GenerateExportHeader) 32 | generate_export_header (${PROJECT_NAME} 33 | EXPORT_MACRO_NAME MYFRAME_SUBMODULE_EXPORT 34 | EXPORT_FILE_NAME "${CMAKE_CURRENT_SOURCE_DIR}/export.h" 35 | ) 36 | 37 | #### install 38 | install(TARGETS ${PROJECT_NAME} 39 | LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib 40 | ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib 41 | RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin 42 | ) 43 | file(GLOB conf_files "*.json") 44 | install(FILES 45 | ${conf_files} 46 | PERMISSIONS 47 | OWNER_READ OWNER_WRITE 48 | GROUP_READ 49 | WORLD_READ 50 | DESTINATION ${CMAKE_INSTALL_PREFIX}/service 51 | ) 52 | 53 | ### package 54 | include(Packing) 55 | -------------------------------------------------------------------------------- /myframe/event_conn.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | #include 9 | #include 10 | 11 | #include "myframe/macros.h" 12 | #include "myframe/event.h" 13 | #include "myframe/mailbox.h" 14 | #include "myframe/cmd_channel.h" 15 | #include "myframe/poller.h" 16 | 17 | namespace myframe { 18 | 19 | class Msg; 20 | class EventConnManager; 21 | class EventConn final : public Event { 22 | friend class App; 23 | friend class EventManager; 24 | friend class EventConnManager; 25 | 26 | public: 27 | enum class Type : char { 28 | kSendReq, 29 | kSend, 30 | }; 31 | 32 | explicit EventConn(std::shared_ptr); 33 | 34 | ev_handle_t GetHandle() const override; 35 | Event::Type GetType() const override; 36 | std::string GetName() const override; 37 | 38 | EventConn::Type GetConnType() { return conn_type_; } 39 | 40 | int Send(std::shared_ptr msg); 41 | 42 | const std::shared_ptr SendRequest( 43 | std::shared_ptr req); 44 | 45 | private: 46 | Mailbox* GetMailbox(); 47 | CmdChannel* GetCmdChannel(); 48 | 49 | std::shared_ptr cmd_channel_; 50 | Mailbox mailbox_; 51 | EventConn::Type conn_type_{ EventConn::Type::kSendReq }; 52 | 53 | DISALLOW_COPY_AND_ASSIGN(EventConn) 54 | }; 55 | 56 | } // namespace myframe 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # myframe 2 | 3 | ![myframe](doc/pics/myframe.png) 4 | 5 | ## 概述 6 | C++实现的组件化的编程框架,程序由actor和worker组成; 7 | actor基于消息驱动,actor之间可以进行消息传递; 8 | worker自驱动,可以通过消息与actor交互; 9 | 适用于构建中大型项目. 10 | 11 | ## 开发/运行环境 12 | | C++ 标准 | 13 | | -------------- | 14 | | C++17 | 15 | 16 | | 操作系统支持 | 17 | | -------------- | 18 | | Linux | 19 | | Windows | 20 | | macOS | 21 | 22 | ## github构建 23 | * [github ci linux](.github/workflows/linux.yml) 24 | * [github ci windows](.github/workflows/windows.yml) 25 | * [github ci macOS](.github/workflows/macos.yml) 26 | 27 | ## 快速本地构建 28 | ```sh 29 | # 下载/构建/安装依赖库 30 | cmake -S 3rd -B build_3rd -DCMAKE_INSTALL_PREFIX=output 31 | cmake --build build_3rd -j --config Release 32 | # 构建安装 33 | cmake -S . -B build_proj -DCMAKE_INSTALL_PREFIX=output -DCMAKE_PREFIX_PATH=output -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON 34 | cmake --build build_proj -j --config Release --target install 35 | ``` 36 | 37 | ### Hello,World API示例 38 | - [API 示例](test/hello_test.cpp) 39 | 40 | ### Hello,World 组件示例 41 | - [组件代码示例](examples/example_actor_helloworld.cpp) 42 | - [组件配置示例](examples/example_actor_helloworld.json) 43 | 44 | ## 程序接口 45 | - [Example](examples) 46 | - [Actor模块](myframe/actor.h) 47 | - [Worker模块](myframe/worker.h) 48 | - [Msg模块](myframe/msg.h) 49 | 50 | ## 文档 51 | - [开发手册](doc/development_guide.md) 52 | - [Discussions](https://github.com/lkpworkspace/myframe/discussions) 53 | - [WIKI](https://github.com/lkpworkspace/myframe/wiki) 54 | - [FAQ](https://github.com/lkpworkspace/myframe/wiki/FAQs) 55 | -------------------------------------------------------------------------------- /myframe/poller.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include "myframe/poller.h" 8 | #include "myframe/platform.h" 9 | 10 | #if defined(MYFRAME_OS_LINUX) || defined(MYFRAME_OS_ANDROID) 11 | #ifdef MYFRAME_USE_CV 12 | #include "myframe/platform/poller_generic.h" 13 | #else 14 | #include "myframe/platform/poller_linux.h" 15 | #endif 16 | #elif defined(MYFRAME_OS_WINDOWS) || defined(MYFRAME_OS_MACOSX) 17 | #ifdef MYFRAME_USE_CV 18 | #include "myframe/platform/poller_generic.h" 19 | #else 20 | #error "Support conditional variables only," 21 | " set MYFRAME_USE_CV to enable" 22 | #endif 23 | #else 24 | #error "Unsupported platform" 25 | #endif 26 | 27 | namespace myframe { 28 | 29 | std::shared_ptr Poller::Create() { 30 | #if defined(MYFRAME_OS_LINUX) || defined(MYFRAME_OS_ANDROID) 31 | #ifdef MYFRAME_USE_CV 32 | return std::make_shared(); 33 | #else 34 | return std::make_shared(); 35 | #endif 36 | #elif defined(MYFRAME_OS_WINDOWS) || defined(MYFRAME_OS_MACOSX) 37 | #ifdef MYFRAME_USE_CV 38 | return std::make_shared(); 39 | #else 40 | #error "Support conditional variables only," 41 | " set MYFRAME_USE_CV to enable" 42 | #endif 43 | #else 44 | #error "Unsupported platform" 45 | #endif 46 | } 47 | 48 | } // namespace myframe 49 | -------------------------------------------------------------------------------- /test/hello_test.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include "myframe/msg.h" 9 | #include "myframe/actor.h" 10 | #include "myframe/mod_manager.h" 11 | #include "myframe/app.h" 12 | 13 | #include "performance_test_config.h" 14 | 15 | class Hello : public myframe::Actor { 16 | public: 17 | /* actor模块加载完毕后调用 */ 18 | int Init() override { 19 | /* 构造 hello,world 消息发送给自己 */ 20 | auto mailbox = GetMailbox(); 21 | mailbox->Send( 22 | mailbox->Addr(), 23 | std::make_shared("hello,world")); 24 | return 0; 25 | } 26 | 27 | void Proc(const std::shared_ptr& msg) override { 28 | /* 获得文本消息, 打印 源actor地址 目的actor地址 消息内容 */ 29 | std::cout << *msg << ": " << msg->GetData() << std::endl; 30 | } 31 | }; 32 | 33 | int main() { 34 | auto lib_dir = 35 | myframe::Common::GetAbsolutePath(MYFRAME_LIB_DIR).string(); 36 | 37 | auto app = std::make_shared(); 38 | if (false == app->Init(lib_dir, 1)) { 39 | std::cout << "Init failed\n"; 40 | return -1; 41 | } 42 | 43 | auto& mod = app->GetModManager(); 44 | 45 | mod->RegActor("Hello", [](const std::string&) { 46 | return std::make_shared(); 47 | }); 48 | auto actor = mod->CreateActorInst("class", "Hello", "1"); 49 | app->AddActor(actor); 50 | 51 | return app->Exec(); 52 | } 53 | -------------------------------------------------------------------------------- /myframe/list.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | 9 | /** 10 | * 双向循环链表 11 | **/ 12 | namespace myframe { 13 | 14 | class ListNode { 15 | public: 16 | ListNode() : prev(nullptr), next(nullptr) {} 17 | virtual ~ListNode() {} 18 | 19 | ListNode* prev; 20 | 21 | ListNode* next; 22 | }; 23 | 24 | class List final { 25 | public: 26 | List() { __Init(); } 27 | 28 | ~List() { Clear(false); } 29 | 30 | void AddHead(ListNode* node); 31 | 32 | void AddTail(ListNode* node); 33 | 34 | void Del(ListNode* node, bool b = false); 35 | 36 | void DelHead(bool b = false); 37 | 38 | void DelTail(bool b = false); 39 | 40 | void DelWithIndex(int index, bool b = false); // not useful 41 | 42 | void MoveHead(ListNode* node); 43 | 44 | void MoveTail(ListNode* node); 45 | 46 | void Append(List* from); 47 | 48 | ListNode* Begin() { return root_.next; } 49 | 50 | ListNode* End() { return &root_; } 51 | 52 | public: 53 | ListNode* GetData(int index); // not useful 54 | 55 | int Count() { return count_; } 56 | 57 | bool IsEmpty() { return &root_ == root_.next; } 58 | 59 | void Clear(bool b = false); 60 | 61 | private: 62 | void __Init(); 63 | 64 | void __Add(ListNode* prev, ListNode* next, ListNode* node); 65 | 66 | void __Del(ListNode* prev, ListNode* next, bool b); 67 | 68 | ListNode root_; 69 | 70 | int count_; 71 | }; 72 | 73 | } // namespace myframe 74 | -------------------------------------------------------------------------------- /myframe/cmd_channel.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include "myframe/cmd_channel.h" 8 | #include "myframe/platform.h" 9 | 10 | #if defined(MYFRAME_OS_LINUX) || defined(MYFRAME_OS_ANDROID) 11 | #ifdef MYFRAME_USE_CV 12 | #include "myframe/platform/cmd_channel_generic.h" 13 | #else 14 | #include "myframe/platform/cmd_channel_linux.h" 15 | #endif 16 | #elif defined(MYFRAME_OS_WINDOWS) || defined(MYFRAME_OS_MACOSX) 17 | #ifdef MYFRAME_USE_CV 18 | #include "myframe/platform/cmd_channel_generic.h" 19 | #else 20 | #error "Support conditional variables only," 21 | " set MYFRAME_USE_CV to enable" 22 | #endif 23 | #else 24 | #error "Unsupported platform" 25 | #endif 26 | 27 | namespace myframe { 28 | 29 | std::shared_ptr CmdChannel::Create( 30 | std::shared_ptr poller) { 31 | #if defined(MYFRAME_OS_LINUX) || defined(MYFRAME_OS_ANDROID) 32 | #ifdef MYFRAME_USE_CV 33 | return std::make_shared(poller); 34 | #else 35 | return std::make_shared(poller); 36 | #endif 37 | #elif defined(MYFRAME_OS_WINDOWS) || defined(MYFRAME_OS_MACOSX) 38 | #ifdef MYFRAME_USE_CV 39 | return std::make_shared(poller); 40 | #else 41 | #error "Support conditional variables only," 42 | " set MYFRAME_USE_CV to enable" 43 | #endif 44 | #else 45 | #error "Unsupported platform" 46 | #endif 47 | } 48 | 49 | } // namespace myframe 50 | -------------------------------------------------------------------------------- /myframe/actor_context_manager.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "myframe/macros.h" 20 | 21 | namespace myframe { 22 | class Msg; 23 | class ActorContext; 24 | class ActorContextManager final { 25 | public: 26 | ActorContextManager(); 27 | virtual ~ActorContextManager(); 28 | 29 | bool Add(std::shared_ptr ctx); 30 | 31 | void DispatchMsg( 32 | std::shared_ptr msg, 33 | const std::string& dst = ""); 34 | 35 | /* 获得一个待处理的actor */ 36 | std::shared_ptr GetContextWithMsg(); 37 | 38 | std::vector GetAllActorAddr(); 39 | bool HasActor(const std::string& name); 40 | 41 | /* 将有消息的actor放入链表 */ 42 | void PushContext(std::shared_ptr ctx); 43 | void ClearContext(); 44 | 45 | private: 46 | /* 获得actor名对应的actor */ 47 | std::shared_ptr GetContext(const std::string& actor_name); 48 | void PrintWaitQueue(); 49 | 50 | /// 当前注册actor数量 51 | uint32_t ctx_count_; 52 | /// 待处理actor链表 53 | std::list> wait_queue_; 54 | /// 读写锁 55 | std::shared_mutex rw_; 56 | /// key: context name, value: context 57 | std::unordered_map> ctxs_; 58 | 59 | DISALLOW_COPY_AND_ASSIGN(ActorContextManager) 60 | }; 61 | 62 | } // namespace myframe 63 | -------------------------------------------------------------------------------- /myframe/actor_context.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include "myframe/macros.h" 16 | #include "myframe/mailbox.h" 17 | 18 | namespace myframe { 19 | 20 | class App; 21 | class Msg; 22 | class Actor; 23 | class WorkerCommon; 24 | class ActorContext final { 25 | friend std::ostream& operator<<(std::ostream& out, const ActorContext& ctx); 26 | 27 | public: 28 | ActorContext(std::shared_ptr app, std::shared_ptr actor); 29 | virtual ~ActorContext(); 30 | 31 | Mailbox* GetMailbox(); 32 | 33 | int Init(const Json::Value& conf); 34 | 35 | void Proc(const std::shared_ptr& msg); 36 | 37 | void SetRuningFlag(bool in_worker) { in_worker_ = in_worker; } 38 | bool IsRuning() { return in_worker_; } 39 | 40 | void SetWaitQueueFlag(bool in_wait_queue) { in_wait_que_ = in_wait_queue; } 41 | bool IsInWaitQueue() { return in_wait_que_; } 42 | 43 | std::shared_ptr GetActor() { return actor_; } 44 | std::shared_ptr GetApp(); 45 | 46 | const Json::Value* GetConfig() const; 47 | 48 | private: 49 | Mailbox mailbox_; 50 | /* 该actor的是否在工作线程的标志 */ 51 | bool in_worker_; 52 | /* actor是否在消息队列中 */ 53 | bool in_wait_que_; 54 | Json::Value config_; 55 | 56 | std::shared_ptr actor_; 57 | std::weak_ptr app_; 58 | 59 | DISALLOW_COPY_AND_ASSIGN(ActorContext) 60 | }; 61 | 62 | std::ostream& operator<<(std::ostream& out, const ActorContext& ctx); 63 | 64 | } // namespace myframe 65 | -------------------------------------------------------------------------------- /examples/example_thread_quit.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | #include 10 | 11 | #include "myframe/log.h" 12 | #include "myframe/msg.h" 13 | #include "myframe/actor.h" 14 | #include "myframe/app.h" 15 | 16 | class ExampleThreadQuit : public myframe::Actor { 17 | public: 18 | virtual ~ExampleThreadQuit() { 19 | LOG(INFO) << "thread quit example quiting..."; 20 | th_run_.store(false); 21 | if (th_.joinable()) { 22 | th_.join(); 23 | } 24 | LOG(INFO) << "thread quit example quit"; 25 | } 26 | 27 | int Init() override { 28 | th_run_.store(true); 29 | th_ = std::thread([this](){ 30 | while (th_run_.load()) { 31 | LOG(INFO) << "thread quit example runing..."; 32 | auto app = GetApp(); 33 | auto msg = std::make_shared(); 34 | msg->SetDst(GetActorName()); 35 | msg->SetData("thread quit example"); 36 | msg->SetType("TEXT"); 37 | app->Send(std::move(msg)); 38 | std::this_thread::sleep_for(std::chrono::seconds(1)); 39 | } 40 | }); 41 | return 0; 42 | } 43 | 44 | void Proc(const std::shared_ptr& msg) override { 45 | LOG(INFO) << "recv msg: " << *msg; 46 | } 47 | 48 | private: 49 | std::atomic_bool th_run_{false}; 50 | std::thread th_; 51 | }; 52 | 53 | extern "C" MYFRAME_EXPORT std::shared_ptr actor_create( 54 | const std::string& actor_name) { 55 | if (actor_name == "ExampleThreadQuit") { 56 | return std::make_shared(); 57 | } 58 | return nullptr; 59 | } 60 | -------------------------------------------------------------------------------- /myframe/common.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #if __has_include() 16 | #include 17 | namespace stdfs = std::filesystem; 18 | #else 19 | #error "no filesystem header" 20 | #endif 21 | 22 | #include 23 | #include "myframe/export.h" 24 | 25 | namespace myframe { 26 | 27 | class MYFRAME_EXPORT Common final { 28 | public: 29 | static std::vector GetDirFiles(const std::string& conf_path); 30 | static stdfs::path GetWorkRoot(); 31 | static stdfs::path GetAbsolutePath(const std::string& flag_path); 32 | static bool IsAbsolutePath(const std::string& path); 33 | 34 | static Json::Value LoadJsonFromFile(const std::string& json_file); 35 | static Json::Value LoadJsonFromString(const std::string& json_str); 36 | 37 | static void SplitMsgName( 38 | const std::string& name, 39 | std::vector* tokens); 40 | 41 | static int SetThreadAffinity(std::thread* t, int cpu_core); 42 | static int SetThreadName(std::thread* t, const std::string& name); 43 | enum class SchedPriority : int { 44 | kLowest, 45 | kNormal, 46 | kRealtime, 47 | }; 48 | static int SetProcessSchedPriority(SchedPriority); 49 | static int SetThreadSchedPriority(std::thread*, SchedPriority); 50 | 51 | // 支持配置文件中的动态库的简略写法,比如: 52 | // libdemo.so 可以简写成 demo 53 | // demo.dll 可以简写成 demo 54 | static std::string GetLibName(const std::string& name); 55 | }; 56 | 57 | } // namespace myframe 58 | -------------------------------------------------------------------------------- /myframe/shared_library.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | #include 11 | 12 | #include "myframe/macros.h" 13 | #include "myframe/export.h" 14 | 15 | namespace myframe { 16 | 17 | class MYFRAME_EXPORT SharedLibrary { 18 | public: 19 | enum class Flags : int { 20 | // On platforms that use dlopen(), use RTLD_GLOBAL. This is the default 21 | // if no flags are given. 22 | kGlobal = 1, 23 | 24 | // On platforms that use dlopen(), use RTLD_LOCAL instead of RTLD_GLOBAL. 25 | // 26 | // Note that if this flag is specified, RTTI (including dynamic_cast and 27 | // throw) will not work for types defined in the shared library with GCC 28 | // and possibly other compilers as well. See 29 | // http://gcc.gnu.org/faq.html#dso for more information. 30 | kLocal = 2, 31 | }; 32 | 33 | SharedLibrary() = default; 34 | virtual ~SharedLibrary() = default; 35 | 36 | static std::shared_ptr Create(); 37 | 38 | virtual bool Load(const std::string& path) = 0; 39 | virtual bool Load(const std::string& path, Flags flags) = 0; 40 | 41 | virtual void Unload() = 0; 42 | 43 | virtual bool IsLoaded() = 0; 44 | 45 | virtual bool HasSymbol(const std::string& name) = 0; 46 | 47 | virtual void* GetSymbol(const std::string& name) = 0; 48 | 49 | inline const std::string& GetPath() const { return path_; } 50 | 51 | protected: 52 | inline void SetPath(const std::string& path) { path_ = path; } 53 | 54 | private: 55 | std::string path_; 56 | 57 | DISALLOW_COPY_AND_ASSIGN(SharedLibrary) 58 | }; 59 | 60 | } // namespace myframe 61 | -------------------------------------------------------------------------------- /examples/example_actor_subscribe.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "myframe/log.h" 13 | #include "myframe/msg.h" 14 | #include "myframe/actor.h" 15 | 16 | class ExampleActorPub : public myframe::Actor { 17 | public: 18 | int Init() override { 19 | Timeout("1000ms", 100); 20 | return 0; 21 | } 22 | 23 | void Proc(const std::shared_ptr& msg) override { 24 | if (msg->GetType() == "SUBSCRIBE") { 25 | pub_list_.push_back(msg->GetSrc()); 26 | return; 27 | } 28 | if (msg->GetType() == "TIMER") { 29 | auto mailbox = GetMailbox(); 30 | for (size_t i = 0; i < pub_list_.size(); ++i) { 31 | mailbox->Send(pub_list_[i], 32 | std::make_shared("pub msg")); 33 | } 34 | Timeout("1000ms", 100); 35 | } 36 | } 37 | private: 38 | std::vector pub_list_; 39 | }; 40 | 41 | class ExampleActorSub : public myframe::Actor { 42 | public: 43 | int Init() override { 44 | Subscribe("actor.ExampleActorPub.1"); 45 | return 0; 46 | } 47 | 48 | void Proc(const std::shared_ptr& msg) override { 49 | LOG(INFO) << "-----> get pub msg: " << *msg; 50 | } 51 | }; 52 | 53 | extern "C" MYFRAME_EXPORT std::shared_ptr actor_create( 54 | const std::string& actor_name) { 55 | if (actor_name == "ExampleActorPub") { 56 | return std::make_shared(); 57 | } 58 | if (actor_name == "ExampleActorSub") { 59 | return std::make_shared(); 60 | } 61 | return nullptr; 62 | } 63 | -------------------------------------------------------------------------------- /myframe/platform/poller_generic.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "myframe/log.h" 16 | #include "myframe/macros.h" 17 | #include "myframe/event.h" 18 | #include "myframe/poller.h" 19 | 20 | namespace myframe { 21 | 22 | class PollerGeneric final : public Poller { 23 | public: 24 | PollerGeneric() = default; 25 | virtual ~PollerGeneric(); 26 | 27 | bool Init() override; 28 | int Wait(std::vector* evs, int timeout_ms = 100) override; 29 | void Notify(ev_handle_t h) override; 30 | 31 | private: 32 | std::vector evs_; 33 | std::mutex mtx_; 34 | std::condition_variable cv_; 35 | 36 | DISALLOW_COPY_AND_ASSIGN(PollerGeneric) 37 | }; 38 | 39 | PollerGeneric::~PollerGeneric() { 40 | LOG(INFO) << "poller deconstruct"; 41 | } 42 | 43 | bool PollerGeneric::Init() { 44 | return true; 45 | } 46 | 47 | int PollerGeneric::Wait(std::vector* evs, int timeout_ms) { 48 | evs->clear(); 49 | using namespace std::chrono_literals; // NOLINT 50 | std::unique_lock lk(mtx_); 51 | if (timeout_ms > 0) { 52 | cv_.wait_for(lk, timeout_ms * 1ms, [this](){ return !evs_.empty(); }); 53 | } else { 54 | cv_.wait(lk, [this](){ return !evs_.empty(); }); 55 | } 56 | for (auto it = evs_.begin(); it != evs_.end(); ++it) { 57 | evs->push_back(*it); 58 | } 59 | evs_.clear(); 60 | return evs->size(); 61 | } 62 | 63 | void PollerGeneric::Notify(ev_handle_t h) { 64 | std::lock_guard lk(mtx_); 65 | evs_.push_back(h); 66 | cv_.notify_one(); 67 | } 68 | 69 | } // namespace myframe 70 | -------------------------------------------------------------------------------- /examples/example_config.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | 10 | #include "myframe/log.h" 11 | #include "myframe/msg.h" 12 | #include "myframe/actor.h" 13 | #include "myframe/worker.h" 14 | 15 | class ExampleActorConfig : public myframe::Actor { 16 | public: 17 | int Init() override { 18 | auto conf = GetConfig(); 19 | LOG(INFO) << GetActorName() << " pending queue size " 20 | << GetMailbox()->GetPendingQueueSize(); 21 | LOG(INFO) << GetActorName() << " run queue size " 22 | << GetMailbox()->GetRunQueueSize(); 23 | LOG(INFO) << GetActorName() << " conf " << conf->toStyledString(); 24 | return 0; 25 | } 26 | 27 | void Proc(const std::shared_ptr& msg) override { 28 | (void)msg; 29 | } 30 | }; 31 | 32 | class ExampleWorkerConfig : public myframe::Worker { 33 | public: 34 | ExampleWorkerConfig() {} 35 | virtual ~ExampleWorkerConfig() {} 36 | 37 | void Init() override { 38 | auto conf = GetConfig(); 39 | LOG(INFO) << GetWorkerName() << " conf " << conf->toStyledString(); 40 | } 41 | void Run() override { 42 | Stop(); 43 | } 44 | }; 45 | 46 | /* 创建actor实例函数 */ 47 | extern "C" MYFRAME_EXPORT std::shared_ptr actor_create( 48 | const std::string& actor_name) { 49 | if (actor_name == "ExampleActorConfig") { 50 | return std::make_shared(); 51 | } 52 | return nullptr; 53 | } 54 | 55 | /* 创建worker实例函数 */ 56 | extern "C" MYFRAME_EXPORT std::shared_ptr worker_create( 57 | const std::string& worker_name) { 58 | if (worker_name == "ExampleWorkerConfig") { 59 | return std::make_shared(); 60 | } 61 | return nullptr; 62 | } 63 | -------------------------------------------------------------------------------- /myframe/msg.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #include "myframe/msg.h" 9 | 10 | #include 11 | #include 12 | 13 | namespace myframe { 14 | 15 | Msg::Msg(const char* data) { 16 | SetData(data, strlen(data)); 17 | } 18 | 19 | Msg::Msg(const char* data, int len) { 20 | SetData(data, len); 21 | } 22 | 23 | Msg::Msg(const std::string& data) { 24 | SetData(data); 25 | } 26 | 27 | Msg::Msg(Msg&& o) { 28 | operator=(std::move(o)); 29 | } 30 | 31 | Msg::Msg(const Msg& o) { 32 | operator=(o); 33 | } 34 | 35 | void Msg::SetData(const char* data, unsigned int len) { 36 | data_.clear(); 37 | data_.append(data, len); 38 | } 39 | void Msg::SetData(const std::string& data) { 40 | SetData(data.data(), data.size()); 41 | } 42 | void Msg::SetAnyData(const std::any& any_data) { 43 | any_data_ = any_data; 44 | } 45 | 46 | Msg& Msg::operator=(Msg&& o) noexcept { 47 | trans_mode_ = o.trans_mode_; 48 | src_ = std::move(o.src_); 49 | dst_ = std::move(o.dst_); 50 | type_ = std::move(o.type_); 51 | desc_ = std::move(o.desc_); 52 | data_ = std::move(o.data_); 53 | any_data_ = std::move(o.any_data_); 54 | return *this; 55 | } 56 | 57 | Msg& Msg::operator=(const Msg& o) noexcept { 58 | trans_mode_ = o.trans_mode_; 59 | src_ = o.src_; 60 | dst_ = o.dst_; 61 | type_ = o.type_; 62 | desc_ = o.desc_; 63 | data_ = o.data_; 64 | any_data_ = o.any_data_; 65 | return *this; 66 | } 67 | 68 | std::ostream& operator<<(std::ostream& out, const Msg& msg) { 69 | out << "[" << msg.GetSrc() 70 | << " to " << msg.GetDst() 71 | << "] trans mode: " << static_cast(msg.GetTransMode()) 72 | << ", type: " << msg.GetType(); 73 | return out; 74 | } 75 | 76 | } // namespace myframe 77 | 78 | -------------------------------------------------------------------------------- /doc/development_guide.md: -------------------------------------------------------------------------------- 1 | # 开发手册 2 | ![myframe](/doc/pics/myframe_view.png) 3 | ## 术语介绍 4 | - Actor:基础的执行单元 5 | - 驱动类型:消息驱动(被动执行) 6 | - 并发类型:单个Actor串行执行,多个Actor并行执行 7 | - 通信方式:接收消息,发送消息 8 | 9 | - Worker:独立执行的线程,可以与框架单向通信 10 | - 驱动类型:自驱动(主动执行) 11 | - 并发类型:单个Worker串行执行,多个Worker并行执行 12 | - 通信方式:接收消息或者发送消息 13 | 14 | - Service:由任意个Actor和Worker组成,通过描述文件展现; 它描述框架需要加载的库,需要创建的实例以及实例名称。 15 | - 例如下面这个例子: 16 | ```json 17 | { 18 | "type": "library", 19 | "lib": "Hello", 20 | "actor": { 21 | "HelloActor": [ 22 | { 23 | "instance_name": "1", 24 | "instance_params": "" 25 | } 26 | ] 27 | }, 28 | "worker": { 29 | "HelloReceiver": [ 30 | { 31 | "instance_name": "1" 32 | } 33 | ], 34 | "HelloSender": [ 35 | { 36 | "instance_name": "1" 37 | } 38 | ] 39 | } 40 | } 41 | ``` 42 | - "type":"library": 服务通过库的形式提供 43 | - "lib":"Hello": 需要加载的库名称 44 | - 可以写简略库名,比如 Hello 45 | - 也可以写库的全名,比如libHello.so, Hello,dll 46 | - 创建1个actor实例,名称是 actor.HelloActor.1 47 | - 创建1个worker实例,名称是 worker.HelloReceiver.1 48 | - 创建1个worker实例,名称是 worker.HelloSender.1 49 | 50 | - Module/Component:通常代指Actor或者Worker 51 | 52 | ## 开发 53 | 组件开发模式,通过编写组件开发业务。 54 | 55 | ### 创建组件工程 56 | ```sh 57 | python path/to/myframe/bin/gen_mod_proj.py --dir="path/to/proj_dir/" --name="mod_name" 58 | ``` 59 | 60 | ### 组件工程目录说明 61 | - 源文件(template.cpp) 62 | - 提供actor/worker的使用模板,根据需求决定使用actor或者worker 63 | 64 | - 配置文件: 65 | - Template.json:Service配置 66 | 67 | ### 组件工程构建安装 68 | ```sh 69 | # 如果myframe库和依赖库安装到系统目录里,则不需要设置CMAKE_PREFIX_PATH变量 70 | cmake -S . -B build -DCMAKE_PREFIX_PATH="path/to/myframe;path/to/jsoncpp;path/to/glog" 71 | cmake --build build --config Release --target install 72 | ``` 73 | 74 | ### 运行组件 75 | ```sh 76 | /path/to/myframe/bin/launcher -p app ${组件名}.json 77 | ``` 78 | 79 | ### 日志文件 80 | ```sh 81 | path/to/myframe/log/app.INFO 82 | ``` 83 | -------------------------------------------------------------------------------- /myframe/event_manager.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "myframe/macros.h" 16 | #include "myframe/event.h" 17 | 18 | namespace myframe { 19 | 20 | class EventConnManager; 21 | class WorkerContextManager; 22 | class EventManager final { 23 | friend class EventConnManager; 24 | friend class WorkerContextManager; 25 | 26 | public: 27 | EventManager(); 28 | virtual ~EventManager(); 29 | 30 | template 31 | std::shared_ptr Get(ev_handle_t h) { 32 | std::shared_lock lk(rw_); 33 | auto p = evs_.find(h); 34 | if (p == evs_.end()) { 35 | return nullptr; 36 | } 37 | return std::dynamic_pointer_cast(p->second); 38 | } 39 | 40 | template 41 | std::shared_ptr Get(const std::string& name) { 42 | std::shared_lock lk(rw_); 43 | auto p = name_handle_map_.find(name); 44 | if (p == name_handle_map_.end()) { 45 | return nullptr; 46 | } 47 | lk.unlock(); 48 | return Get(p->second); 49 | } 50 | 51 | bool Has(const std::string& name); 52 | 53 | std::vector> Get( 54 | const std::vector&); 55 | 56 | ev_handle_t ToHandle(const std::string&); 57 | 58 | void Clear(); 59 | 60 | private: 61 | bool Add(const std::shared_ptr&); 62 | bool Del(const std::shared_ptr&); 63 | 64 | std::shared_mutex rw_; 65 | std::unordered_map name_handle_map_; 66 | std::unordered_map> evs_; 67 | 68 | DISALLOW_COPY_AND_ASSIGN(EventManager) 69 | }; 70 | 71 | } // namespace myframe 72 | -------------------------------------------------------------------------------- /myframe/event.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | #include 9 | #include 10 | 11 | #include "myframe/config.h" 12 | #include "myframe/export.h" 13 | #include "myframe/platform.h" 14 | 15 | namespace myframe { 16 | 17 | #if defined(MYFRAME_OS_LINUX) || defined(MYFRAME_OS_ANDROID) 18 | #ifdef MYFRAME_USE_CV 19 | typedef void* ev_handle_t; 20 | #else 21 | typedef int ev_handle_t; 22 | #endif 23 | #elif defined(MYFRAME_OS_WINDOWS) || defined(MYFRAME_OS_MACOSX) 24 | #ifdef MYFRAME_USE_CV 25 | typedef void* ev_handle_t; 26 | #else 27 | #error "Support conditional variables only," 28 | " set MYFRAME_USE_CV to enable" 29 | #endif 30 | #else 31 | #error "Unsupported platform" 32 | #endif 33 | 34 | class MYFRAME_EXPORT Event { 35 | public: 36 | enum class Type : int { 37 | kWorkerCommon, 38 | kWorkerTimer, 39 | kWorkerUser, 40 | kEventConn, 41 | }; 42 | 43 | Event() = default; 44 | virtual ~Event() = default; 45 | 46 | /* 事件类型 */ 47 | virtual Type GetType() const { return Type::kWorkerUser; } 48 | 49 | /* 事件句柄 */ 50 | virtual ev_handle_t GetHandle() const = 0; 51 | 52 | /* 事件名称 */ 53 | virtual std::string GetName() const = 0; 54 | 55 | #if defined(MYFRAME_OS_LINUX) || defined(MYFRAME_OS_ANDROID) 56 | #ifdef MYFRAME_USE_CV 57 | static const ev_handle_t DEFAULT_EV_HANDLE; 58 | #else 59 | static const ev_handle_t DEFAULT_EV_HANDLE{-1}; 60 | #endif 61 | #elif defined(MYFRAME_OS_WINDOWS) || defined(MYFRAME_OS_MACOSX) 62 | #ifdef MYFRAME_USE_CV 63 | static const ev_handle_t DEFAULT_EV_HANDLE; 64 | #else 65 | #error "Support conditional variables only," 66 | " set MYFRAME_USE_CV to enable" 67 | #endif 68 | #else 69 | #error "Unsupported platform" 70 | #endif 71 | }; 72 | 73 | } // namespace myframe 74 | -------------------------------------------------------------------------------- /myframe/event_conn.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include "myframe/event_conn.h" 8 | #include 9 | #include "myframe/log.h" 10 | #include "myframe/msg.h" 11 | 12 | namespace myframe { 13 | 14 | EventConn::EventConn(std::shared_ptr poller) { 15 | cmd_channel_ = CmdChannel::Create(poller); 16 | } 17 | 18 | ev_handle_t EventConn::GetHandle() const { 19 | return cmd_channel_->GetMainHandle(); 20 | } 21 | 22 | Event::Type EventConn::GetType() const { 23 | return Event::Type::kEventConn; 24 | } 25 | 26 | std::string EventConn::GetName() const { 27 | return mailbox_.Addr(); 28 | } 29 | 30 | Mailbox* EventConn::GetMailbox() { 31 | return &mailbox_; 32 | } 33 | 34 | CmdChannel* EventConn::GetCmdChannel() { 35 | return cmd_channel_.get(); 36 | } 37 | 38 | int EventConn::Send(std::shared_ptr msg) { 39 | conn_type_ = EventConn::Type::kSend; 40 | mailbox_.SendClear(); 41 | if (msg->GetSrc().empty()) { 42 | msg->SetSrc(mailbox_.Addr()); 43 | } 44 | if (msg->GetDst().empty()) { 45 | return -1; 46 | } 47 | mailbox_.Send(std::move(msg)); 48 | cmd_channel_->SendToMain(CmdChannel::Cmd::kRun); 49 | CmdChannel::Cmd cmd; 50 | return cmd_channel_->RecvFromMain(&cmd); 51 | } 52 | 53 | const std::shared_ptr EventConn::SendRequest( 54 | std::shared_ptr req) { 55 | conn_type_ = EventConn::Type::kSendReq; 56 | mailbox_.SendClear(); 57 | if (req->GetSrc().empty()) { 58 | req->SetSrc(mailbox_.Addr()); 59 | } 60 | if (req->GetDst().empty()) { 61 | return nullptr; 62 | } 63 | mailbox_.Send(std::move(req)); 64 | cmd_channel_->SendToMain(CmdChannel::Cmd::kRunWithMsg); 65 | CmdChannel::Cmd cmd; 66 | cmd_channel_->RecvFromMain(&cmd); 67 | if (mailbox_.RecvEmpty()) { 68 | return nullptr; 69 | } 70 | auto msg = mailbox_.PopRecv(); 71 | mailbox_.RecvClear(); 72 | return msg; 73 | } 74 | 75 | } // namespace myframe 76 | -------------------------------------------------------------------------------- /examples/example_trans_obj.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | 10 | #include "myframe/log.h" 11 | #include "myframe/msg.h" 12 | #include "myframe/actor.h" 13 | #include "myframe/worker.h" 14 | 15 | class TransObj { 16 | public: 17 | TransObj(const std::string& a, const std::string& b) 18 | : a_(a) 19 | , b_(b) 20 | {} 21 | 22 | int Sum() { 23 | return std::stoi(a_) + std::stoi(b_); 24 | } 25 | 26 | private: 27 | std::string a_; 28 | std::string b_; 29 | }; 30 | 31 | class ExampleActorTransObj : public myframe::Actor { 32 | public: 33 | int Init() override { 34 | return 0; 35 | } 36 | 37 | void Proc(const std::shared_ptr& msg) override { 38 | auto obj = msg->GetAnyData>(); 39 | LOG(INFO) << *msg << " res " << obj->Sum(); 40 | } 41 | }; 42 | 43 | class ExampleWorkerTransObj : public myframe::Worker { 44 | public: 45 | void Run() override { 46 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 47 | auto obj = std::make_shared( 48 | std::to_string(a_), 49 | std::to_string(b_)); 50 | auto mailbox = GetMailbox(); 51 | mailbox->Send("actor.ExampleActorTransObj.1", obj); 52 | DispatchMsg(); 53 | a_++; 54 | b_++; 55 | } 56 | private: 57 | int a_{0}; 58 | int b_{0}; 59 | }; 60 | 61 | /* 创建actor实例函数 */ 62 | extern "C" MYFRAME_EXPORT std::shared_ptr actor_create( 63 | const std::string& actor_name) { 64 | if (actor_name == "ExampleActorTransObj") { 65 | return std::make_shared(); 66 | } 67 | return nullptr; 68 | } 69 | 70 | /* 创建worker实例函数 */ 71 | extern "C" MYFRAME_EXPORT std::shared_ptr worker_create( 72 | const std::string& worker_name) { 73 | if (worker_name == "ExampleWorkerTransObj") { 74 | return std::make_shared(); 75 | } 76 | return nullptr; 77 | } 78 | -------------------------------------------------------------------------------- /myframe/worker_context_manager.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "myframe/macros.h" 19 | #include "myframe/event.h" 20 | 21 | namespace myframe { 22 | 23 | class Msg; 24 | class EventManager; 25 | class WorkerContext; 26 | class WorkerContextManager final { 27 | public: 28 | WorkerContextManager(std::shared_ptr); 29 | virtual ~WorkerContextManager(); 30 | 31 | bool Init(int warning_msg_size = 10); 32 | 33 | int WorkerSize(); 34 | 35 | bool Add(std::shared_ptr worker); 36 | void Del(std::shared_ptr worker); 37 | 38 | // 内置工作线程池 39 | int IdleWorkerSize(); 40 | std::shared_ptr FrontIdleWorker(); 41 | void PopFrontIdleWorker(); 42 | void PushBackIdleWorker(std::shared_ptr worker); 43 | 44 | // 用户工作线程 45 | void PushWaitWorker(std::shared_ptr worker); 46 | void WeakupWorker(); 47 | void DispatchWorkerMsg( 48 | std::shared_ptr msg, 49 | const std::string& dst = ""); 50 | 51 | std::vector GetAllUserWorkerAddr(); 52 | 53 | // 停止工作线程 54 | void StopAllWorker(); 55 | void WaitAllWorkerQuit(); 56 | void ClearStopWorker(); 57 | 58 | private: 59 | std::atomic warning_msg_size_{10}; 60 | /// 工作线程数(包含用户线程) 61 | std::atomic_int cur_worker_count_{0}; 62 | /// 读写锁 63 | std::shared_mutex rw_; 64 | /// 空闲线程链表 65 | std::list> idle_workers_ctx_; 66 | /// 有消息user线程 67 | std::list> weakup_workers_ctx_; 68 | /// 停止的线程列表 69 | std::list> stoped_workers_ctx_; 70 | /// 事件管理对象 71 | std::shared_ptr ev_mgr_; 72 | 73 | DISALLOW_COPY_AND_ASSIGN(WorkerContextManager) 74 | }; 75 | 76 | } // namespace myframe 77 | -------------------------------------------------------------------------------- /launcher/module_argument.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | #include 9 | #include 10 | #include "cmdline.h" 11 | #include "myframe/common.h" 12 | 13 | namespace myframe { 14 | 15 | class ModuleArgument final { 16 | public: 17 | ModuleArgument(const std::string& default_sys_conf_dir); 18 | ~ModuleArgument() = default; 19 | 20 | void ParseArgument(const int argc, char** argv); 21 | inline std::list GetConfList() const { return conf_list_; } 22 | inline std::string GetConfDir() const { return conf_dir_; } 23 | inline std::string GetLogDir() const { return log_dir_; } 24 | inline std::string GetLibDir() const { return lib_dir_; } 25 | inline std::string GetBinaryName() const { return binary_name_; } 26 | inline std::string GetProcessName() const { return process_name_; } 27 | inline std::string GetCmd() const { return cmd_; } 28 | inline int GetThreadPoolSize() const { return thread_poll_size_; } 29 | inline int GetConnEventSize() const { return conn_event_size_; } 30 | inline int GetWarningMsgSize() const { return warning_msg_size_; } 31 | inline int GetDefaultPendingQueueSize() const { 32 | return default_pending_queue_size_; 33 | } 34 | inline int GetDefaultRunQueueSize() const { 35 | return default_run_queue_size_; 36 | } 37 | inline int GetLogMaxSizeMB() const { return log_max_size_mb_; } 38 | 39 | private: 40 | bool ParseSysConf(const std::string&); 41 | 42 | int thread_poll_size_{4}; 43 | int conn_event_size_{2}; 44 | int warning_msg_size_{10}; 45 | int default_pending_queue_size_{-1}; 46 | int default_run_queue_size_{2}; 47 | int log_max_size_mb_{100}; 48 | std::string log_dir_; 49 | std::string lib_dir_; 50 | std::string conf_dir_; 51 | std::list conf_list_; 52 | stdfs::path default_sys_conf_dir_; 53 | 54 | std::string cmd_; 55 | std::string binary_name_; 56 | std::string process_name_; 57 | 58 | cmdline::parser parser_; 59 | }; 60 | 61 | } // namespace myframe 62 | -------------------------------------------------------------------------------- /myframe/actor_context.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #include "myframe/actor_context.h" 9 | 10 | #include 11 | 12 | #include "myframe/log.h" 13 | #include "myframe/actor.h" 14 | #include "myframe/app.h" 15 | #include "myframe/msg.h" 16 | 17 | namespace myframe { 18 | 19 | ActorContext::ActorContext( 20 | std::shared_ptr app, 21 | std::shared_ptr actor) 22 | : in_worker_(false) 23 | , in_wait_que_(false) 24 | , actor_(actor) 25 | , app_(app) { 26 | } 27 | 28 | ActorContext::~ActorContext() { 29 | LOG(INFO) << mailbox_.Addr() << " context deconstruct"; 30 | } 31 | 32 | std::shared_ptr ActorContext::GetApp() { return app_.lock(); } 33 | 34 | int ActorContext::Init(const Json::Value& conf) { 35 | actor_->SetContext(this); 36 | mailbox_.SetAddr(actor_->GetActorName()); 37 | config_ = conf; 38 | auto app = GetApp(); 39 | int pending_queue_size = app->GetDefaultPendingQueueSize(); 40 | int run_queue_size = app->GetDefaultRunQueueSize(); 41 | if (config_.isMember("pending_queue_size")) { 42 | pending_queue_size = config_.get("pending_queue_size", -1).asInt(); 43 | } 44 | if (config_.isMember("run_queue_size")) { 45 | run_queue_size = config_.get("run_queue_size", -1).asInt(); 46 | } 47 | mailbox_.SetPendingQueueSize(pending_queue_size); 48 | mailbox_.SetRunQueueSize(run_queue_size); 49 | LOG(INFO) << mailbox_.Addr() << " context init"; 50 | return actor_->Init(); 51 | } 52 | 53 | Mailbox* ActorContext::GetMailbox() { 54 | return &mailbox_; 55 | } 56 | 57 | void ActorContext::Proc(const std::shared_ptr& msg) { 58 | actor_->Proc(msg); 59 | } 60 | 61 | const Json::Value* ActorContext::GetConfig() const { 62 | return &config_; 63 | } 64 | 65 | std::ostream& operator<<(std::ostream& out, const ActorContext& ctx) { 66 | out << ctx.actor_->GetActorName() << ", in worker: " << ctx.in_worker_ 67 | << ", in wait queue: " << ctx.in_wait_que_; 68 | return out; 69 | } 70 | 71 | } // namespace myframe 72 | -------------------------------------------------------------------------------- /myframe/mailbox.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "myframe/export.h" 14 | 15 | namespace myframe { 16 | 17 | class Msg; 18 | class MYFRAME_EXPORT Mailbox final { 19 | friend class ActorContext; 20 | friend class ActorContextManager; 21 | friend class WorkerContext; 22 | friend class WorkerContextManager; 23 | friend class EventConn; 24 | friend class EventConnManager; 25 | friend class App; 26 | 27 | public: 28 | /// 邮箱地址 29 | const std::string& Addr() const; 30 | 31 | /// 发件箱 32 | int SendSize() const; 33 | bool SendEmpty() const; 34 | void SendClear(); 35 | void Send(std::shared_ptr msg); 36 | void Send( 37 | const std::string& dst, 38 | std::shared_ptr msg); 39 | void Send( 40 | const std::string& dst, 41 | const std::any& data); 42 | void Send(std::list>* msg_list); 43 | 44 | /// 信件处理(仅供worker调用) 45 | void MoveToRun(); 46 | bool RunEmpty() const; 47 | int RunSize() const; 48 | const std::shared_ptr PopRun(); 49 | void SetRunQueueSize(int sz); 50 | int GetRunQueueSize() const; 51 | 52 | /// 收件箱 53 | int RecvSize() const; 54 | bool RecvEmpty() const; 55 | void SetPendingQueueSize(int sz); 56 | int GetPendingQueueSize() const; 57 | 58 | private: 59 | /// 收件箱 60 | void RecvClear(); 61 | void Recv(std::shared_ptr msg); 62 | const std::shared_ptr PopRecv(); 63 | 64 | /// 设置邮箱地址 65 | void SetAddr(const std::string& addr); 66 | 67 | std::list>* GetSendList(); 68 | std::list>* GetRecvList(); 69 | 70 | std::string addr_; 71 | std::list> recv_; 72 | std::list> send_; 73 | std::list> run_; 74 | int pending_queue_size_{-1}; 75 | int run_queue_size_{-1}; 76 | }; 77 | 78 | MYFRAME_EXPORT std::ostream& operator<<( 79 | std::ostream& out, const Mailbox& mailbox); 80 | 81 | } // namespace myframe 82 | -------------------------------------------------------------------------------- /examples/example_worker_actor_interactive.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | 10 | #include "myframe/log.h" 11 | #include "myframe/actor.h" 12 | #include "myframe/msg.h" 13 | #include "myframe/worker.h" 14 | 15 | /// 回显worker发来的消息 16 | class ExampleActorInteractive : public myframe::Actor { 17 | public: 18 | int Init() override { 19 | return 0; 20 | } 21 | 22 | void Proc(const std::shared_ptr& msg) override { 23 | auto mailbox = GetMailbox(); 24 | mailbox->Send(msg->GetSrc(), 25 | std::make_shared("this is ExampleActorInteractive resp")); 26 | } 27 | }; 28 | 29 | /// 给ExampleActorInteractive发送消息,并打印收到的消息 30 | class ExampleWorkerInteractive : public myframe::Worker { 31 | public: 32 | ExampleWorkerInteractive() {} 33 | virtual ~ExampleWorkerInteractive() {} 34 | 35 | /// override Worker virtual method 36 | void Run() override { 37 | auto mailbox = GetMailbox(); 38 | auto send_msg = 39 | std::make_shared("this is ExampleWorkerInteractive req"); 40 | mailbox->Send("actor.ExampleActorInteractive.1", send_msg); 41 | if (-1 == DispatchAndWaitMsg()) { 42 | return; 43 | } 44 | while (1) { 45 | const auto msg = mailbox->PopRun(); 46 | if (msg == nullptr) { 47 | break; 48 | } 49 | LOG(INFO) << *msg << ": " << msg->GetData(); 50 | } 51 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 52 | } 53 | }; 54 | 55 | /* 创建actor实例函数 */ 56 | extern "C" MYFRAME_EXPORT std::shared_ptr actor_create( 57 | const std::string& actor_name) { 58 | if (actor_name == "ExampleActorInteractive") { 59 | return std::make_shared(); 60 | } 61 | return nullptr; 62 | } 63 | 64 | /* 创建worker实例函数 */ 65 | extern "C" MYFRAME_EXPORT std::shared_ptr worker_create( 66 | const std::string& worker_name) { 67 | if (worker_name == "ExampleWorkerInteractive") { 68 | return std::make_shared(); 69 | } 70 | return nullptr; 71 | } 72 | -------------------------------------------------------------------------------- /templates/template.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, likepeng 3 | All rights reserved. 4 | 5 | Author: likepeng 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "export.h" 13 | #include "myframe/msg.h" 14 | #include "myframe/actor.h" 15 | #include "myframe/worker.h" 16 | 17 | class @template_name@Actor : public myframe::Actor { 18 | public: 19 | int Init() override { 20 | return 0; 21 | } 22 | 23 | void Proc(const std::shared_ptr& msg) override { 24 | /* print recv msg */ 25 | std::cout << *msg << ": " << msg->GetData() << std::endl; 26 | /* resp msg */ 27 | auto mailbox = GetMailbox(); 28 | mailbox->Send(msg->GetSrc(), 29 | std::make_shared("this is template actor resp")); 30 | } 31 | }; 32 | 33 | class @template_name@Worker : public myframe::Worker { 34 | public: 35 | @template_name@Worker() {} 36 | virtual ~@template_name@Worker() {} 37 | 38 | void Run() override { 39 | /* send msg to actor.@template_name@ and recv resps msg */ 40 | auto mailbox = GetMailbox(); 41 | mailbox->Send("actor.@template_name@.@template_name@1", 42 | std::make_shared("this is template worker req")); 43 | DispatchAndWaitMsg(); 44 | while (1) { 45 | const auto msg = mailbox->PopRecv(); 46 | if (msg == nullptr) { 47 | break; 48 | } 49 | std::cout << *msg << ": " << msg->GetData() << std::endl; 50 | } 51 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 52 | } 53 | }; 54 | 55 | /* create actor instance */ 56 | extern "C" MYFRAME_SUBMODULE_EXPORT std::shared_ptr 57 | actor_create( 58 | const std::string& actor_name) { 59 | if (actor_name == "@template_name@") { 60 | return std::make_shared<@template_name@Actor>(); 61 | } 62 | return nullptr; 63 | } 64 | 65 | /* create worker instance */ 66 | extern "C" MYFRAME_SUBMODULE_EXPORT std::shared_ptr 67 | worker_create( 68 | const std::string& worker_name) { 69 | if (worker_name == "@template_name@") { 70 | return std::make_shared<@template_name@Worker>(); 71 | } 72 | return nullptr; 73 | } 74 | -------------------------------------------------------------------------------- /test/actor_run_queue_test.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "myframe/common.h" 13 | #include "myframe/log.h" 14 | #include "myframe/msg.h" 15 | #include "myframe/actor.h" 16 | #include "myframe/mod_manager.h" 17 | #include "myframe/app.h" 18 | 19 | #include "performance_test_config.h" 20 | 21 | /* 22 | 设置run_queue_size为2, 23 | 给自己一次性发送10条消息, 24 | 理论上每执行两条消息就应该让出线程资源, 25 | 下一次执行应该在另外一个线程, 26 | 可以通过打印线程号判断是否让出资源。 27 | */ 28 | class RunQueueTest : public myframe::Actor { 29 | public: 30 | int Init() override { 31 | for (int i = 0; i < send_cnt_; ++i) { 32 | auto msg = std::make_shared("hello"); 33 | GetMailbox()->Send(GetActorName(), std::move(msg)); 34 | } 35 | LOG(INFO) << "init RunQueueTest"; 36 | return 0; 37 | } 38 | 39 | void Proc(const std::shared_ptr& msg) override { 40 | LOG(INFO) << "recv " << msg->GetSrc() << ":" << msg->GetData(); 41 | recv_cnt_++; 42 | if (recv_cnt_ == send_cnt_) { 43 | GetApp()->Quit(); 44 | } 45 | } 46 | 47 | private: 48 | int recv_cnt_{0}; 49 | int send_cnt_{10}; 50 | }; 51 | 52 | int main() { 53 | auto lib_dir = 54 | myframe::Common::GetAbsolutePath(MYFRAME_LIB_DIR).string(); 55 | auto log_dir = 56 | myframe::Common::GetAbsolutePath(MYFRAME_LOG_DIR).string(); 57 | 58 | myframe::InitLog(log_dir, "actor_run_queue_test"); 59 | 60 | auto app = std::make_shared(); 61 | if (false == app->Init( 62 | lib_dir, 63 | // 线程池大小 64 | 4, 65 | // ConnectEvent池大小 66 | 2, 67 | // 接收队列最大值警告 68 | 10, 69 | // 接收队列最大值(全局) 70 | -1, 71 | // 运行队列最大值(全局) 72 | 2)) { 73 | LOG(ERROR) << "Init failed"; 74 | return -1; 75 | } 76 | 77 | // mod manager 78 | auto& mod = app->GetModManager(); 79 | 80 | // 注册echo Actor 81 | mod->RegActor("RunQueueTest", [](const std::string&) { 82 | return std::make_shared(); 83 | }); 84 | auto actor = mod->CreateActorInst("class", "RunQueueTest", "1"); 85 | app->AddActor(actor); 86 | 87 | return app->Exec(); 88 | } 89 | -------------------------------------------------------------------------------- /3rd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | if (POLICY CMP0091) 3 | cmake_policy(SET CMP0091 NEW) 4 | endif() 5 | project(myframe_deps VERSION 1.0.0) 6 | 7 | include(ExternalProject) 8 | 9 | set(DEPS_SOURCE_DIR ${CMAKE_BINARY_DIR}/src CACHE PATH "package source dir") 10 | set(DEPS_DOWNLOAD_DIR ${CMAKE_BINARY_DIR}/pkg CACHE PATH "package tgz dir") 11 | 12 | ExternalProject_Add( 13 | gflags 14 | URL https://github.com/gflags/gflags/archive/refs/tags/v2.2.2.tar.gz 15 | URL_MD5 1a865b93bacfa963201af3f75b7bd64c 16 | DOWNLOAD_NAME "gflags.tar.gz" 17 | PREFIX ${CMAKE_BINARY_DIR} 18 | DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR} 19 | SOURCE_DIR "${DEPS_SOURCE_DIR}/gflags" 20 | UPDATE_COMMAND "" 21 | PATCH_COMMAND "" 22 | CMAKE_ARGS 23 | -DBUILD_SHARED_LIBS=ON 24 | -DCMAKE_BUILD_TYPE=Release 25 | -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} 26 | -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} 27 | ) 28 | 29 | ExternalProject_Add( 30 | glog 31 | URL https://github.com/google/glog/archive/refs/tags/v0.6.0.tar.gz 32 | URL_MD5 c98a6068bc9b8ad9cebaca625ca73aa2 33 | DOWNLOAD_NAME "glog.tar.gz" 34 | PREFIX ${CMAKE_BINARY_DIR} 35 | DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR} 36 | SOURCE_DIR "${DEPS_SOURCE_DIR}/glog" 37 | UPDATE_COMMAND "" 38 | PATCH_COMMAND "" 39 | CMAKE_ARGS 40 | -DBUILD_SHARED_LIBS=ON 41 | -DCMAKE_BUILD_TYPE=Release 42 | -DWITH_PKGCONFIG=OFF 43 | -DWITH_GTEST=OFF 44 | -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} 45 | -DCMAKE_PREFIX_PATH=${CMAKE_INSTALL_PREFIX} 46 | -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} 47 | ) 48 | ExternalProject_Add_StepDependencies(glog install gflags) 49 | 50 | ExternalProject_Add( 51 | jsoncpp 52 | URL https://github.com/open-source-parsers/jsoncpp/archive/refs/tags/1.9.5.tar.gz 53 | URL_MD5 d6c8c609f2162eff373db62b90a051c7 54 | DOWNLOAD_NAME "jsoncpp.tar.gz" 55 | PREFIX ${CMAKE_BINARY_DIR} 56 | DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR} 57 | SOURCE_DIR "${DEPS_SOURCE_DIR}/jsoncpp" 58 | UPDATE_COMMAND "" 59 | PATCH_COMMAND "" 60 | CMAKE_ARGS 61 | -DBUILD_SHARED_LIBS=ON 62 | -DBUILD_STATIC_LIBS=OFF 63 | -DBUILD_OBJECT_LIBS=OFF 64 | -DCMAKE_BUILD_TYPE=Release 65 | -DJSONCPP_WITH_TESTS=OFF 66 | -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF 67 | -DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF 68 | -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} 69 | -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} 70 | ) 71 | -------------------------------------------------------------------------------- /myframe/worker_timer.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "myframe/list.h" 15 | #include "myframe/msg.h" 16 | #include "myframe/worker.h" 17 | 18 | #define TVN_BITS 6 19 | #define TVR_BITS 8 20 | #define TVN_SIZE (1 << TVN_BITS) 21 | #define TVR_SIZE (1 << TVR_BITS) 22 | #define TVN_MASK (TVN_SIZE - 1) 23 | #define TVR_MASK (TVR_SIZE - 1) 24 | 25 | #define MY_RESOLUTION_MS 10 26 | 27 | namespace myframe { 28 | 29 | class Timer final : public ListNode { 30 | friend class TimerManager; 31 | 32 | public: 33 | Timer() {} 34 | virtual ~Timer() {} 35 | 36 | std::string actor_name_; 37 | std::string timer_name_; 38 | uint32_t expire_; // interval 39 | bool run_; 40 | }; 41 | 42 | class TimerManager final { 43 | public: 44 | TimerManager(); 45 | virtual ~TimerManager(); 46 | 47 | int Timeout( 48 | const std::string& actor_name, 49 | const std::string& timer_name, 50 | int time); 51 | 52 | std::list>* Updatetime(); 53 | 54 | private: 55 | void _AddTimerNode(Timer* node); 56 | void _Updatetime(); 57 | void _Execute(); 58 | void _MoveList(int level, int idx); 59 | void _Shift(); 60 | void _Dispath(List* cur); 61 | uint64_t GetMonoTimeMs(); 62 | 63 | List tv1_[TVR_SIZE]; 64 | List tv2_[TVN_SIZE]; 65 | List tv3_[TVN_SIZE]; 66 | List tv4_[TVN_SIZE]; 67 | List tv5_[TVN_SIZE]; 68 | List* tv_[4]; 69 | 70 | uint32_t time_; 71 | uint64_t cur_point_; 72 | 73 | std::list> timeout_list_; 74 | std::mutex mtx_; 75 | }; 76 | 77 | class WorkerTimer final : public Worker { 78 | friend class App; 79 | 80 | public: 81 | WorkerTimer() = default; 82 | virtual ~WorkerTimer(); 83 | 84 | int SetTimeout( 85 | const std::string& actor_name, 86 | const std::string& timer_name, 87 | int time); 88 | 89 | void Init() override; 90 | void Run() override; 91 | void Exit() override; 92 | Event::Type GetType() override { 93 | return Event::Type::kWorkerTimer; 94 | } 95 | 96 | private: 97 | int Work(); 98 | int sleep_us_{2500}; 99 | int cur_us_{0}; 100 | int dispatch_timeout_us_{1000000}; 101 | TimerManager timer_mgr_; 102 | }; 103 | 104 | } // namespace myframe 105 | -------------------------------------------------------------------------------- /test/app_send_test.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "myframe/common.h" 13 | #include "myframe/log.h" 14 | #include "myframe/msg.h" 15 | #include "myframe/actor.h" 16 | #include "myframe/mod_manager.h" 17 | #include "myframe/app.h" 18 | 19 | #include "performance_test_config.h" 20 | 21 | class EchoActorTest : public myframe::Actor { 22 | public: 23 | int Init() override { 24 | LOG(INFO) << "init EchoActorTest"; 25 | return 0; 26 | } 27 | 28 | void Proc(const std::shared_ptr& msg) override { 29 | LOG(INFO) << "recv " << msg->GetSrc() << ":" << msg->GetData(); 30 | } 31 | }; 32 | 33 | int main() { 34 | auto lib_dir = 35 | myframe::Common::GetAbsolutePath(MYFRAME_LIB_DIR).string(); 36 | auto log_dir = 37 | myframe::Common::GetAbsolutePath(MYFRAME_LOG_DIR).string(); 38 | 39 | myframe::InitLog(log_dir, "app_send_test"); 40 | 41 | auto app = std::make_shared(); 42 | if (false == app->Init(lib_dir, 4)) { 43 | LOG(ERROR) << "Init failed"; 44 | return -1; 45 | } 46 | 47 | // mod manager 48 | auto& mod = app->GetModManager(); 49 | 50 | // 注册echo Actor 51 | mod->RegActor("EchoActorTest", [](const std::string&) { 52 | return std::make_shared(); 53 | }); 54 | auto actor = mod->CreateActorInst("class", "EchoActorTest", "1"); 55 | app->AddActor(actor); 56 | 57 | // 测试Send函数 58 | std::mutex mtx; 59 | int th_cnt = 5; 60 | int exit_th_cnt = 0; 61 | int send_cnt = 10000; 62 | std::vector th_vec; 63 | for (int i = 0; i < th_cnt; ++i) { 64 | th_vec.push_back(std::thread([&, i](){ 65 | int cnt = send_cnt; 66 | while (cnt--) { 67 | auto msg = std::make_shared( 68 | "world " + std::to_string(i)); 69 | msg->SetDst("actor.EchoActorTest.1"); 70 | app->Send(std::move(msg)); 71 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 72 | } 73 | std::lock_guard g(mtx); 74 | LOG(INFO) << "user thread " << i << " exit"; 75 | ++exit_th_cnt; 76 | if (exit_th_cnt == th_cnt) { 77 | app->Quit(); 78 | } 79 | })); 80 | } 81 | 82 | app->Exec(); 83 | for (int i = 0; i < th_cnt; ++i) { 84 | if (th_vec[i].joinable()) { 85 | th_vec[i].join(); 86 | } 87 | } 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /myframe/actor.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #include "myframe/actor.h" 9 | #include 10 | 11 | #include "myframe/log.h" 12 | #include "myframe/app.h" 13 | #include "myframe/actor_context.h" 14 | #include "myframe/worker_timer.h" 15 | #include "myframe/mailbox.h" 16 | 17 | namespace myframe { 18 | 19 | Actor::~Actor() {} 20 | 21 | void Actor::SetModName(const std::string& name) { 22 | mod_name_ = name; 23 | } 24 | 25 | const std::string& Actor::GetModName() const { 26 | return mod_name_; 27 | } 28 | 29 | Mailbox* Actor::GetMailbox() { 30 | if (ctx_ == nullptr) { 31 | return nullptr; 32 | } 33 | return ctx_->GetMailbox(); 34 | } 35 | 36 | const std::string& Actor::GetTypeName() const { return class_name_; } 37 | 38 | const std::string& Actor::GetInstName() const { return instance_name_; } 39 | 40 | const std::string Actor::GetActorName() const { 41 | return "actor." + class_name_ + "." + instance_name_; 42 | } 43 | 44 | void Actor::SetTypeName(const std::string& name) { class_name_ = name; } 45 | 46 | void Actor::SetInstName(const std::string& name) { instance_name_ = name; } 47 | 48 | int Actor::Timeout(const std::string& timer_name, int expired) { 49 | if (ctx_ == nullptr) { 50 | LOG(ERROR) << "actor context is nullptr"; 51 | return -1; 52 | } 53 | auto app = ctx_->GetApp(); 54 | if (app == nullptr) { 55 | LOG(ERROR) << "app is nullptr"; 56 | return -1; 57 | } 58 | auto timer_worker = app->GetTimerWorker(); 59 | if (timer_worker == nullptr) { 60 | LOG(ERROR) << "timer worker is nullptr"; 61 | return -1; 62 | } 63 | return timer_worker->SetTimeout(GetActorName(), timer_name, expired); 64 | } 65 | 66 | bool Actor::Subscribe( 67 | const std::string& addr, 68 | const std::string& msg_type, 69 | const Msg::TransMode mode) { 70 | if (ctx_ == nullptr) { 71 | return false; 72 | } 73 | if (addr == GetActorName()) { 74 | return false; 75 | } 76 | auto msg = std::make_shared(); 77 | msg->SetType("SUBSCRIBE"); 78 | msg->SetDesc(msg_type); 79 | msg->SetTransMode(mode); 80 | auto mailbox = ctx_->GetMailbox(); 81 | mailbox->Send(addr, std::move(msg)); 82 | return true; 83 | } 84 | 85 | void Actor::SetContext(ActorContext* c) { ctx_ = c; } 86 | 87 | const Json::Value* Actor::GetConfig() const { 88 | return ctx_->GetConfig(); 89 | } 90 | 91 | std::shared_ptr Actor::GetApp() { 92 | if (ctx_ == nullptr) { 93 | return nullptr; 94 | } 95 | return ctx_->GetApp(); 96 | } 97 | 98 | } // namespace myframe 99 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | ### configure file 4 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/performance_test_config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/performance_test_config.h @ONLY) 5 | 6 | ### link static libs 7 | if (glog_lib_type STREQUAL "STATIC_LIBRARY") 8 | link_libraries( 9 | glog::glog 10 | ) 11 | endif () 12 | if (jsoncpp_lib_type STREQUAL "STATIC_LIBRARY") 13 | link_libraries( 14 | JsonCpp::JsonCpp 15 | ) 16 | endif () 17 | if (MYFRAME_ENABLE_ASAN) 18 | add_compile_options( 19 | "$<$:-fsanitize=address>" 20 | ) 21 | add_link_options( 22 | "$<$:-fsanitize=address>" 23 | ) 24 | endif () 25 | 26 | ### test bin 27 | add_executable(hello_test hello_test.cpp) 28 | target_link_libraries(hello_test 29 | myframe 30 | ) 31 | 32 | add_executable(common_test common_test.cpp) 33 | target_link_libraries(common_test 34 | myframe 35 | ) 36 | 37 | add_executable(actor_run_queue_test actor_run_queue_test.cpp) 38 | target_link_libraries(actor_run_queue_test 39 | myframe 40 | ) 41 | 42 | add_executable(app_send_test app_send_test.cpp) 43 | target_link_libraries(app_send_test 44 | myframe 45 | ) 46 | 47 | add_executable(app_send_req_test app_send_req_test.cpp) 48 | target_link_libraries(app_send_req_test 49 | myframe 50 | ) 51 | 52 | add_executable(performance_trans1_cost_test performance_trans1_cost_test.cpp) 53 | target_link_libraries(performance_trans1_cost_test 54 | myframe 55 | ) 56 | 57 | add_executable(performance_trans10_cost_test performance_trans10_cost_test.cpp) 58 | target_link_libraries(performance_trans10_cost_test 59 | myframe 60 | ) 61 | 62 | add_executable(performance_trans1_fullspeed_test performance_trans1_fullspeed_test.cpp) 63 | target_link_libraries(performance_trans1_fullspeed_test 64 | myframe 65 | ) 66 | 67 | add_executable(performance_trans20_fullspeed_test performance_trans20_fullspeed_test.cpp) 68 | target_link_libraries(performance_trans20_fullspeed_test 69 | myframe 70 | ) 71 | 72 | add_executable(performance_trans100_fullspeed_test performance_trans100_fullspeed_test.cpp) 73 | target_link_libraries(performance_trans100_fullspeed_test 74 | myframe 75 | ) 76 | 77 | ### install 78 | INSTALL(TARGETS 79 | hello_test 80 | common_test 81 | actor_run_queue_test 82 | app_send_test 83 | app_send_req_test 84 | performance_trans1_cost_test 85 | performance_trans10_cost_test 86 | performance_trans1_fullspeed_test 87 | performance_trans20_fullspeed_test 88 | performance_trans100_fullspeed_test 89 | LIBRARY DESTINATION ${MYFRAME_LIB_DIR} 90 | ARCHIVE DESTINATION ${MYFRAME_LIB_DIR} 91 | RUNTIME DESTINATION ${MYFRAME_BIN_DIR} 92 | ) 93 | -------------------------------------------------------------------------------- /myframe/worker.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include "myframe/worker.h" 8 | 9 | #include 10 | 11 | #include "myframe/log.h" 12 | #include "myframe/msg.h" 13 | #include "myframe/worker_context.h" 14 | #include "myframe/app.h" 15 | 16 | namespace myframe { 17 | 18 | Worker::~Worker() {} 19 | 20 | const std::string Worker::GetWorkerName() const { 21 | return "worker." + class_name_ + "." + inst_name_; 22 | } 23 | const std::string& Worker::GetModName() const { return mod_name_; } 24 | const std::string& Worker::GetTypeName() const { return class_name_; } 25 | const std::string& Worker::GetInstName() const { return inst_name_; } 26 | void Worker::SetModName(const std::string& name) { mod_name_ = name; } 27 | void Worker::SetTypeName(const std::string& name) { class_name_ = name; } 28 | void Worker::SetInstName(const std::string& name) { inst_name_ = name; } 29 | 30 | void Worker::Stop() { 31 | if (ctx_ == nullptr) { 32 | return; 33 | } 34 | ctx_->Stop(); 35 | } 36 | 37 | int Worker::DispatchMsg() { 38 | auto channel = GetCmdChannel(); 39 | CmdChannel::Cmd cmd = CmdChannel::Cmd::kIdle; 40 | channel->SendToMain(cmd); 41 | auto ret = channel->RecvFromMain(&cmd); 42 | if (cmd == CmdChannel::Cmd::kQuit) { 43 | LOG(INFO) << GetWorkerName() << " recv stop msg, stoping..."; 44 | Stop(); 45 | return -1; 46 | } 47 | return ret; 48 | } 49 | 50 | int Worker::DispatchAndWaitMsg() { 51 | auto channel = GetCmdChannel(); 52 | CmdChannel::Cmd cmd = CmdChannel::Cmd::kWaitForMsg; 53 | channel->SendToMain(cmd); 54 | auto ret = channel->RecvFromMain(&cmd); 55 | if (cmd == CmdChannel::Cmd::kQuit) { 56 | LOG(INFO) << GetWorkerName() << " recv stop msg, stoping..."; 57 | Stop(); 58 | return -1; 59 | } 60 | return ret; 61 | } 62 | 63 | Mailbox* Worker::GetMailbox() { 64 | if (ctx_ == nullptr) { 65 | return nullptr; 66 | } 67 | return ctx_->GetMailbox(); 68 | } 69 | 70 | CmdChannel* Worker::GetCmdChannel() { 71 | LOG_IF(FATAL, ctx_ == nullptr) 72 | << "worker ctx is nullptr"; 73 | return ctx_->GetCmdChannel(); 74 | } 75 | 76 | const Json::Value* Worker::GetConfig() const { 77 | return ctx_->GetConfig(); 78 | } 79 | 80 | void Worker::SetContext(WorkerContext* ctx) { 81 | ctx_ = ctx; 82 | } 83 | 84 | Event::Type Worker::GetType() { 85 | return Event::Type::kWorkerUser; 86 | } 87 | 88 | std::shared_ptr Worker::GetApp() { 89 | if (ctx_ == nullptr) { 90 | return nullptr; 91 | } 92 | return ctx_->GetApp(); 93 | } 94 | 95 | } // namespace myframe 96 | -------------------------------------------------------------------------------- /myframe/mod_manager.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "myframe/export.h" 19 | #include "myframe/macros.h" 20 | #include "myframe/common.h" 21 | 22 | namespace myframe { 23 | 24 | class Actor; 25 | class Worker; 26 | class SharedLibrary; 27 | class MYFRAME_EXPORT ModManager final { 28 | public: 29 | ModManager(); 30 | virtual ~ModManager(); 31 | 32 | bool RegActor( 33 | const std::string& class_name, 34 | std::function(const std::string&)> func); 35 | 36 | bool RegWorker( 37 | const std::string& class_name, 38 | std::function(const std::string&)> func); 39 | 40 | std::shared_ptr CreateActorInst( 41 | const std::string& mod_or_class_name, 42 | const std::string& class_name, 43 | const std::string& inst_name); 44 | 45 | std::shared_ptr CreateWorkerInst( 46 | const std::string& mod_or_class_name, 47 | const std::string& class_name, 48 | const std::string& inst_name); 49 | 50 | bool LoadService( 51 | const stdfs::path& lib_dir, 52 | const Json::Value& service, 53 | std::vector>>* actors, 54 | std::vector>>* workers); 55 | 56 | private: 57 | bool LoadMod(const std::string& dl_path); 58 | 59 | bool LoadActors( 60 | const std::string& mod_name, 61 | const std::string& class_name, 62 | const Json::Value& actor_list, 63 | std::vector>>* actors); 64 | 65 | bool LoadWorkers( 66 | const std::string& mod_name, 67 | const std::string& class_name, 68 | const Json::Value& worker_list, 69 | std::vector>>* workers); 70 | 71 | std::unordered_map< 72 | std::string, std::function(const std::string&)>> 73 | class_actors_; 74 | std::unordered_map< 75 | std::string, std::function(const std::string&)>> 76 | class_workers_; 77 | std::shared_mutex class_actor_rw_; 78 | std::shared_mutex class_worker_rw_; 79 | 80 | std::unordered_map> mods_; 81 | std::shared_mutex mods_rw_; 82 | 83 | DISALLOW_COPY_AND_ASSIGN(ModManager) 84 | }; 85 | 86 | } // namespace myframe 87 | -------------------------------------------------------------------------------- /myframe/msg.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | 13 | #include "myframe/export.h" 14 | 15 | namespace myframe { 16 | 17 | class MYFRAME_EXPORT Msg final { 18 | public: 19 | enum class TransMode : int { 20 | kHybrid = 0, 21 | kIntra = 1, 22 | kDDS = 2, 23 | }; 24 | 25 | Msg() = default; 26 | Msg(const char* data); 27 | Msg(const char* data, int len); 28 | Msg(const std::string& data); 29 | Msg(Msg&& o); 30 | Msg(const Msg& o); 31 | 32 | /** 33 | * @brief 获得消息分发模式 34 | * @note 来源:actor/worker/timer 35 | * @return 消息分发模式 36 | */ 37 | TransMode GetTransMode() const { return trans_mode_; } 38 | 39 | /** 40 | * @brief 获得消息源地址 41 | * @note 来源:actor/worker/timer 42 | * @return const std::string& 源地址 43 | */ 44 | const std::string& GetSrc() const { return src_; } 45 | const std::string& GetDst() const { return dst_; } 46 | 47 | /** 48 | * @brief 消息类型 49 | * @note 目前使用到的 "TEXT", "TIMER", "SUBSCRIBE"; 50 | * 也可以自定义,用于区分传递给同一个actor的不同消息类型 51 | * @return const std::string& 消息类型 52 | */ 53 | const std::string& GetType() const { return type_; } 54 | 55 | /** 56 | * @brief 消息描述 57 | * @note 目前timer使用到该函数,见 Actor::Timeout() 58 | * 59 | * @return const std::string& 消息描述 60 | */ 61 | const std::string& GetDesc() const { return desc_; } 62 | 63 | /** 64 | * @brief 数据 65 | * 66 | * @return const std::string& 数据 67 | */ 68 | const std::string& GetData() const { return data_; } 69 | template 70 | const T GetAnyData() const { 71 | return std::any_cast(any_data_); 72 | } 73 | 74 | void SetTransMode(TransMode tans_mode) { trans_mode_ = tans_mode; } 75 | void SetSrc(const std::string& src) { src_ = src; } 76 | void SetDst(const std::string& dst) { dst_ = dst; } 77 | void SetType(const std::string& type) { type_ = type; } 78 | void SetDesc(const std::string& desc) { desc_ = desc; } 79 | void SetData(const char* data, unsigned int len); 80 | void SetData(const std::string& data); 81 | void SetAnyData(const std::any& any_data); 82 | 83 | Msg& operator=(Msg&& o) noexcept; 84 | Msg& operator=(const Msg& o) noexcept; 85 | 86 | private: 87 | std::string src_; 88 | std::string dst_; 89 | std::string type_; 90 | std::string desc_; 91 | std::string data_; 92 | std::any any_data_; 93 | TransMode trans_mode_{TransMode::kIntra}; 94 | }; 95 | 96 | MYFRAME_EXPORT std::ostream& operator<<(std::ostream& out, const Msg& msg); 97 | 98 | } // namespace myframe 99 | -------------------------------------------------------------------------------- /examples/example_actor_concurrent.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "myframe/log.h" 13 | #include "myframe/msg.h" 14 | #include "myframe/actor.h" 15 | 16 | class ExampleActorConcurrent : public myframe::Actor { 17 | public: 18 | int random(int min, int max) { 19 | std::random_device seed; 20 | std::ranlux48 engine(seed()); 21 | std::uniform_int_distribution<> distrib(min, max); 22 | return distrib(engine); 23 | } 24 | 25 | int Init() override { 26 | return 0; 27 | } 28 | 29 | void Proc(const std::shared_ptr& msg) override { 30 | if (msg->GetSrc() == "actor.ExampleActorConcurrentTrigger.1") { 31 | int cost_ms = random(100, 500); 32 | LOG(INFO) << "-----> " << GetActorName() << " begin runing..."; 33 | std::this_thread::sleep_for(std::chrono::milliseconds(cost_ms)); 34 | LOG(INFO) << "-----> " << GetActorName() << " process end, cost " 35 | << cost_ms << " ms"; 36 | auto mailbox = GetMailbox(); 37 | mailbox->Send(msg->GetSrc(), std::make_shared("")); 38 | } 39 | } 40 | }; 41 | 42 | class ExampleActorConcurrentTrigger : public myframe::Actor { 43 | public: 44 | int Init() override { 45 | state_ = { 46 | {"actor.ExampleActorConcurrent.1", false}, 47 | {"actor.ExampleActorConcurrent.2", false}, 48 | {"actor.ExampleActorConcurrent.3", false}, 49 | }; 50 | LOG(INFO) << "begin concurrent task..."; 51 | for (auto it : state_) { 52 | auto mailbox = GetMailbox(); 53 | mailbox->Send(it.first, std::make_shared("")); 54 | } 55 | return 0; 56 | } 57 | 58 | bool WaitEnd(const std::string& name) { 59 | state_[name] = true; 60 | for (auto it : state_) { 61 | if (it.second == false) { 62 | return false; 63 | } 64 | } 65 | return true; 66 | } 67 | 68 | void Proc(const std::shared_ptr& msg) override { 69 | if (WaitEnd(msg->GetSrc())) { 70 | LOG(INFO) << "concurrent task finished"; 71 | } 72 | } 73 | 74 | private: 75 | std::unordered_map state_; 76 | }; 77 | 78 | extern "C" MYFRAME_EXPORT std::shared_ptr actor_create( 79 | const std::string& actor_name) { 80 | if (actor_name == "ExampleActorConcurrent") { 81 | return std::make_shared(); 82 | } 83 | if (actor_name == "ExampleActorConcurrentTrigger") { 84 | return std::make_shared(); 85 | } 86 | return nullptr; 87 | } 88 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | if (POLICY CMP0091) 3 | cmake_policy(SET CMP0091 NEW) 4 | endif() 5 | project(myframe 6 | VERSION 0.9.9 7 | LANGUAGES CXX 8 | ) 9 | 10 | ### option 11 | option(MYFRAME_USE_CV "Using conditional variables for thread communication" ON) 12 | option(MYFRAME_GENERATE_EXAMPLE "Generate example library" ON) 13 | option(MYFRAME_GENERATE_TEST "Generate test executable program" ON) 14 | option(MYFRAME_INSTALL_TEMPLATE "Install template project" ON) 15 | option(MYFRAME_INSTALL_LAUNCHER "Install launcher program" ON) 16 | option(MYFRAME_ENABLE_ASAN "Enable ASAN" OFF) 17 | 18 | ### cmake module 19 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") 20 | 21 | ### compile option 22 | set(CMAKE_C_VISIBILITY_PRESET hidden) 23 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 24 | set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) 25 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 26 | if (CMAKE_CXX_STANDARD_REQUIRED) 27 | message(STATUS "Set cxx standard ${CMAKE_CXX_STANDARD}") 28 | else() 29 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 30 | set(CMAKE_CXX_STANDARD 17) 31 | message(STATUS "Set default cxx standard 17") 32 | endif() 33 | add_compile_options("$<$:/source-charset:utf-8>") 34 | if (MSVC) 35 | add_definitions( 36 | -wd4251 37 | ) 38 | endif (MSVC) 39 | 40 | ### install path 41 | if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 42 | message(STATUS "Set default install prefix ${CMAKE_INSTALL_PREFIX}") 43 | else() 44 | message(STATUS "Set install prefix ${CMAKE_INSTALL_PREFIX}") 45 | endif() 46 | set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") 47 | set(MYFRAME_BIN_DIR "bin") 48 | set(MYFRAME_INC_DIR "include") 49 | set(MYFRAME_LIB_DIR "lib") 50 | set(MYFRAME_LOG_DIR "log") 51 | set(MYFRAME_SERVICE_DIR "service") 52 | set(MYFRAME_CONF_DIR "conf") 53 | 54 | ### deps libs 55 | find_package(Threads REQUIRED) 56 | find_package(jsoncpp REQUIRED) 57 | find_package(glog REQUIRED) 58 | 59 | get_target_property(glog_lib_type glog::glog TYPE) 60 | get_target_property(jsoncpp_lib_type JsonCpp::JsonCpp TYPE) 61 | 62 | ### sub directory 63 | add_subdirectory(myframe) 64 | if (MYFRAME_INSTALL_LAUNCHER) 65 | add_subdirectory(launcher) 66 | endif() 67 | if (MYFRAME_GENERATE_EXAMPLE) 68 | add_subdirectory(examples) 69 | endif() 70 | if (MYFRAME_GENERATE_TEST) 71 | add_subdirectory(test) 72 | endif() 73 | 74 | ### install file/dir 75 | install(FILES 76 | "LICENSE" 77 | PERMISSIONS 78 | OWNER_READ OWNER_WRITE 79 | GROUP_READ 80 | WORLD_READ 81 | DESTINATION . 82 | ) 83 | if (MYFRAME_INSTALL_TEMPLATE) 84 | install(PROGRAMS 85 | "tools/gen_mod_proj.py" 86 | DESTINATION ${MYFRAME_BIN_DIR} 87 | ) 88 | install(DIRECTORY templates DESTINATION .) 89 | endif() 90 | install(DIRECTORY DESTINATION ${MYFRAME_LOG_DIR}) 91 | install(DIRECTORY DESTINATION ${MYFRAME_SERVICE_DIR}) 92 | 93 | ### package 94 | include(Packing) 95 | -------------------------------------------------------------------------------- /myframe/event_conn_manager.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #include "myframe/event_conn_manager.h" 9 | #include 10 | #include "myframe/log.h" 11 | #include "myframe/event_conn.h" 12 | #include "myframe/event_manager.h" 13 | 14 | namespace myframe { 15 | 16 | EventConnManager::EventConnManager( 17 | std::shared_ptr ev_mgr, 18 | std::shared_ptr poller) 19 | : ev_mgr_(ev_mgr) { 20 | poller_ = poller; 21 | LOG(INFO) << "EventConnManager create"; 22 | } 23 | 24 | EventConnManager::~EventConnManager() { 25 | LOG(INFO) << "EventConnManager deconstruct"; 26 | } 27 | 28 | bool EventConnManager::Init(int sz) { 29 | for (int i = 0; i < sz; ++i) { 30 | std::lock_guard g(mtx_); 31 | AddEventConn(); 32 | } 33 | return true; 34 | } 35 | 36 | void EventConnManager::AddEventConn() { 37 | auto conn = std::make_shared(poller_); 38 | std::string name = "event.conn." + std::to_string(conn_sz_); 39 | conn->GetMailbox()->SetAddr(name); 40 | idle_conn_.push_back(std::move(conn)); 41 | conn_sz_++; 42 | } 43 | 44 | std::shared_ptr EventConnManager::Alloc() { 45 | std::lock_guard g(mtx_); 46 | // check has event conn 47 | if (idle_conn_.empty()) { 48 | AddEventConn(); 49 | } 50 | // remove from idle_conn 51 | auto conn = idle_conn_.front(); 52 | idle_conn_.pop_front(); 53 | // add to run_conn 54 | if (!ev_mgr_->Add(conn)) { 55 | return nullptr; 56 | } 57 | return conn; 58 | } 59 | 60 | void EventConnManager::Release(std::shared_ptr ev) { 61 | // remove from run_conn 62 | if (!ev_mgr_->Del(ev)) { 63 | return; 64 | } 65 | // add to idle_conn 66 | std::lock_guard g(mtx_); 67 | idle_conn_.push_back(std::move(ev)); 68 | } 69 | 70 | // call by main frame 71 | void EventConnManager::Notify( 72 | ev_handle_t h, 73 | std::shared_ptr msg) { 74 | std::shared_ptr ev = nullptr; 75 | ev = ev_mgr_->Get(h); 76 | if (ev == nullptr) { 77 | LOG(ERROR) << "can't find handle " << h; 78 | return; 79 | } 80 | if (ev->GetConnType() == EventConn::Type::kSend) { 81 | LOG(WARNING) << "event " << ev->GetName() << " need't resp msg"; 82 | return; 83 | } 84 | // need release immediately 85 | Release(ev); 86 | // push msg to event_conn 87 | ev->GetMailbox()->Recv(std::move(msg)); 88 | // send cmd to event_conn 89 | auto cmd_channel = ev->GetCmdChannel(); 90 | cmd_channel->SendToOwner(CmdChannel::Cmd::kIdle); 91 | } 92 | 93 | void EventConnManager::Clear() { 94 | std::lock_guard g(mtx_); 95 | idle_conn_.clear(); 96 | } 97 | 98 | } // namespace myframe 99 | -------------------------------------------------------------------------------- /myframe/worker_context.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "myframe/macros.h" 18 | #include "myframe/event.h" 19 | #include "myframe/mailbox.h" 20 | #include "myframe/cmd_channel.h" 21 | 22 | namespace myframe { 23 | 24 | class App; 25 | class Worker; 26 | class WorkerContext final : public Event { 27 | public: 28 | enum class CtrlOwner : int { 29 | kMain, 30 | kWorker, 31 | }; 32 | 33 | WorkerContext( 34 | std::shared_ptr app, 35 | std::shared_ptr worker, 36 | std::shared_ptr poller); 37 | virtual ~WorkerContext(); 38 | 39 | bool Init(const Json::Value& conf); 40 | 41 | /// thread 相关函数 42 | void Start(); 43 | void Stop(); 44 | void Join(); 45 | bool IsRuning() { return runing_.load(); } 46 | std::thread::id GetThreadId() { return th_.get_id(); } 47 | bool SetThreadAffinity(int cpu_core); 48 | 49 | /// event 相关函数 50 | ev_handle_t GetHandle() const override; 51 | Event::Type GetType() const override; 52 | std::string GetName() const override; 53 | 54 | Mailbox* GetMailbox(); 55 | 56 | CmdChannel* GetCmdChannel(); 57 | 58 | template 59 | std::shared_ptr GetWorker() const { 60 | return std::dynamic_pointer_cast(worker_); 61 | } 62 | 63 | /// 线程交互控制flag函数 64 | void SetCtrlOwnerFlag(CtrlOwner owner) { 65 | ctrl_owner_ = owner; 66 | } 67 | CtrlOwner GetOwner() const { 68 | return ctrl_owner_; 69 | } 70 | void SetWaitMsgQueueFlag(bool in_wait_msg_queue) { 71 | in_msg_wait_queue_ = in_wait_msg_queue; 72 | } 73 | bool IsInWaitMsgQueue() { 74 | return in_msg_wait_queue_; 75 | } 76 | 77 | std::shared_ptr GetApp(); 78 | 79 | const Json::Value* GetConfig() const; 80 | 81 | private: 82 | void ListenThread(); 83 | void Initialize(); 84 | 85 | /// state flag 86 | std::atomic_bool runing_; 87 | CtrlOwner ctrl_owner_{ CtrlOwner::kWorker }; 88 | bool in_msg_wait_queue_{ false }; 89 | 90 | /// config 91 | Json::Value config_; 92 | 93 | /// worker 94 | std::shared_ptr worker_; 95 | std::weak_ptr app_; 96 | 97 | /// mailbox 98 | Mailbox mailbox_; 99 | 100 | /// cmd channel 101 | std::shared_ptr cmd_channel_; 102 | 103 | /// thread 104 | std::thread th_; 105 | 106 | DISALLOW_COPY_AND_ASSIGN(WorkerContext) 107 | }; 108 | 109 | std::ostream& operator<<(std::ostream& out, WorkerContext& ctx); 110 | 111 | } // namespace myframe 112 | -------------------------------------------------------------------------------- /myframe/platform/shared_library_win.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #include "myframe/log.h" 17 | #include "myframe/macros.h" 18 | #include "myframe/shared_library.h" 19 | 20 | namespace myframe { 21 | 22 | class SharedLibraryWin final : public SharedLibrary { 23 | public: 24 | SharedLibraryWin() = default; 25 | virtual ~SharedLibraryWin(); 26 | 27 | bool Load(const std::string& path) override; 28 | bool Load(const std::string& path, Flags flags) override; 29 | 30 | void Unload() override; 31 | 32 | bool IsLoaded() override; 33 | 34 | bool HasSymbol(const std::string& name) override; 35 | 36 | void* GetSymbol(const std::string& name) override; 37 | 38 | private: 39 | HMODULE handle_{ nullptr }; 40 | std::mutex mutex_; 41 | 42 | DISALLOW_COPY_AND_ASSIGN(SharedLibraryWin) 43 | }; 44 | 45 | SharedLibraryWin::~SharedLibraryWin() { 46 | Unload(); 47 | } 48 | 49 | bool SharedLibraryWin::Load(const std::string& path) { 50 | return Load(path, Flags::kGlobal); 51 | } 52 | 53 | bool SharedLibraryWin::Load( 54 | const std::string& path, 55 | Flags flags) { 56 | (void)flags; 57 | std::lock_guard lock(mutex_); 58 | if (handle_ != nullptr) { 59 | return false; 60 | } 61 | handle_ = LoadLibrary(path.c_str()); 62 | if (handle_ == nullptr) { 63 | LOG(ERROR) << "Open dll " << path << " failed"; 64 | FreeLibrary(handle_); 65 | return false; 66 | } 67 | SetPath(path); 68 | return true; 69 | } 70 | 71 | void SharedLibraryWin::Unload() { 72 | std::lock_guard lock(mutex_); 73 | if (handle_ == nullptr) { 74 | return; 75 | } 76 | if (!FreeLibrary(handle_)) { 77 | LOG(ERROR) << "lib " << GetPath() << " close failed"; 78 | } 79 | handle_ = nullptr; 80 | } 81 | 82 | bool SharedLibraryWin::IsLoaded() { 83 | std::lock_guard lock(mutex_); 84 | return handle_ != nullptr; 85 | } 86 | 87 | bool SharedLibraryWin::HasSymbol(const std::string& name) { 88 | return GetSymbol(name) != nullptr; 89 | } 90 | 91 | void* SharedLibraryWin::GetSymbol(const std::string& name) { 92 | std::lock_guard lock(mutex_); 93 | if (handle_ == nullptr) { 94 | return nullptr; 95 | } 96 | 97 | void* result = GetProcAddress(handle_, name.c_str()); 98 | if (result == nullptr) { 99 | LOG(ERROR) << "lib " << GetPath() 100 | << " has no symbol " << name; 101 | return nullptr; 102 | } 103 | return result; 104 | } 105 | 106 | } // namespace myframe 107 | -------------------------------------------------------------------------------- /myframe/event_manager.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #include "myframe/event_manager.h" 9 | 10 | #include "myframe/log.h" 11 | #include "myframe/worker.h" 12 | #include "myframe/event_conn.h" 13 | 14 | namespace myframe { 15 | 16 | EventManager::EventManager() { 17 | LOG(INFO) << "EventManager create"; 18 | } 19 | 20 | EventManager::~EventManager() { 21 | LOG(INFO) << "EventManager deconstruct"; 22 | std::unique_lock lk(rw_); 23 | name_handle_map_.clear(); 24 | evs_.clear(); 25 | } 26 | 27 | ev_handle_t EventManager::ToHandle(const std::string& name) { 28 | std::shared_lock lk(rw_); 29 | auto p = name_handle_map_.find(name); 30 | if (p == name_handle_map_.end()) { 31 | return Event::DEFAULT_EV_HANDLE; 32 | } 33 | return p->second; 34 | } 35 | 36 | bool EventManager::Has(const std::string& name) { 37 | std::shared_lock lk(rw_); 38 | return name_handle_map_.find(name) != name_handle_map_.end(); 39 | } 40 | 41 | std::vector> EventManager::Get( 42 | const std::vector& type_list) { 43 | std::shared_lock lk(rw_); 44 | std::vector> tmp_evs; 45 | for (auto it = evs_.begin(); it != evs_.end(); ++it) { 46 | for (size_t i = 0; i < type_list.size(); ++i) { 47 | if (type_list[i] == it->second->GetType()) { 48 | tmp_evs.push_back(it->second); 49 | break; 50 | } 51 | } 52 | } 53 | return tmp_evs; 54 | } 55 | 56 | bool EventManager::Add(const std::shared_ptr& ev) { 57 | auto handle = ev->GetHandle(); 58 | std::unique_lock lk(rw_); 59 | if (evs_.find(handle) != evs_.end()) { 60 | LOG(ERROR) << " add handle " << handle << " has exist"; 61 | return false; 62 | } 63 | evs_[handle] = ev; 64 | name_handle_map_[ev->GetName()] = handle; 65 | return true; 66 | } 67 | 68 | bool EventManager::Del(const std::shared_ptr& ev) { 69 | auto handle = ev->GetHandle(); 70 | std::unique_lock lk(rw_); 71 | if (evs_.find(handle) == evs_.end()) { 72 | LOG(ERROR) << " del handle " << handle << " has exist"; 73 | return false; 74 | } 75 | evs_.erase(handle); 76 | name_handle_map_.erase(ev->GetName()); 77 | return true; 78 | } 79 | 80 | void EventManager::Clear() { 81 | std::unique_lock lk(rw_); 82 | name_handle_map_.clear(); 83 | // notify all connevent quit 84 | for (auto& p : evs_) { 85 | if (p.second->GetType() == Event::Type::kEventConn) { 86 | auto ev_conn = std::dynamic_pointer_cast(p.second); 87 | auto cmd_channel = ev_conn->GetCmdChannel(); 88 | cmd_channel->SendToOwner(CmdChannel::Cmd::kQuit); 89 | } 90 | } 91 | evs_.clear(); 92 | } 93 | 94 | } // namespace myframe 95 | -------------------------------------------------------------------------------- /myframe/platform/shared_library_linux.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "myframe/log.h" 17 | #include "myframe/macros.h" 18 | #include "myframe/shared_library.h" 19 | 20 | namespace myframe { 21 | 22 | class SharedLibraryLinux final : public SharedLibrary { 23 | public: 24 | SharedLibraryLinux() = default; 25 | virtual ~SharedLibraryLinux(); 26 | 27 | bool Load(const std::string& path) override; 28 | bool Load(const std::string& path, Flags flags) override; 29 | 30 | void Unload() override; 31 | 32 | bool IsLoaded() override; 33 | 34 | bool HasSymbol(const std::string& name) override; 35 | 36 | void* GetSymbol(const std::string& name) override; 37 | 38 | private: 39 | void* handle_{ nullptr }; 40 | std::mutex mutex_; 41 | 42 | DISALLOW_COPY_AND_ASSIGN(SharedLibraryLinux) 43 | }; 44 | 45 | SharedLibraryLinux::~SharedLibraryLinux() { 46 | Unload(); 47 | } 48 | 49 | bool SharedLibraryLinux::Load(const std::string& path) { 50 | return Load(path, Flags::kGlobal); 51 | } 52 | 53 | bool SharedLibraryLinux::Load( 54 | const std::string& path, 55 | Flags flags) { 56 | std::lock_guard lock(mutex_); 57 | if (handle_ != nullptr) { 58 | return false; 59 | } 60 | int real_flag = RTLD_NOW; 61 | if (static_cast(flags) & static_cast(Flags::kLocal)) { 62 | real_flag |= RTLD_LOCAL; 63 | } else { 64 | real_flag |= RTLD_GLOBAL; 65 | } 66 | handle_ = dlopen(path.c_str(), real_flag); 67 | if (handle_ == nullptr) { 68 | LOG(ERROR) << "Open dll " << path << " failed, " << dlerror(); 69 | return false; 70 | } 71 | SetPath(path); 72 | return true; 73 | } 74 | 75 | void SharedLibraryLinux::Unload() { 76 | std::lock_guard lock(mutex_); 77 | if (handle_ == nullptr) { 78 | return; 79 | } 80 | if (dlclose(handle_)) { 81 | LOG(ERROR) << "lib " << GetPath() << " close failed, " << dlerror(); 82 | } 83 | handle_ = nullptr; 84 | } 85 | 86 | bool SharedLibraryLinux::IsLoaded() { 87 | std::lock_guard lock(mutex_); 88 | return handle_ != nullptr; 89 | } 90 | 91 | bool SharedLibraryLinux::HasSymbol(const std::string& name) { 92 | return GetSymbol(name) != nullptr; 93 | } 94 | 95 | void* SharedLibraryLinux::GetSymbol(const std::string& name) { 96 | std::lock_guard lock(mutex_); 97 | if (handle_ == nullptr) { 98 | return nullptr; 99 | } 100 | 101 | void* result = dlsym(handle_, name.c_str()); 102 | if (result == nullptr) { 103 | LOG(ERROR) << "lib " << GetPath() 104 | << " has no symbol " << name << ", " << dlerror(); 105 | return nullptr; 106 | } 107 | return result; 108 | } 109 | 110 | } // namespace myframe 111 | -------------------------------------------------------------------------------- /myframe/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | ### config 4 | configure_file (${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_SOURCE_DIR}/config.h) 5 | 6 | ### source 7 | aux_source_directory(. __srcs) 8 | 9 | ### lib 10 | add_library(${PROJECT_NAME} ${__srcs}) 11 | target_include_directories(${PROJECT_NAME} 12 | PUBLIC 13 | $ 14 | INTERFACE 15 | $ 16 | ) 17 | target_link_libraries(${PROJECT_NAME} 18 | PRIVATE 19 | ${CMAKE_DL_LIBS} 20 | PUBLIC 21 | Threads::Threads 22 | ) 23 | 24 | if (glog_lib_type STREQUAL "STATIC_LIBRARY") 25 | target_link_libraries(${PROJECT_NAME} 26 | PRIVATE 27 | glog::glog 28 | ) 29 | else () 30 | set(glog_DEPENDENCY "find_dependency (glog)") 31 | target_link_libraries(${PROJECT_NAME} 32 | PUBLIC 33 | glog::glog 34 | ) 35 | endif () 36 | if (jsoncpp_lib_type STREQUAL "STATIC_LIBRARY") 37 | target_link_libraries(${PROJECT_NAME} 38 | PRIVATE 39 | JsonCpp::JsonCpp 40 | ) 41 | else () 42 | set(jsoncpp_DEPENDENCY "find_dependency (jsoncpp)") 43 | target_link_libraries(${PROJECT_NAME} 44 | PUBLIC 45 | JsonCpp::JsonCpp 46 | ) 47 | endif () 48 | 49 | if (glog_lib_type STREQUAL "STATIC_LIBRARY" OR jsoncpp_lib_type STREQUAL "STATIC_LIBRARY") 50 | get_target_property(${PROJECT_NAME}_lib_type ${PROJECT_NAME} TYPE) 51 | if (${PROJECT_NAME}_lib_type STREQUAL "SHARED_LIBRARY") 52 | target_link_options(${PROJECT_NAME} 53 | PRIVATE 54 | "$<$:LINKER:--exclude-libs,ALL>" 55 | ) 56 | endif () 57 | endif () 58 | 59 | if (MYFRAME_ENABLE_ASAN) 60 | target_compile_options(${PROJECT_NAME} 61 | PRIVATE 62 | "$<$:-fsanitize=address>" 63 | ) 64 | target_link_options(${PROJECT_NAME} 65 | PRIVATE 66 | "$<$:-fsanitize=address>" 67 | ) 68 | endif () 69 | 70 | set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION}) 71 | set_target_properties(${PROJECT_NAME} PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR}) 72 | 73 | # export file 74 | include (GenerateExportHeader) 75 | generate_export_header (${PROJECT_NAME} 76 | EXPORT_MACRO_NAME MYFRAME_EXPORT 77 | EXPORT_FILE_NAME "${CMAKE_CURRENT_SOURCE_DIR}/export.h" 78 | ) 79 | 80 | ### install 81 | file(GLOB header_files 82 | config.h 83 | platform.h 84 | export.h 85 | macros.h 86 | common.h 87 | log.h 88 | msg.h 89 | mailbox.h 90 | cmd_channel.h 91 | poller.h 92 | actor.h 93 | event.h 94 | worker.h 95 | mod_manager.h 96 | shared_library.h 97 | app.h 98 | ) 99 | install(FILES 100 | ${header_files} 101 | PERMISSIONS 102 | OWNER_READ OWNER_WRITE 103 | GROUP_READ 104 | WORLD_READ 105 | DESTINATION ${MYFRAME_INC_DIR}/${PROJECT_NAME} 106 | ) 107 | install(TARGETS ${PROJECT_NAME} 108 | EXPORT "${PROJECT_NAME}Targets" 109 | LIBRARY DESTINATION ${MYFRAME_LIB_DIR} 110 | ARCHIVE DESTINATION ${MYFRAME_LIB_DIR} 111 | RUNTIME DESTINATION ${MYFRAME_BIN_DIR} 112 | ) 113 | 114 | include(InstallingConfigs) 115 | -------------------------------------------------------------------------------- /examples/example_actor_serial.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "myframe/log.h" 13 | #include "myframe/msg.h" 14 | #include "myframe/actor.h" 15 | 16 | int random(int min, int max) { 17 | std::random_device seed; 18 | std::ranlux48 engine(seed()); 19 | std::uniform_int_distribution<> distrib(min, max); 20 | return distrib(engine); 21 | } 22 | 23 | class ExampleActorSerial1 : public myframe::Actor { 24 | public: 25 | int Init() override { 26 | auto mailbox = GetMailbox(); 27 | mailbox->Send("actor.ExampleActorSerial1.1", 28 | std::make_shared("")); 29 | return 0; 30 | } 31 | 32 | void Proc(const std::shared_ptr& msg) override { 33 | (void)msg; 34 | int cost_ms = random(100, 500); 35 | LOG(INFO) << "-----> begin runing task " << GetActorName() << "..."; 36 | std::this_thread::sleep_for(std::chrono::milliseconds(cost_ms)); 37 | LOG(INFO) << "-----> " << GetActorName() << " process end, cost " << cost_ms 38 | << " ms"; 39 | auto mailbox = GetMailbox(); 40 | mailbox->Send("actor.ExampleActorSerial2.1", 41 | std::make_shared("")); 42 | } 43 | }; 44 | 45 | class ExampleActorSerial2 : public myframe::Actor { 46 | public: 47 | int Init() override { 48 | return 0; 49 | } 50 | 51 | void Proc(const std::shared_ptr& msg) override { 52 | (void)msg; 53 | int cost_ms = random(100, 500); 54 | LOG(INFO) << "-----> begin runing task " << GetActorName() << "..."; 55 | std::this_thread::sleep_for(std::chrono::milliseconds(cost_ms)); 56 | LOG(INFO) << "-----> " << GetActorName() << " process end, cost " << cost_ms 57 | << " ms"; 58 | auto mailbox = GetMailbox(); 59 | mailbox->Send("actor.ExampleActorSerial3.1", 60 | std::make_shared("")); 61 | } 62 | }; 63 | 64 | class ExampleActorSerial3 : public myframe::Actor { 65 | public: 66 | int Init() override { 67 | return 0; 68 | } 69 | 70 | void Proc(const std::shared_ptr& msg) override { 71 | (void)msg; 72 | int cost_ms = random(100, 500); 73 | LOG(INFO) << "-----> begin runing task " << GetActorName() << "..."; 74 | std::this_thread::sleep_for(std::chrono::milliseconds(cost_ms)); 75 | LOG(INFO) << "-----> " << GetActorName() << " process end, cost " << cost_ms 76 | << " ms"; 77 | } 78 | }; 79 | 80 | extern "C" MYFRAME_EXPORT std::shared_ptr actor_create( 81 | const std::string& actor_name) { 82 | if (actor_name == "ExampleActorSerial1") { 83 | return std::make_shared(); 84 | } 85 | if (actor_name == "ExampleActorSerial2") { 86 | return std::make_shared(); 87 | } 88 | if (actor_name == "ExampleActorSerial3") { 89 | return std::make_shared(); 90 | } 91 | return nullptr; 92 | } 93 | -------------------------------------------------------------------------------- /myframe/list.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #include "myframe/list.h" 9 | 10 | namespace myframe { 11 | 12 | // init 13 | void List::__Init() { 14 | root_.prev = root_.next = &root_; 15 | count_ = 0; 16 | } 17 | 18 | // add 19 | void List::__Add(ListNode* prev, ListNode* next, ListNode* node) { 20 | if (!node) return; 21 | ++count_; 22 | prev->next = node; 23 | next->prev = node; 24 | node->prev = prev; 25 | node->next = next; 26 | } 27 | 28 | void List::AddHead(ListNode* node) { __Add(&root_, root_.next, node); } 29 | 30 | void List::AddTail(ListNode* node) { __Add(root_.prev, &root_, node); } 31 | 32 | // del 33 | void List::__Del(ListNode* prev, ListNode* next, bool b) { 34 | --count_; 35 | if (b) delete prev->next; 36 | prev->next = next; 37 | next->prev = prev; 38 | } 39 | 40 | void List::Del(ListNode* node, bool b) { 41 | __Del(node->prev, node->next, b); 42 | node->prev = node->next = node; 43 | } 44 | 45 | void List::DelHead(bool b) { 46 | if (IsEmpty()) return; 47 | Del(root_.next, b); 48 | } 49 | 50 | void List::DelTail(bool b) { 51 | if (IsEmpty()) return; 52 | Del(root_.prev, b); 53 | } 54 | 55 | void List::DelWithIndex(int index, bool b) { 56 | if (IsEmpty()) return; 57 | ListNode* temp; 58 | 59 | temp = GetData(index); 60 | if (temp) __Del(temp->prev, temp->next, b); 61 | } 62 | 63 | // move append函数 64 | void List::MoveHead(ListNode* node) { 65 | __Del(node->prev, node->next, false); 66 | AddHead(node); 67 | } 68 | 69 | void List::MoveTail(ListNode* node) { 70 | __Del(node->prev, node->next, false); 71 | AddTail(node); 72 | } 73 | 74 | void List::Append(List* from) { 75 | if (from->IsEmpty()) return; 76 | ListNode* f_head = from->root_.next; 77 | ListNode* f_tail = from->root_.prev; 78 | 79 | root_.prev->next = f_head; 80 | f_head->prev = root_.prev; 81 | 82 | f_tail->next = &root_; 83 | root_.prev = f_tail; 84 | 85 | count_ += from->count_; 86 | // must invoke this method 87 | from->__Init(); 88 | } 89 | 90 | ListNode* List::GetData(int index) { 91 | if (IsEmpty()) return &root_; 92 | if (index < 0 || index >= count_) return nullptr; 93 | 94 | ListNode* temp = &root_; 95 | int temp_index = -1; 96 | 97 | if (index >= count_ / 2) { // 使用倒序遍历 98 | temp_index = count_; 99 | do { 100 | if (index != temp_index) { 101 | --temp_index; 102 | temp = temp->prev; 103 | } else { 104 | return temp; 105 | } 106 | } while (true); 107 | } else { // 使用正序遍历 108 | temp_index = -1; 109 | do { 110 | if (index != temp_index) { 111 | ++temp_index; 112 | temp = temp->next; 113 | } else { 114 | return temp; 115 | } 116 | } while (true); 117 | } 118 | } 119 | 120 | void List::Clear(bool b) { 121 | while (!IsEmpty()) { 122 | DelHead(b); 123 | } 124 | count_ = 0; 125 | } 126 | 127 | } // namespace myframe 128 | -------------------------------------------------------------------------------- /tools/gen_mod_proj.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | 4 | import sys, getopt 5 | import os, os.path 6 | import re 7 | import shutil 8 | 9 | def dumpUsage(): 10 | print("Usage: python3 gen_mod_proj.py --name=MOD_NAME --dir=MOD_DIR") 11 | print("Options:") 12 | print(" --name MOD_NAME Base module name, for example: usermod") 13 | print(" --dir MOD_DIR Base module project dirtory, for example: /home/yourname/") 14 | print("") 15 | print("Sample: python3 gen_mod_proj.py --name=\"usermod\" --dir=\"/home/yourname/\"") 16 | print("") 17 | 18 | def checkParams(opts): 19 | """ 20 | 检查模块名是否符合命名规则 21 | 检查目录是否存在 22 | """ 23 | res = {} 24 | for opt, arg in opts: 25 | if opt in ('--name'): 26 | if re.match('^[a-zA-Z_][a-zA-Z0-9_]*$', arg): 27 | res['name'] = arg 28 | else: 29 | return res 30 | elif opt in ('--dir'): 31 | res['dir'] = arg; 32 | else: 33 | print("Unknown option " + arg) 34 | res['dir'] = res['dir'] + res['name'] + '/' 35 | return res 36 | 37 | def replaceParams(params, line): 38 | return re.sub("@template_name@", params["name"], line) 39 | 40 | def replaceFile(params, file): 41 | file_content = "" 42 | rf = open(file, 'r') 43 | while True: 44 | line = rf.readline() 45 | if line == '' or line is None: 46 | break 47 | # replaceParams(params, line) 48 | file_content = file_content + replaceParams(params, line) 49 | # print(file_content) 50 | rf.close() 51 | 52 | wf = open(file, 'w') 53 | wf.write(file_content) 54 | wf.close() 55 | 56 | if __name__ == "__main__": 57 | try: 58 | opts, args = getopt.getopt(sys.argv[1:], 59 | "h", 60 | ["name=", "dir="]) 61 | except getopt.GetOptError: 62 | dumpUsage() 63 | sys.exit(1) 64 | 65 | opt_cnt = 2 66 | if len(opts) != opt_cnt: 67 | dumpUsage() 68 | sys.exit(1) 69 | 70 | print("opts %s" % opts) 71 | params_dict = checkParams(opts) 72 | if len(params_dict) != opt_cnt: 73 | dumpUsage() 74 | sys.exit(1) 75 | 76 | proj_src_dir = os.path.split(os.path.realpath(__file__))[0] 77 | proj_rename_dict = {} 78 | proj_modify_var_list = [] 79 | # 设置要修改的文件 80 | proj_src_dir = proj_src_dir + "/../templates/" 81 | proj_rename_dict[params_dict["dir"] + "template.cpp"] = params_dict["dir"] + params_dict["name"] + ".cpp" 82 | proj_rename_dict[params_dict["dir"] + "template.json"] = params_dict["dir"] + params_dict["name"] + ".json" 83 | proj_modify_var_list.append(params_dict["dir"] + "CMakeLists.txt") 84 | proj_modify_var_list.append(params_dict["dir"] + params_dict["name"] + ".json") 85 | proj_modify_var_list.append(params_dict["dir"] + params_dict["name"] + ".cpp") 86 | 87 | # 拷贝到指定目录 88 | shutil.copytree(proj_src_dir, params_dict["dir"], True) 89 | 90 | # 重命名模板工程文件 91 | for k in proj_rename_dict: 92 | os.rename(k, proj_rename_dict[k]) 93 | 94 | # 替换文件中变量 95 | for v in proj_modify_var_list: 96 | replaceFile(params_dict, v) 97 | 98 | print("Success!!!") 99 | -------------------------------------------------------------------------------- /myframe/worker.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "myframe/export.h" 14 | #include "myframe/macros.h" 15 | #include "myframe/mailbox.h" 16 | #include "myframe/cmd_channel.h" 17 | #include "myframe/event.h" 18 | 19 | namespace myframe { 20 | 21 | class App; 22 | class WorkerContext; 23 | class MYFRAME_EXPORT Worker { 24 | friend class App; 25 | friend class ModManager; 26 | friend class WorkerContext; 27 | 28 | public: 29 | Worker() = default; 30 | virtual ~Worker(); 31 | 32 | /** 33 | * GetType() - 获得事件类型 34 | * 35 | * @return: 事件类型 36 | */ 37 | virtual Event::Type GetType(); 38 | 39 | /** 40 | * GetConfig() - 获得配置参数 41 | * @return: 返回json对象 42 | */ 43 | const Json::Value* GetConfig() const; 44 | 45 | /** 46 | * GetWorkerName() - 获得该worker的worker名 47 | * 48 | * @return: 成功返回:worker名,失败返回:空字符串 49 | */ 50 | const std::string GetWorkerName() const; 51 | const std::string& GetModName() const; 52 | const std::string& GetTypeName() const; 53 | const std::string& GetInstName() const; 54 | 55 | protected: 56 | /** 57 | * Mailbox() - 发送消息的mailbox 58 | * 59 | * @return: 失败 nullptr 60 | */ 61 | Mailbox* GetMailbox(); 62 | 63 | /** 64 | * GetCmdChannel() - 与框架通信对象 65 | * 66 | * @return: 失败 nullptr 67 | */ 68 | CmdChannel* GetCmdChannel(); 69 | 70 | /** 71 | * Init() - worker初始化 72 | * 73 | * 在新创建的线程中调用该初始化函数 74 | */ 75 | virtual void Init() {} 76 | 77 | /** 78 | * Run() - worker线程循环调用 79 | * 80 | * 函数会循环调用,调用Stop停止调用 81 | */ 82 | virtual void Run() = 0; 83 | 84 | /** 85 | * Exit() - worker线程退出时调用 86 | */ 87 | virtual void Exit() {} 88 | 89 | /** 90 | * Stop() - 停止Run循环调用 91 | */ 92 | void Stop(); 93 | 94 | /// 分发消息并立即返回 95 | int DispatchMsg(); 96 | 97 | /// 分发消息并等待回复消息 98 | int DispatchAndWaitMsg(); 99 | 100 | /** 101 | * GetApp() - 获得应用实例 102 | * 103 | * 注意:不要将返回的对象存储为成员变量或者静态变量, 104 | * 否则会导致程序退出异常。 105 | * 106 | * @return: 成功返回: app对象指针, 失败返回: nullptr 107 | */ 108 | std::shared_ptr GetApp(); 109 | 110 | private: 111 | void SetModName(const std::string&); 112 | void SetTypeName(const std::string&); 113 | void SetInstName(const std::string&); 114 | 115 | void SetContext(WorkerContext*); 116 | 117 | std::string mod_name_; 118 | std::string class_name_; 119 | std::string inst_name_; 120 | 121 | WorkerContext* ctx_{ nullptr }; 122 | 123 | DISALLOW_COPY_AND_ASSIGN(Worker) 124 | }; 125 | 126 | } // namespace myframe 127 | 128 | #include "myframe/platform.h" 129 | #if defined(MYFRAME_OS_WINDOWS) 130 | template class std::shared_ptr; 131 | #endif 132 | extern "C" { 133 | typedef std::shared_ptr (*worker_create_func_t)( 134 | const std::string&); 135 | } // extern "C" 136 | -------------------------------------------------------------------------------- /test/app_send_req_test.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "myframe/common.h" 14 | #include "myframe/log.h" 15 | #include "myframe/msg.h" 16 | #include "myframe/actor.h" 17 | #include "myframe/mod_manager.h" 18 | #include "myframe/app.h" 19 | 20 | #include "performance_test_config.h" 21 | 22 | class EchoActorTest : public myframe::Actor { 23 | public: 24 | int Init() override { 25 | LOG(INFO) << "init EchoActorTest"; 26 | return 0; 27 | } 28 | 29 | void Proc(const std::shared_ptr& msg) override { 30 | if (msg->GetData() == "hello") { 31 | auto re = std::make_shared( 32 | "resp:" + std::to_string(seq_++)); 33 | auto mailbox = GetMailbox(); 34 | mailbox->Send(msg->GetSrc(), std::move(re)); 35 | } 36 | } 37 | 38 | private: 39 | int seq_{0}; 40 | }; 41 | 42 | int main() { 43 | auto lib_dir = 44 | myframe::Common::GetAbsolutePath(MYFRAME_LIB_DIR).string(); 45 | auto log_dir = 46 | myframe::Common::GetAbsolutePath(MYFRAME_LOG_DIR).string(); 47 | 48 | myframe::InitLog(log_dir, "app_send_req_test"); 49 | 50 | auto app = std::make_shared(); 51 | if (false == app->Init(lib_dir, 4)) { 52 | LOG(ERROR) << "Init failed"; 53 | return -1; 54 | } 55 | 56 | // mod manager 57 | auto& mod = app->GetModManager(); 58 | 59 | // 注册echo Actor 60 | mod->RegActor("EchoActorTest", [](const std::string&) { 61 | return std::make_shared(); 62 | }); 63 | auto actor = mod->CreateActorInst("class", "EchoActorTest", "1"); 64 | app->AddActor(actor); 65 | 66 | // 压力测试SendRequest函数 67 | std::mutex mtx; 68 | int th_cnt = 5; 69 | int exit_th_cnt = 0; 70 | int send_cnt = 10000; 71 | std::vector th_vec; 72 | for (int i = 0; i < th_cnt; ++i) { 73 | th_vec.push_back(std::thread([&, i](){ 74 | while (app->GetState() != myframe::App::State::kRunning) { 75 | std::this_thread::sleep_for( 76 | std::chrono::milliseconds(100)); 77 | } 78 | int cnt = send_cnt; 79 | while (cnt--) { 80 | auto msg = std::make_shared("hello"); 81 | msg->SetDst("actor.EchoActorTest.1"); 82 | auto resp = app->SendRequest(std::move(msg)); 83 | if (resp == nullptr) { 84 | continue; 85 | } 86 | LOG(INFO) << "thread " << i << " resp: " << resp->GetData(); 87 | } 88 | std::lock_guard g(mtx); 89 | LOG(INFO) << "user thread " << i << " exit"; 90 | ++exit_th_cnt; 91 | if (exit_th_cnt == th_cnt) { 92 | app->Quit(); 93 | } 94 | })); 95 | } 96 | 97 | app->Exec(); 98 | for (int i = 0; i < th_cnt; ++i) { 99 | if (th_vec[i].joinable()) { 100 | th_vec[i].join(); 101 | } 102 | } 103 | return 0; 104 | } 105 | -------------------------------------------------------------------------------- /myframe/platform/cmd_channel_generic.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "myframe/log.h" 15 | #include "myframe/export.h" 16 | #include "myframe/macros.h" 17 | #include "myframe/event.h" 18 | #include "myframe/cmd_channel.h" 19 | 20 | namespace myframe { 21 | 22 | class CmdChannelGeneric final : public CmdChannel { 23 | public: 24 | explicit CmdChannelGeneric(std::shared_ptr); 25 | virtual ~CmdChannelGeneric(); 26 | 27 | ev_handle_t GetOwnerHandle() const override; 28 | ev_handle_t GetMainHandle() const override; 29 | 30 | int SendToOwner(const Cmd& cmd) override; 31 | int RecvFromOwner(Cmd* cmd) override; 32 | 33 | int SendToMain(const Cmd& cmd) override; 34 | int RecvFromMain(Cmd* cmd, int timeout_ms = -1) override; 35 | 36 | private: 37 | std::mutex main_cmd_mtx_; 38 | std::list to_main_cmd_; 39 | 40 | std::mutex mtx_; 41 | std::list to_owner_cmd_; 42 | std::condition_variable cv_; 43 | 44 | DISALLOW_COPY_AND_ASSIGN(CmdChannelGeneric) 45 | }; 46 | 47 | CmdChannelGeneric::CmdChannelGeneric(std::shared_ptr poller) 48 | : CmdChannel(poller) { 49 | } 50 | 51 | CmdChannelGeneric::~CmdChannelGeneric() { 52 | LOG(INFO) << "CmdChannel " << this << " deconstruct"; 53 | } 54 | 55 | ev_handle_t CmdChannelGeneric::GetOwnerHandle() const { 56 | return reinterpret_cast(const_cast(this)); 57 | } 58 | 59 | ev_handle_t CmdChannelGeneric::GetMainHandle() const { 60 | return reinterpret_cast(const_cast(this)); 61 | } 62 | 63 | int CmdChannelGeneric::SendToOwner(const Cmd& cmd) { 64 | std::lock_guard lk(mtx_); 65 | to_owner_cmd_.push_back(cmd); 66 | cv_.notify_one(); 67 | return 0; 68 | } 69 | 70 | int CmdChannelGeneric::RecvFromOwner(Cmd* cmd) { 71 | std::lock_guard lk(main_cmd_mtx_); 72 | *cmd = to_main_cmd_.front(); 73 | to_main_cmd_.pop_front(); 74 | return 0; 75 | } 76 | 77 | int CmdChannelGeneric::RecvFromMain(Cmd* cmd, int timeout_ms) { 78 | std::unique_lock lk(mtx_); 79 | using namespace std::chrono_literals; // NOLINT 80 | if (timeout_ms > 0) { 81 | cv_.wait_for(lk, timeout_ms * 1ms, 82 | [this](){ return !to_owner_cmd_.empty(); }); 83 | } else { 84 | cv_.wait(lk, [this](){ return !to_owner_cmd_.empty(); }); 85 | } 86 | if (to_owner_cmd_.size() > 1) { 87 | std::stringstream ss; 88 | for (Cmd p : to_owner_cmd_) { 89 | ss << static_cast(p) << ", "; 90 | } 91 | LOG(WARNING) << this << " too many cmd " << ss.str(); 92 | } 93 | *cmd = to_owner_cmd_.front(); 94 | to_owner_cmd_.pop_front(); 95 | return 0; 96 | } 97 | 98 | int CmdChannelGeneric::SendToMain(const Cmd& cmd) { 99 | std::lock_guard lk(main_cmd_mtx_); 100 | to_main_cmd_.push_back(cmd); 101 | 102 | poller_->Notify( 103 | reinterpret_cast( 104 | const_cast(this))); 105 | return 0; 106 | } 107 | 108 | } // namespace myframe 109 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.10) 2 | 3 | add_compile_definitions(${PROJECT_NAME}_EXPORTS) 4 | 5 | ### link static lib 6 | if (glog_lib_type STREQUAL "STATIC_LIBRARY") 7 | link_libraries( 8 | glog::glog 9 | ) 10 | endif () 11 | if (jsoncpp_lib_type STREQUAL "STATIC_LIBRARY") 12 | link_libraries( 13 | JsonCpp::JsonCpp 14 | ) 15 | endif () 16 | if (MYFRAME_ENABLE_ASAN) 17 | add_compile_options( 18 | "$<$:-fsanitize=address>" 19 | ) 20 | add_link_options( 21 | "$<$:-fsanitize=address>" 22 | ) 23 | endif () 24 | 25 | ### actor 26 | add_library(example_actor_helloworld SHARED example_actor_helloworld.cpp) 27 | target_link_libraries(example_actor_helloworld ${PROJECT_NAME}) 28 | add_library(example_actor_timer SHARED example_actor_timer.cpp) 29 | target_link_libraries(example_actor_timer ${PROJECT_NAME}) 30 | add_library(example_actor_serial SHARED example_actor_serial.cpp) 31 | target_link_libraries(example_actor_serial ${PROJECT_NAME}) 32 | add_library(example_actor_concurrent SHARED example_actor_concurrent.cpp) 33 | target_link_libraries(example_actor_concurrent ${PROJECT_NAME}) 34 | add_library(example_actor_subscribe SHARED example_actor_subscribe.cpp) 35 | target_link_libraries(example_actor_subscribe ${PROJECT_NAME}) 36 | add_library(example_node SHARED example_node.cpp) 37 | target_link_libraries(example_node ${PROJECT_NAME}) 38 | 39 | ### worker 40 | add_library(example_worker_publish SHARED example_worker_publish.cpp) 41 | target_link_libraries(example_worker_publish ${PROJECT_NAME}) 42 | add_library(example_worker_talk SHARED example_worker_talk.cpp) 43 | target_link_libraries(example_worker_talk ${PROJECT_NAME}) 44 | add_library(example_worker_actor_interactive SHARED example_worker_actor_interactive.cpp) 45 | target_link_libraries(example_worker_actor_interactive ${PROJECT_NAME}) 46 | add_library(example_worker_interactive_with_3rd_frame SHARED example_worker_interactive_with_3rd_frame.cpp) 47 | target_link_libraries(example_worker_interactive_with_3rd_frame ${PROJECT_NAME}) 48 | add_library(example_worker_quit SHARED example_worker_quit.cpp) 49 | target_link_libraries(example_worker_quit ${PROJECT_NAME}) 50 | 51 | add_library(example_config SHARED example_config.cpp) 52 | target_link_libraries(example_config ${PROJECT_NAME}) 53 | add_library(example_trans_obj SHARED example_trans_obj.cpp) 54 | target_link_libraries(example_trans_obj ${PROJECT_NAME}) 55 | add_library(example_thread_quit SHARED example_thread_quit.cpp) 56 | target_link_libraries(example_thread_quit ${PROJECT_NAME}) 57 | 58 | ### install 59 | file(GLOB conf_files "*.json") 60 | # 在unix like系统下禁用MYFRAME_USE_CV才能用,有需要再打开 61 | list(REMOVE_ITEM conf_files "${CMAKE_CURRENT_SOURCE_DIR}/example_worker_interactive_with_3rd_frame.json") 62 | install(FILES 63 | ${conf_files} 64 | PERMISSIONS 65 | OWNER_READ OWNER_WRITE 66 | GROUP_READ 67 | WORLD_READ 68 | DESTINATION ${MYFRAME_SERVICE_DIR} 69 | ) 70 | install(TARGETS 71 | example_actor_helloworld 72 | example_actor_timer 73 | example_actor_serial 74 | example_actor_concurrent 75 | example_actor_subscribe 76 | example_node 77 | example_worker_actor_interactive 78 | example_worker_publish 79 | example_worker_talk 80 | # 在unix like系统下禁用MYFRAME_USE_CV才能用,有需要再打开 81 | # example_worker_interactive_with_3rd_frame 82 | example_worker_quit 83 | example_config 84 | example_trans_obj 85 | example_thread_quit 86 | LIBRARY DESTINATION ${MYFRAME_LIB_DIR} 87 | ARCHIVE DESTINATION ${MYFRAME_LIB_DIR} 88 | RUNTIME DESTINATION ${MYFRAME_BIN_DIR} 89 | ) 90 | -------------------------------------------------------------------------------- /myframe/worker_context.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include "myframe/worker_context.h" 8 | 9 | #include 10 | #include 11 | 12 | #include "myframe/log.h" 13 | #include "myframe/msg.h" 14 | #include "myframe/worker.h" 15 | #include "myframe/app.h" 16 | #include "myframe/common.h" 17 | 18 | namespace myframe { 19 | 20 | WorkerContext::WorkerContext( 21 | std::shared_ptr app, 22 | std::shared_ptr worker, 23 | std::shared_ptr poller) 24 | : runing_(false) 25 | , worker_(worker) 26 | , app_(app) { 27 | worker_->SetContext(this); 28 | cmd_channel_ = CmdChannel::Create(poller); 29 | } 30 | 31 | WorkerContext::~WorkerContext() { 32 | LOG(INFO) << worker_->GetWorkerName() << " deconstruct"; 33 | } 34 | 35 | bool WorkerContext::Init(const Json::Value& conf) { 36 | LOG(INFO) << worker_->GetWorkerName() << " context init"; 37 | config_ = conf; 38 | return true; 39 | } 40 | 41 | ev_handle_t WorkerContext::GetHandle() const { 42 | return cmd_channel_->GetMainHandle(); 43 | } 44 | 45 | Event::Type WorkerContext::GetType() const { 46 | return worker_->GetType(); 47 | } 48 | 49 | std::string WorkerContext::GetName() const { 50 | return worker_->GetWorkerName(); 51 | } 52 | 53 | void WorkerContext::Start() { 54 | if (runing_.load() == false) { 55 | runing_.store(true); 56 | th_ = std::thread(std::bind(&WorkerContext::ListenThread, this)); 57 | } 58 | } 59 | 60 | void WorkerContext::Stop() { 61 | runing_.store(false); 62 | } 63 | 64 | void WorkerContext::Join() { 65 | if (th_.joinable()) { 66 | th_.join(); 67 | } 68 | } 69 | 70 | bool WorkerContext::SetThreadAffinity(int cpu_core) { 71 | if (runing_.load()) { 72 | if (0 == Common::SetThreadAffinity(&th_, cpu_core)) { 73 | return true; 74 | } 75 | LOG(WARNING) << GetName() << " bind cpu " << cpu_core << " failed"; 76 | } else { 77 | LOG(WARNING) << GetName() << " not runing, skip SetThreadAffinity"; 78 | } 79 | return false; 80 | } 81 | 82 | void WorkerContext::Initialize() { 83 | mailbox_.SetAddr(worker_->GetWorkerName()); 84 | std::string th_name = mailbox_.Addr(); 85 | th_name = th_name.size() >= 16 ? th_name.substr(0, 15) : th_name; 86 | if (Common::SetThreadName(nullptr, th_name)) { 87 | LOG(WARNING) << "set " << mailbox_.Addr() 88 | << " thread name " << th_name << " failed"; 89 | } else { 90 | LOG(INFO) << "set " << mailbox_.Addr() 91 | << " thread name " << th_name; 92 | } 93 | worker_->Init(); 94 | } 95 | 96 | void WorkerContext::ListenThread() { 97 | if (worker_ == nullptr) { 98 | return; 99 | } 100 | Initialize(); 101 | while (runing_.load()) { 102 | worker_->Run(); 103 | } 104 | worker_->Exit(); 105 | cmd_channel_->SendToMain(CmdChannel::Cmd::kQuit); 106 | } 107 | 108 | Mailbox* WorkerContext::GetMailbox() { 109 | return &mailbox_; 110 | } 111 | 112 | CmdChannel* WorkerContext::GetCmdChannel() { 113 | return cmd_channel_.get(); 114 | } 115 | 116 | std::shared_ptr WorkerContext::GetApp() { 117 | return app_.lock(); 118 | } 119 | 120 | const Json::Value* WorkerContext::GetConfig() const { 121 | return &config_; 122 | } 123 | 124 | std::ostream& operator<<(std::ostream& out, WorkerContext& ctx) { 125 | auto w = ctx.GetWorker(); 126 | out << w->GetWorkerName() << "." << ctx.GetThreadId(); 127 | return out; 128 | } 129 | 130 | } // namespace myframe 131 | -------------------------------------------------------------------------------- /test/performance_trans1_cost_test.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "myframe/log.h" 14 | #include "myframe/common.h" 15 | #include "myframe/msg.h" 16 | #include "myframe/actor.h" 17 | #include "myframe/mod_manager.h" 18 | #include "myframe/app.h" 19 | 20 | #include "performance_test_config.h" 21 | 22 | class TransMsgCostTest : public myframe::Actor { 23 | public: 24 | TransMsgCostTest() : msg_(8192, 'x') {} 25 | 26 | int Init() override { 27 | LOG(INFO) << "runing TransMsgCostTest..."; 28 | cost_us_list_.reserve(6000); 29 | last_ = std::chrono::high_resolution_clock::now(); 30 | begin_ = std::chrono::high_resolution_clock::now(); 31 | auto mailbox = GetMailbox(); 32 | mailbox->Send(GetActorName(), std::make_shared(msg_)); 33 | return 0; 34 | } 35 | 36 | void Proc(const std::shared_ptr& msg) override { 37 | (void)msg; 38 | auto now = std::chrono::high_resolution_clock::now(); 39 | auto us = std::chrono::duration_cast(now - last_) 40 | .count(); 41 | LOG(INFO) << GetActorName() << " trans msg cost(us) " << us; 42 | cost_us_list_.push_back(us); 43 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 44 | last_ = std::chrono::high_resolution_clock::now(); 45 | 46 | auto sec = std::chrono::duration_cast(last_ - begin_) 47 | .count(); 48 | if (sec < 60) { 49 | auto mailbox = GetMailbox(); 50 | mailbox->Send(GetActorName(), std::make_shared(msg_)); 51 | } else { 52 | LOG(INFO) << "runing TransMsgCostTest end"; 53 | // 耗时 54 | // 平均值: 55 | // 99分位: 56 | int sum = std::accumulate(cost_us_list_.begin(), cost_us_list_.end(), 0); 57 | int avg = sum / cost_us_list_.size(); 58 | LOG(INFO) << "trans 1 actor cnt: " << cost_us_list_.size(); 59 | LOG(INFO) << "trans 1 actor avg(us): " << avg; 60 | std::sort(cost_us_list_.begin(), cost_us_list_.end()); 61 | LOG(INFO) << "trans 1 actor 99(us): " << 62 | cost_us_list_[ 63 | static_cast(cost_us_list_.size() * 0.99)]; 64 | GetApp()->Quit(); 65 | } 66 | } 67 | 68 | private: 69 | std::chrono::high_resolution_clock::time_point begin_; 70 | std::chrono::high_resolution_clock::time_point last_; 71 | std::string msg_; 72 | std::vector cost_us_list_; 73 | }; 74 | 75 | int main() { 76 | auto lib_dir = 77 | myframe::Common::GetAbsolutePath(MYFRAME_LIB_DIR).string(); 78 | auto log_dir = 79 | myframe::Common::GetAbsolutePath(MYFRAME_LOG_DIR).string(); 80 | 81 | myframe::InitLog(log_dir, "performance_trans1_cost_test"); 82 | 83 | auto app = std::make_shared(); 84 | if (false == app->Init(lib_dir, 4)) { 85 | LOG(ERROR) << "Init failed"; 86 | return -1; 87 | } 88 | 89 | // mod manager 90 | auto& mod = app->GetModManager(); 91 | 92 | // 发送单条消息耗时(测试时长1分钟,每隔10毫秒发送1条消息) 93 | // 耗时 94 | // 平均值: 95 | // 99分位: 96 | mod->RegActor("TransMsgCostTest", [](const std::string&) { 97 | return std::make_shared(); 98 | }); 99 | auto actor = mod->CreateActorInst("class", "TransMsgCostTest", "1"); 100 | app->AddActor(actor); 101 | 102 | return app->Exec(); 103 | } 104 | -------------------------------------------------------------------------------- /myframe/actor.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include "myframe/export.h" 16 | #include "myframe/macros.h" 17 | #include "myframe/mailbox.h" 18 | #include "myframe/msg.h" 19 | 20 | namespace myframe { 21 | 22 | class Msg; 23 | class ActorContext; 24 | class App; 25 | class MYFRAME_EXPORT Actor { 26 | friend class App; 27 | friend class ActorContext; 28 | friend class ModManager; 29 | 30 | public: 31 | Actor() = default; 32 | virtual ~Actor(); 33 | 34 | /** 35 | * GetConfig() - 获得配置参数 36 | * @return: 返回json对象 37 | */ 38 | const Json::Value* GetConfig() const; 39 | 40 | /** 41 | * GetActorName() - 获得该actor的actor名 42 | * 43 | * @return: 成功返回:actor名,失败返回:空字符串 44 | */ 45 | const std::string GetActorName() const; 46 | const std::string& GetModName() const; 47 | const std::string& GetTypeName() const; 48 | const std::string& GetInstName() const; 49 | 50 | protected: 51 | /** 52 | * Init() - actor初始化调用的初始化函数 53 | * 54 | * @return: 成功 0, 失败 -1 55 | */ 56 | virtual int Init() = 0; 57 | 58 | /** 59 | * Proc() - 消息处理函数 60 | * @msg: actor收到的消息 61 | * 62 | */ 63 | virtual void Proc(const std::shared_ptr& msg) = 0; 64 | 65 | /** 66 | * Mailbox() - 发送消息的mailbox 67 | * 68 | * @return: 失败 nullptr 69 | */ 70 | Mailbox* GetMailbox(); 71 | 72 | /** 73 | * Timeout() - 设置定时器 74 | * @expired: 超时时间(单位:10ms, 比如 expired = 1, 那么超时时间就是10ms) 75 | * 76 | * 定时器设置之后,过了超时时间,actor就会收到超时消息; 77 | * 如果想实现周期性的定时器,可以在收到超时消息之后, 78 | * 再次调用此函数设置下一次的超时。 79 | * 80 | * msg->GetType() == "TIMER" 确认是定时器消息 81 | * msg->GetDesc() == timer_name 确认是那个定时器消息 82 | * 83 | * @return: 成功返回: 0, 失败返回: -1 84 | */ 85 | int Timeout(const std::string& timer_name, int expired); 86 | 87 | /** 88 | * Subscribe() - 订阅actor的消息 89 | * @addr: 订阅actor的地址 90 | * @msg_type: 订阅消息类型 91 | * @mode: 订阅消息影响范围 92 | * 93 | * 被订阅的组件需要在Proc函数中处理订阅消息,消息格式: 94 | * msg->GetType() == "SUBSCRIBE" 确认是订阅消息 95 | * msg->GetDesc() == 确认消息类型 96 | * msg->GetSrc() 确定是订阅组件地址 97 | * @return: 成功返回true,失败返回false 98 | */ 99 | bool Subscribe( 100 | const std::string& addr, 101 | const std::string& msg_type = "", 102 | const Msg::TransMode mode = Msg::TransMode::kIntra); 103 | 104 | /** 105 | * GetApp() - 获得应用实例 106 | * 107 | * 注意:不要将返回的对象存储为成员变量或者静态变量, 108 | * 否则会导致程序退出异常。 109 | * 110 | * @return: 成功返回: app对象指针, 失败返回: nullptr 111 | */ 112 | std::shared_ptr GetApp(); 113 | 114 | private: 115 | void SetModName(const std::string& name); 116 | void SetTypeName(const std::string& name); 117 | void SetInstName(const std::string& name); 118 | 119 | void SetContext(ActorContext*); 120 | 121 | std::string mod_name_; 122 | std::string class_name_; 123 | std::string instance_name_; 124 | 125 | ActorContext* ctx_{ nullptr }; 126 | 127 | DISALLOW_COPY_AND_ASSIGN(Actor) 128 | }; 129 | 130 | } // namespace myframe 131 | 132 | #include "myframe/platform.h" 133 | #if defined(MYFRAME_OS_WINDOWS) 134 | template class std::shared_ptr; 135 | #endif 136 | extern "C" { 137 | typedef std::shared_ptr (*actor_create_func_t)( 138 | const std::string&); 139 | } // extern "C" 140 | -------------------------------------------------------------------------------- /myframe/platform/poller_linux.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #pragma once 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "myframe/log.h" 17 | #include "myframe/macros.h" 18 | #include "myframe/event.h" 19 | #include "myframe/poller.h" 20 | 21 | struct epoll_event; 22 | 23 | namespace myframe { 24 | 25 | class PollerLinux final : public Poller { 26 | public: 27 | PollerLinux() = default; 28 | virtual ~PollerLinux(); 29 | 30 | bool Init() override; 31 | bool Add(const std::shared_ptr&) const override; 32 | bool Del(const std::shared_ptr&) const override; 33 | int Wait(std::vector* evs, int timeout_ms = 100) override; 34 | 35 | private: 36 | std::atomic_bool init_{false}; 37 | int poll_fd_{-1}; 38 | size_t max_ev_count_{64}; 39 | struct epoll_event* evs_{nullptr}; 40 | 41 | DISALLOW_COPY_AND_ASSIGN(PollerLinux) 42 | }; 43 | 44 | PollerLinux::~PollerLinux() { 45 | if (poll_fd_ != -1) { 46 | close(poll_fd_); 47 | poll_fd_ = -1; 48 | } 49 | if (evs_ != nullptr) { 50 | free(evs_); 51 | evs_ = nullptr; 52 | } 53 | init_.store(false); 54 | } 55 | 56 | bool PollerLinux::Init() { 57 | if (init_.load()) { 58 | return true; 59 | } 60 | poll_fd_ = epoll_create(1024); 61 | if (-1 == poll_fd_) { 62 | LOG(ERROR) << "poller create() failed, " << strerror(errno); 63 | return false; 64 | } 65 | LOG(INFO) << "Create epoll fd " << poll_fd_; 66 | auto void_evs = malloc(sizeof(struct epoll_event) * max_ev_count_); 67 | evs_ = reinterpret_cast(void_evs); 68 | init_.store(true); 69 | return true; 70 | } 71 | 72 | bool PollerLinux::Add(const std::shared_ptr& ev) const { 73 | if (!init_.load()) { 74 | return false; 75 | } 76 | struct epoll_event event; 77 | event.data.fd = ev->GetHandle(); 78 | event.events = EPOLLIN; 79 | int res = 0; 80 | // 如果该事件已经注册,就修改事件类型 81 | res = epoll_ctl(poll_fd_, EPOLL_CTL_MOD, ev->GetHandle(), &event); 82 | if (-1 == res) { 83 | // 没有注册就添加至epoll 84 | res = epoll_ctl(poll_fd_, EPOLL_CTL_ADD, ev->GetHandle(), &event); 85 | if (-1 == res) { 86 | LOG(ERROR) << "epoll_ctl error, " << strerror(errno); 87 | return false; 88 | } 89 | } else { 90 | LOG(WARNING) 91 | << " has already reg ev " << ev->GetHandle() << ": " 92 | << strerror(errno); 93 | return false; 94 | } 95 | return true; 96 | } 97 | 98 | bool PollerLinux::Del(const std::shared_ptr& ev) const { 99 | if (!init_.load()) { 100 | return false; 101 | } 102 | if (-1 == epoll_ctl(poll_fd_, EPOLL_CTL_DEL, ev->GetHandle(), NULL)) { 103 | LOG(ERROR) << "del event " << ev->GetHandle() << ": " << strerror(errno); 104 | return false; 105 | } 106 | return true; 107 | } 108 | 109 | int PollerLinux::Wait(std::vector* evs, int timeout_ms) { 110 | if (!init_.load()) { 111 | return -1; 112 | } 113 | evs->clear(); 114 | int ev_count = epoll_wait(poll_fd_, 115 | evs_, 116 | static_cast(max_ev_count_), 117 | timeout_ms); 118 | if (0 > ev_count) { 119 | LOG(WARNING) << "epoll wait error: " << strerror(errno); 120 | return -1; 121 | } 122 | for (int i = 0; i < ev_count; ++i) { 123 | if (evs_[i].events != EPOLLIN) { 124 | LOG(WARNING) << "epoll event " << evs_[i].events << " continue"; 125 | continue; 126 | } 127 | evs->push_back(evs_[i].data.fd); 128 | } 129 | return ev_count; 130 | } 131 | 132 | } // namespace myframe 133 | -------------------------------------------------------------------------------- /test/performance_trans1_fullspeed_test.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "myframe/log.h" 14 | #include "myframe/common.h" 15 | #include "myframe/msg.h" 16 | #include "myframe/actor.h" 17 | #include "myframe/mod_manager.h" 18 | #include "myframe/app.h" 19 | 20 | #include "performance_test_config.h" 21 | 22 | class FullSpeedTransTest : public myframe::Actor { 23 | public: 24 | FullSpeedTransTest() : msg_(8192, 'z') {} 25 | 26 | int Init() override { 27 | LOG(INFO) << "init full speed trans "; 28 | // 启动测试 29 | msg_cnt_per_sec_list_.reserve(64); 30 | auto mailbox = GetMailbox(); 31 | mailbox->Send(GetActorName(), std::make_shared(msg_)); 32 | return 0; 33 | } 34 | 35 | void Proc(const std::shared_ptr& msg) override { 36 | (void)msg; 37 | if (!init_) { 38 | init_ = true; 39 | begin_ = std::chrono::high_resolution_clock::now(); 40 | last_ = std::chrono::high_resolution_clock::now(); 41 | } 42 | auto now = std::chrono::high_resolution_clock::now(); 43 | auto us = std::chrono::duration_cast(now - last_) 44 | .count(); 45 | cnt_++; 46 | if (us / 1000.0 > 1000.0) { 47 | LOG(INFO) << GetActorName() << ": full speed msg count " << cnt_; 48 | msg_cnt_per_sec_list_.push_back(cnt_); 49 | cnt_ = 0; 50 | last_ = std::chrono::high_resolution_clock::now(); 51 | } 52 | auto sec = std::chrono::duration_cast( 53 | std::chrono::high_resolution_clock::now() - begin_) 54 | .count(); 55 | if (sec < 60) { 56 | auto mailbox = GetMailbox(); 57 | mailbox->Send(GetActorName(), std::make_shared()); 58 | } else { 59 | LOG(INFO) << "runing FullSpeedTransTest end"; 60 | int sum = std::accumulate(msg_cnt_per_sec_list_.begin(), 61 | msg_cnt_per_sec_list_.end(), 0); 62 | int avg = sum / msg_cnt_per_sec_list_.size(); 63 | LOG(INFO) << "1 actor fullspeed trans msg avg(cnt/sec): " << avg; 64 | std::sort(msg_cnt_per_sec_list_.begin(), msg_cnt_per_sec_list_.end()); 65 | LOG(INFO) << "1 actor fullspeed trans msg 99(cnt/sec): " 66 | << msg_cnt_per_sec_list_[ 67 | static_cast(msg_cnt_per_sec_list_.size() * 0.99)]; 68 | GetApp()->Quit(); 69 | } 70 | } 71 | 72 | private: 73 | bool init_{false}; 74 | int cnt_{0}; 75 | std::chrono::high_resolution_clock::time_point begin_; 76 | std::chrono::high_resolution_clock::time_point last_; 77 | std::string msg_; 78 | std::vector msg_cnt_per_sec_list_; 79 | }; 80 | 81 | int main() { 82 | auto lib_dir = 83 | myframe::Common::GetAbsolutePath(MYFRAME_LIB_DIR).string(); 84 | auto log_dir = 85 | myframe::Common::GetAbsolutePath(MYFRAME_LOG_DIR).string(); 86 | 87 | myframe::InitLog(log_dir, "performance_trans1_fullspeed_test"); 88 | 89 | auto app = std::make_shared(); 90 | if (false == app->Init(lib_dir, 4)) { 91 | LOG(ERROR) << "Init failed"; 92 | return -1; 93 | } 94 | 95 | // mod manager 96 | auto& mod = app->GetModManager(); 97 | 98 | // 1个actor消息吞吐量(测试时长1分钟,全速运行) 99 | mod->RegActor("FullSpeedTransTest", [](const std::string&) { 100 | return std::make_shared(); 101 | }); 102 | auto actor = mod->CreateActorInst("class", "FullSpeedTransTest", "0"); 103 | app->AddActor(actor); 104 | 105 | return app->Exec(); 106 | } 107 | -------------------------------------------------------------------------------- /myframe/mailbox.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include "myframe/mailbox.h" 8 | #include 9 | 10 | #include "myframe/msg.h" 11 | #include "myframe/log.h" 12 | 13 | namespace myframe { 14 | 15 | const std::string& Mailbox::Addr() const { 16 | return addr_; 17 | } 18 | 19 | void Mailbox::SetAddr(const std::string& addr) { 20 | addr_ = addr; 21 | } 22 | 23 | int Mailbox::SendSize() const { 24 | return send_.size(); 25 | } 26 | 27 | bool Mailbox::SendEmpty() const { 28 | return send_.empty(); 29 | } 30 | 31 | void Mailbox::SendClear() { 32 | send_.clear(); 33 | } 34 | 35 | void Mailbox::Send(std::shared_ptr msg) { 36 | send_.push_back(std::move(msg)); 37 | } 38 | 39 | void Mailbox::Send( 40 | const std::string& dst, 41 | std::shared_ptr msg) { 42 | msg->SetSrc(addr_); 43 | msg->SetDst(dst); 44 | Send(std::move(msg)); 45 | } 46 | 47 | void Mailbox::Send( 48 | const std::string& dst, 49 | const std::any& data) { 50 | auto msg = std::make_shared(); 51 | msg->SetAnyData(data); 52 | Send(dst, std::move(msg)); 53 | } 54 | 55 | void Mailbox::Send(std::list>* msg_list) { 56 | send_.splice(send_.end(), *msg_list); 57 | } 58 | 59 | std::list>* Mailbox::GetSendList() { 60 | return &send_; 61 | } 62 | 63 | int Mailbox::RecvSize() const { 64 | return recv_.size(); 65 | } 66 | 67 | bool Mailbox::RecvEmpty() const { 68 | return recv_.empty(); 69 | } 70 | 71 | void Mailbox::RecvClear() { 72 | recv_.clear(); 73 | } 74 | 75 | void Mailbox::Recv(std::shared_ptr msg) { 76 | if (pending_queue_size_ > 0) { 77 | for (; recv_.size() >= static_cast(pending_queue_size_);) { 78 | LOG(WARNING) << Addr() << " pending queue overflow " 79 | << recv_.size() << "/" << pending_queue_size_; 80 | recv_.pop_front(); 81 | } 82 | } 83 | recv_.push_back(std::move(msg)); 84 | } 85 | 86 | const std::shared_ptr Mailbox::PopRecv() { 87 | if (recv_.empty()) { 88 | return nullptr; 89 | } 90 | auto msg = recv_.front(); 91 | recv_.pop_front(); 92 | return msg; 93 | } 94 | 95 | void Mailbox::MoveToRun() { 96 | if (run_queue_size_ > 0) { 97 | auto it = recv_.begin(); 98 | for (size_t i = 0; 99 | i < static_cast(run_queue_size_) && it != recv_.end(); 100 | ++i) { 101 | ++it; 102 | } 103 | run_.splice(run_.begin(), recv_, recv_.begin(), it); 104 | return; 105 | } 106 | run_.splice(run_.end(), recv_); 107 | } 108 | 109 | bool Mailbox::RunEmpty() const { 110 | return run_.empty(); 111 | } 112 | 113 | int Mailbox::RunSize() const { 114 | return run_.size(); 115 | } 116 | 117 | const std::shared_ptr Mailbox::PopRun() { 118 | if (run_.empty()) { 119 | return nullptr; 120 | } 121 | auto msg = run_.front(); 122 | run_.pop_front(); 123 | return msg; 124 | } 125 | 126 | void Mailbox::SetPendingQueueSize(int sz) { 127 | pending_queue_size_ = sz; 128 | } 129 | 130 | int Mailbox::GetPendingQueueSize() const { 131 | return pending_queue_size_; 132 | } 133 | 134 | void Mailbox::SetRunQueueSize(int sz) { 135 | run_queue_size_ = sz; 136 | } 137 | 138 | int Mailbox::GetRunQueueSize() const { 139 | return run_queue_size_; 140 | } 141 | 142 | std::list>* Mailbox::GetRecvList() { 143 | return &recv_; 144 | } 145 | 146 | std::ostream& operator<<(std::ostream& out, const Mailbox& mailbox) { 147 | out << mailbox.Addr() << " recv " << mailbox.RecvSize() 148 | << ", send " << mailbox.SendSize() 149 | << ", run " << mailbox.RunSize(); 150 | return out; 151 | } 152 | 153 | } // namespace myframe 154 | -------------------------------------------------------------------------------- /launcher/launcher.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | 10 | #include "myframe/log.h" 11 | #include "myframe/platform.h" 12 | #include "myframe/common.h" 13 | #include "myframe/app.h" 14 | #include "launcher_config.h" 15 | #include "module_argument.h" 16 | 17 | static std::shared_ptr g_app{nullptr}; 18 | 19 | void OnShutDown(int sig) { 20 | if (g_app == nullptr) { 21 | return; 22 | } 23 | LOG(INFO) << "received interrupt " << sig << ", quiting..."; 24 | g_app->Quit(); 25 | } 26 | 27 | int main(int argc, char** argv) { 28 | // 命令行参数解析 29 | myframe::ModuleArgument module_args(MYFRAME_CONF_DIR); 30 | module_args.ParseArgument(argc, argv); 31 | 32 | // 初始化日志和参数 33 | auto root_dir = myframe::Common::GetWorkRoot(); 34 | stdfs::path log_dir; 35 | stdfs::path lib_dir; 36 | stdfs::path service_dir; 37 | stdfs::path conf_dir; 38 | 39 | if (module_args.GetLogDir().empty()) { 40 | log_dir = MYFRAME_LOG_DIR; 41 | } else { 42 | log_dir = module_args.GetLogDir(); 43 | } 44 | log_dir = myframe::Common::GetAbsolutePath(log_dir.string()); 45 | myframe::InitLog( 46 | log_dir, 47 | module_args.GetProcessName(), 48 | module_args.GetLogMaxSizeMB()); 49 | LOG(INFO) << "launch command: " << module_args.GetCmd(); 50 | 51 | if (module_args.GetLibDir().empty()) { 52 | #if defined(MYFRAME_OS_WINDOWS) 53 | lib_dir = MYFRAME_BIN_DIR; 54 | #else 55 | lib_dir = MYFRAME_LIB_DIR; 56 | #endif 57 | } else { 58 | lib_dir = module_args.GetLibDir(); 59 | } 60 | lib_dir = myframe::Common::GetAbsolutePath(lib_dir.string()); 61 | 62 | if (module_args.GetConfDir().empty()) { 63 | service_dir = MYFRAME_SERVICE_DIR; 64 | } else { 65 | service_dir = module_args.GetConfDir(); 66 | } 67 | service_dir = myframe::Common::GetAbsolutePath(service_dir.string()); 68 | 69 | conf_dir = myframe::Common::GetAbsolutePath(MYFRAME_CONF_DIR); 70 | 71 | LOG(INFO) << "root dir: " << root_dir.string(); 72 | LOG(INFO) << "default lib dir: " << lib_dir.string(); 73 | LOG(INFO) << "default service dir: " << service_dir.string(); 74 | LOG(INFO) << "default log dir: " << log_dir.string(); 75 | LOG(INFO) << "default conf dir: " << conf_dir.string(); 76 | 77 | // 初始化并启动线程 78 | g_app = std::make_shared(); 79 | if (false == g_app->Init( 80 | lib_dir.string(), 81 | module_args.GetThreadPoolSize(), 82 | module_args.GetConnEventSize(), 83 | module_args.GetWarningMsgSize(), 84 | module_args.GetDefaultPendingQueueSize(), 85 | module_args.GetDefaultRunQueueSize())) { 86 | LOG(ERROR) << "Init failed"; 87 | return -1; 88 | } 89 | 90 | // 从配置文件加载服务 91 | if (!module_args.GetConfList().empty()) { 92 | auto conf_list = module_args.GetConfList(); 93 | for (auto conf : conf_list) { 94 | std::string abs_conf_file; 95 | if (myframe::Common::IsAbsolutePath(conf)) { 96 | abs_conf_file = conf; 97 | } else { 98 | abs_conf_file = (service_dir / conf).string(); 99 | } 100 | if (!g_app->LoadServiceFromFile(abs_conf_file)) { 101 | LOG(ERROR) << "Load " << abs_conf_file << " failed, exit"; 102 | g_app->Quit(); 103 | break; 104 | } 105 | } 106 | } else { 107 | if (g_app->LoadServiceFromDir(service_dir.string()) <= 0) { 108 | LOG(ERROR) << "Load service from " << service_dir.string() 109 | << " failed, exit"; 110 | g_app->Quit(); 111 | } 112 | } 113 | 114 | // 注册退出函数 115 | std::signal(SIGINT, OnShutDown); 116 | 117 | // 开始事件循环 118 | g_app->Exec(); 119 | 120 | // 退出资源清理 121 | g_app = nullptr; 122 | LOG(INFO) << "launcher exit"; 123 | myframe::ShutdownLog(); 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /test/performance_trans20_fullspeed_test.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "myframe/log.h" 14 | #include "myframe/common.h" 15 | #include "myframe/msg.h" 16 | #include "myframe/actor.h" 17 | #include "myframe/mod_manager.h" 18 | #include "myframe/app.h" 19 | 20 | #include "performance_test_config.h" 21 | 22 | class FullSpeed20ActorTransTest : public myframe::Actor { 23 | public: 24 | FullSpeed20ActorTransTest() : msg_(8192, 'j') {} 25 | 26 | int Init() override { 27 | LOG(INFO) << "init full speed 20 actor trans "; 28 | // 启动测试 29 | msg_cnt_per_sec_list_.reserve(64); 30 | auto mailbox = GetMailbox(); 31 | mailbox->Send(GetActorName(), std::make_shared(msg_)); 32 | return 0; 33 | } 34 | 35 | void Proc(const std::shared_ptr& msg) override { 36 | (void)msg; 37 | if (!init_) { 38 | init_ = true; 39 | begin_ = std::chrono::high_resolution_clock::now(); 40 | last_ = std::chrono::high_resolution_clock::now(); 41 | } 42 | auto now = std::chrono::high_resolution_clock::now(); 43 | auto us = std::chrono::duration_cast(now - last_) 44 | .count(); 45 | cnt_++; 46 | if (us / 1000.0 > 1000.0) { 47 | LOG(INFO) << GetActorName() << ": full speed 20 actor msg count " << cnt_; 48 | msg_cnt_per_sec_list_.push_back(cnt_); 49 | cnt_ = 0; 50 | last_ = std::chrono::high_resolution_clock::now(); 51 | } 52 | auto sec = std::chrono::duration_cast( 53 | std::chrono::high_resolution_clock::now() - begin_) 54 | .count(); 55 | if (sec < 60) { 56 | auto mailbox = GetMailbox(); 57 | mailbox->Send(GetActorName(), std::make_shared()); 58 | } else { 59 | if (!is_send_) { 60 | is_send_.store(true); 61 | LOG(INFO) << "runing FullSpeed20ActorTransTest end"; 62 | int sum = std::accumulate(msg_cnt_per_sec_list_.begin(), 63 | msg_cnt_per_sec_list_.end(), 0); 64 | int avg = sum / msg_cnt_per_sec_list_.size(); 65 | LOG(INFO) << "20 actor fullspeed trans msg avg(cnt/sec): " << avg; 66 | std::sort(msg_cnt_per_sec_list_.begin(), msg_cnt_per_sec_list_.end()); 67 | LOG(INFO) << "20 actor fullspeed trans msg 99(cnt/sec): " 68 | << msg_cnt_per_sec_list_[ 69 | static_cast(msg_cnt_per_sec_list_.size() * 0.99)]; 70 | GetApp()->Quit(); 71 | } 72 | } 73 | } 74 | 75 | private: 76 | static std::atomic_bool is_send_; 77 | bool init_{false}; 78 | int cnt_{0}; 79 | std::chrono::high_resolution_clock::time_point begin_; 80 | std::chrono::high_resolution_clock::time_point last_; 81 | std::string msg_; 82 | std::vector msg_cnt_per_sec_list_; 83 | }; 84 | std::atomic_bool FullSpeed20ActorTransTest::is_send_{false}; 85 | 86 | int main() { 87 | auto lib_dir = 88 | myframe::Common::GetAbsolutePath(MYFRAME_LIB_DIR).string(); 89 | auto log_dir = 90 | myframe::Common::GetAbsolutePath(MYFRAME_LOG_DIR).string(); 91 | 92 | myframe::InitLog(log_dir, "performance_trans20_fullspeed_test"); 93 | 94 | auto app = std::make_shared(); 95 | if (false == app->Init(lib_dir, 4)) { 96 | LOG(ERROR) << "Init failed"; 97 | return -1; 98 | } 99 | 100 | // mod manager 101 | auto& mod = app->GetModManager(); 102 | 103 | // 20个actor消息吞吐量(测试时长1分钟,全速运行) 104 | mod->RegActor("FullSpeed20ActorTransTest", [](const std::string&) { 105 | return std::make_shared(); 106 | }); 107 | for (int i = 0; i < 20; ++i) { 108 | auto actor = mod->CreateActorInst("class", 109 | "FullSpeed20ActorTransTest", std::to_string(i)); 110 | app->AddActor(actor); 111 | } 112 | 113 | LOG(INFO) << "FullSpeed20ActorTransTest begin..."; 114 | return app->Exec(); 115 | } 116 | -------------------------------------------------------------------------------- /test/performance_trans100_fullspeed_test.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "myframe/log.h" 14 | #include "myframe/common.h" 15 | #include "myframe/msg.h" 16 | #include "myframe/actor.h" 17 | #include "myframe/mod_manager.h" 18 | #include "myframe/app.h" 19 | 20 | #include "performance_test_config.h" 21 | 22 | class FullSpeed100ActorTransTest : public myframe::Actor { 23 | public: 24 | FullSpeed100ActorTransTest() : msg_(8192, 'k') {} 25 | 26 | int Init() override { 27 | LOG(INFO) << "init full speed 100 actor trans "; 28 | // 启动测试 29 | msg_cnt_per_sec_list_.reserve(64); 30 | auto mailbox = GetMailbox(); 31 | mailbox->Send(GetActorName(), std::make_shared(msg_)); 32 | return 0; 33 | } 34 | 35 | void Proc(const std::shared_ptr& msg) override { 36 | (void)msg; 37 | if (!init_) { 38 | init_ = true; 39 | begin_ = std::chrono::high_resolution_clock::now(); 40 | last_ = std::chrono::high_resolution_clock::now(); 41 | } 42 | auto now = std::chrono::high_resolution_clock::now(); 43 | auto us = std::chrono::duration_cast(now - last_) 44 | .count(); 45 | cnt_++; 46 | if (us / 1000.0 > 1000.0) { 47 | LOG(INFO) << GetActorName() << ": full speed 100 actor msg count " 48 | << cnt_; 49 | msg_cnt_per_sec_list_.push_back(cnt_); 50 | cnt_ = 0; 51 | last_ = std::chrono::high_resolution_clock::now(); 52 | } 53 | auto sec = std::chrono::duration_cast( 54 | std::chrono::high_resolution_clock::now() - begin_) 55 | .count(); 56 | if (sec < 60) { 57 | auto mailbox = GetMailbox(); 58 | mailbox->Send(GetActorName(), std::make_shared()); 59 | } else { 60 | if (!is_send_) { 61 | is_send_.store(true); 62 | LOG(INFO) << "runing FullSpeed100ActorTransTest end"; 63 | int sum = std::accumulate(msg_cnt_per_sec_list_.begin(), 64 | msg_cnt_per_sec_list_.end(), 0); 65 | int avg = sum / msg_cnt_per_sec_list_.size(); 66 | LOG(INFO) << "100 actor fullspeed trans msg avg(cnt/sec): " << avg; 67 | std::sort(msg_cnt_per_sec_list_.begin(), msg_cnt_per_sec_list_.end()); 68 | LOG(INFO) << "100 actor fullspeed trans msg 99(cnt/sec): " 69 | << msg_cnt_per_sec_list_[ 70 | static_cast(msg_cnt_per_sec_list_.size() * 0.99)]; 71 | GetApp()->Quit(); 72 | } 73 | } 74 | } 75 | 76 | private: 77 | static std::atomic_bool is_send_; 78 | bool init_{false}; 79 | int cnt_{0}; 80 | std::chrono::high_resolution_clock::time_point begin_; 81 | std::chrono::high_resolution_clock::time_point last_; 82 | std::string msg_; 83 | std::vector msg_cnt_per_sec_list_; 84 | }; 85 | std::atomic_bool FullSpeed100ActorTransTest::is_send_{false}; 86 | 87 | int main() { 88 | auto lib_dir = 89 | myframe::Common::GetAbsolutePath(MYFRAME_LIB_DIR).string(); 90 | auto log_dir = 91 | myframe::Common::GetAbsolutePath(MYFRAME_LOG_DIR).string(); 92 | 93 | myframe::InitLog(log_dir, "performance_trans100_fullspeed_test"); 94 | 95 | auto app = std::make_shared(); 96 | if (false == app->Init(lib_dir, 4)) { 97 | LOG(ERROR) << "Init failed"; 98 | return -1; 99 | } 100 | 101 | // mod manager 102 | auto& mod = app->GetModManager(); 103 | 104 | // 100个actor消息吞吐量(测试时长1分钟,全速运行) 105 | mod->RegActor("FullSpeed100ActorTransTest", [](const std::string&) { 106 | return std::make_shared(); 107 | }); 108 | for (int i = 0; i < 100; ++i) { 109 | auto actor = mod->CreateActorInst("class", 110 | "FullSpeed100ActorTransTest", std::to_string(i)); 111 | app->AddActor(actor); 112 | } 113 | 114 | LOG(INFO) << "FullSpeed100ActorTransTest begin..."; 115 | return app->Exec(); 116 | } 117 | -------------------------------------------------------------------------------- /myframe/actor_context_manager.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | 8 | #include "myframe/actor_context_manager.h" 9 | 10 | #include 11 | #include 12 | 13 | #include "myframe/log.h" 14 | #include "myframe/msg.h" 15 | #include "myframe/actor.h" 16 | #include "myframe/actor_context.h" 17 | 18 | namespace myframe { 19 | 20 | ActorContextManager::ActorContextManager() : ctx_count_(0) { 21 | LOG(INFO) << "ActorContextManager create"; 22 | } 23 | 24 | ActorContextManager::~ActorContextManager() { 25 | LOG(INFO) << "ActorContextManager deconstruct"; 26 | } 27 | 28 | void ActorContextManager::DispatchMsg( 29 | std::shared_ptr msg, 30 | const std::string& dst) { 31 | std::string actor_name = dst.empty()? msg->GetDst() : dst; 32 | auto ctx = GetContext(actor_name); 33 | if (nullptr == ctx) { 34 | LOG(ERROR) << "Unknown msg " << *msg; 35 | return; 36 | } 37 | auto mailbox = ctx->GetMailbox(); 38 | mailbox->Recv(std::move(msg)); 39 | PushContext(std::move(ctx)); 40 | } 41 | 42 | bool ActorContextManager::Add(std::shared_ptr ctx) { 43 | std::unique_lock lk(rw_); 44 | if (ctxs_.find(ctx->GetActor()->GetActorName()) != ctxs_.end()) { 45 | LOG(WARNING) << "reg the same actor name: " 46 | << ctx->GetActor()->GetActorName(); 47 | return false; 48 | } 49 | ctxs_[ctx->GetActor()->GetActorName()] = std::move(ctx); 50 | return true; 51 | } 52 | 53 | std::shared_ptr ActorContextManager::GetContext( 54 | const std::string& actor_name) { 55 | std::shared_lock lk(rw_); 56 | auto p = ctxs_.find(actor_name); 57 | if (p == ctxs_.end()) { 58 | LOG(WARNING) << "not found " << actor_name; 59 | return nullptr; 60 | } 61 | return p->second; 62 | } 63 | 64 | std::vector ActorContextManager::GetAllActorAddr() { 65 | std::vector res; 66 | std::shared_lock lk(rw_); 67 | for (auto ctx : ctxs_) { 68 | res.push_back(ctx.first); 69 | } 70 | return res; 71 | } 72 | 73 | bool ActorContextManager::HasActor(const std::string& name) { 74 | std::shared_lock lk(rw_); 75 | return ctxs_.find(name) != ctxs_.end(); 76 | } 77 | 78 | void ActorContextManager::PrintWaitQueue() { 79 | if (!VLOG_IS_ON(1)) { 80 | return; 81 | } 82 | VLOG(1) << "cur wait queue actor:"; 83 | auto it = wait_queue_.begin(); 84 | while (it != wait_queue_.end()) { 85 | auto ctx = it->lock(); 86 | if (ctx == nullptr) { 87 | LOG(ERROR) << "context is nullptr"; 88 | continue; 89 | } 90 | VLOG(1) << "|--> " << *ctx; 91 | ++it; 92 | } 93 | } 94 | 95 | std::shared_ptr ActorContextManager::GetContextWithMsg() { 96 | if (wait_queue_.empty()) { 97 | return nullptr; 98 | } 99 | 100 | std::list> in_runing_context; 101 | std::shared_ptr ret = nullptr; 102 | while (!wait_queue_.empty()) { 103 | if (wait_queue_.front().expired()) { 104 | wait_queue_.pop_front(); 105 | continue; 106 | } 107 | auto ctx = wait_queue_.front().lock(); 108 | if (ctx->IsRuning()) { 109 | wait_queue_.pop_front(); 110 | VLOG(1) << ctx->GetActor()->GetActorName() 111 | << " is runing, move to wait queue back"; 112 | in_runing_context.push_back(std::move(ctx)); 113 | } else { 114 | wait_queue_.pop_front(); 115 | ctx->SetRuningFlag(true); 116 | ctx->SetWaitQueueFlag(false); 117 | ret.swap(ctx); 118 | break; 119 | } 120 | } 121 | if (!in_runing_context.empty()) { 122 | wait_queue_.splice(wait_queue_.end(), in_runing_context); 123 | } 124 | return ret; 125 | } 126 | 127 | void ActorContextManager::PushContext(std::shared_ptr ctx) { 128 | if (ctx->IsInWaitQueue()) { 129 | VLOG(1) << *ctx << " already in wait queue, return"; 130 | PrintWaitQueue(); 131 | return; 132 | } 133 | ctx->SetWaitQueueFlag(true); 134 | wait_queue_.push_back(std::move(ctx)); 135 | PrintWaitQueue(); 136 | } 137 | 138 | void ActorContextManager::ClearContext() { 139 | std::unique_lock lk(rw_); 140 | ctxs_.clear(); 141 | } 142 | 143 | } // namespace myframe 144 | -------------------------------------------------------------------------------- /myframe/app.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "myframe/macros.h" 18 | #include "myframe/event.h" 19 | #include "myframe/export.h" 20 | #include "myframe/common.h" 21 | 22 | namespace myframe { 23 | 24 | class Msg; 25 | class Poller; 26 | class Actor; 27 | class ActorContext; 28 | class ActorContextManager; 29 | class Event; 30 | class EventManager; 31 | class EventConn; 32 | class EventConnManager; 33 | class Worker; 34 | class WorkerContext; 35 | class WorkerCommon; 36 | class WorkerTimer; 37 | class WorkerContextManager; 38 | class ModManager; 39 | class MYFRAME_EXPORT App final : public std::enable_shared_from_this { 40 | friend class Actor; 41 | 42 | public: 43 | enum class State : std::uint8_t { 44 | kUninitialized = 0, 45 | kInitialized, 46 | kRunning, 47 | kQuitting, 48 | kQuit, 49 | }; 50 | 51 | App(); 52 | virtual ~App(); 53 | 54 | bool Init( 55 | const std::string& lib_dir, 56 | int thread_pool_size = 4, 57 | int event_conn_size = 2, 58 | int warning_msg_size = 10, 59 | int default_pending_queue_size = -1, 60 | int default_run_queue_size = 2); 61 | 62 | int LoadServiceFromDir(const std::string& path); 63 | 64 | bool LoadServiceFromFile(const std::string& file); 65 | 66 | bool LoadServiceFromJson(const Json::Value& service); 67 | 68 | bool AddActor( 69 | std::shared_ptr actor, 70 | const Json::Value& config = Json::Value::nullSingleton()); 71 | 72 | bool AddWorker( 73 | std::shared_ptr worker, 74 | const Json::Value& config = Json::Value::nullSingleton()); 75 | 76 | int Send(std::shared_ptr msg); 77 | 78 | const std::shared_ptr SendRequest( 79 | std::shared_ptr msg); 80 | 81 | std::unique_ptr& GetModManager(); 82 | 83 | int Exec(); 84 | 85 | void Quit(); 86 | 87 | int GetDefaultPendingQueueSize() const; 88 | int GetDefaultRunQueueSize() const; 89 | 90 | State GetState() const; 91 | std::vector GetAllUserModAddr() const; 92 | 93 | private: 94 | bool HasUserInst(const std::string& name); 95 | std::shared_ptr GetTimerWorker(); 96 | 97 | bool LoadActors( 98 | const std::string& mod_name, 99 | const std::string& actor_name, 100 | const Json::Value& actor_list); 101 | bool LoadWorkers( 102 | const std::string& mod_name, 103 | const std::string& worker_name, 104 | const Json::Value& worker_list); 105 | 106 | /// worker 107 | bool StartCommonWorker(int worker_count); 108 | bool StartTimerWorker(); 109 | 110 | /// 通知执行事件 111 | void CheckStopWorkers(); 112 | 113 | /// 分发消息 114 | void DispatchMsg(std::shared_ptr msg); 115 | void DispatchMsg(std::list>* msg_list); 116 | void DispatchMsg(std::shared_ptr context); 117 | /// 处理事件 118 | void ProcessEvent(const std::vector& evs); 119 | void ProcessWorkerEvent(std::shared_ptr); 120 | void ProcessTimerEvent(std::shared_ptr); 121 | void ProcessUserEvent(std::shared_ptr); 122 | void ProcessEventConn(std::shared_ptr); 123 | 124 | stdfs::path lib_dir_; 125 | std::vector name_list_; 126 | /// node地址 127 | std::string node_addr_; 128 | /// 129 | int default_pending_queue_size_{-1}; 130 | int default_run_queue_size_{2}; 131 | std::atomic warning_msg_size_{10}; 132 | std::atomic state_{State::kUninitialized}; 133 | std::recursive_mutex local_mtx_; 134 | /// 缓存消息列表 135 | std::list> cache_msgs_; 136 | /// 模块管理对象 137 | std::unique_ptr mods_; 138 | /// poller 139 | std::shared_ptr poller_; 140 | /// 句柄管理对象 141 | std::unique_ptr actor_ctx_mgr_; 142 | /// 事件管理对象 143 | std::shared_ptr ev_mgr_; 144 | /// 与框架通信管理对象 145 | std::unique_ptr ev_conn_mgr_; 146 | /// 线程管理对象 147 | std::unique_ptr worker_ctx_mgr_; 148 | 149 | DISALLOW_COPY_AND_ASSIGN(App) 150 | }; 151 | 152 | } // namespace myframe 153 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macOS 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build-macos: 7 | name: AppleClang-C++${{matrix.std}}-${{matrix.build_type}} 8 | runs-on: macos-latest 9 | permissions: 10 | actions: read 11 | contents: read 12 | security-events: write 13 | strategy: 14 | fail-fast: true 15 | matrix: 16 | std: [17, 20] 17 | include: 18 | - generator: Ninja 19 | - build_type: Debug 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - name: Setup Dependencies 25 | run: | 26 | brew install ninja cmake 27 | 28 | - name: Cache jsoncpp 29 | id: cache-jsoncpp 30 | uses: actions/cache@v4 31 | with: 32 | path: jsoncpp/ 33 | key: ${{runner.os}}-jsoncpp-1.9.5 34 | 35 | - name: Download jsoncpp 36 | shell: bash 37 | if: steps.cache-jsoncpp.outputs.cache-hit != 'true' 38 | run: | 39 | wget https://github.com/open-source-parsers/jsoncpp/archive/refs/tags/1.9.5.tar.gz 40 | tar xvf 1.9.5.tar.gz 41 | 42 | - name: Build jsoncpp 43 | if: steps.cache-jsoncpp.outputs.cache-hit != 'true' 44 | run: | 45 | cmake -S jsoncpp-1.9.5 -B build-jsoncpp \ 46 | -DBUILD_SHARED_LIBS=ON \ 47 | -DBUILD_STATIC_LIBS=OFF \ 48 | -DBUILD_OBJECT_LIBS=OFF \ 49 | -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ 50 | -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/jsoncpp \ 51 | -DJSONCPP_WITH_TESTS=OFF \ 52 | -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF \ 53 | -DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF \ 54 | -G "${{matrix.generator}}" 55 | cmake --build build-jsoncpp --target install 56 | 57 | - name: Cache gflags 58 | id: cache-gflags 59 | uses: actions/cache@v4 60 | with: 61 | path: gflags/ 62 | key: ${{runner.os}}-gflags-2.2.2 63 | 64 | - name: Download gflags 65 | shell: bash 66 | if: steps.cache-gflags.outputs.cache-hit != 'true' 67 | run: | 68 | wget https://github.com/gflags/gflags/archive/refs/tags/v2.2.2.tar.gz 69 | tar xvf v2.2.2.tar.gz 70 | 71 | - name: Build gflags 72 | if: steps.cache-gflags.outputs.cache-hit != 'true' 73 | run: | 74 | cmake -S gflags-2.2.2 -B build-gflags \ 75 | -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ 76 | -DBUILD_SHARED_LIBS=ON \ 77 | -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ 78 | -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/gflags \ 79 | -G "${{matrix.generator}}" 80 | cmake --build build-gflags --target install 81 | 82 | - name: Cache glog 83 | id: cache-glog 84 | uses: actions/cache@v4 85 | with: 86 | path: glog/ 87 | key: ${{runner.os}}-glog-0.6.0 88 | 89 | - name: Download glog 90 | shell: bash 91 | if: steps.cache-glog.outputs.cache-hit != 'true' 92 | run: | 93 | wget https://github.com/google/glog/archive/refs/tags/v0.6.0.tar.gz 94 | tar xvf v0.6.0.tar.gz 95 | 96 | - name: Build glog 97 | if: steps.cache-glog.outputs.cache-hit != 'true' 98 | run: | 99 | cmake -S glog-0.6.0 -B build-glog \ 100 | -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ 101 | -DBUILD_SHARED_LIBS=ON \ 102 | -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ 103 | -DWITH_PKGCONFIG=OFF \ 104 | -DWITH_GTEST=OFF \ 105 | -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/glog \ 106 | -DCMAKE_PREFIX_PATH="${{github.workspace}}/gflags" \ 107 | -G "${{matrix.generator}}" 108 | cmake --build build-glog --target install 109 | 110 | - name: Configure 111 | run: | 112 | cmake -S . -B build_${{matrix.build_type}} \ 113 | -DCMAKE_POLICY_VERSION_MINIMUM=3.5 \ 114 | -DCMAKE_CXX_EXTENSIONS=OFF \ 115 | -DCMAKE_CXX_STANDARD=${{matrix.std}} \ 116 | -DCMAKE_CXX_STANDARD_REQUIRED=ON \ 117 | -DMYFRAME_USE_CV=ON \ 118 | -DCMAKE_PREFIX_PATH="${{github.workspace}}/jsoncpp;${{github.workspace}}/glog;${{github.workspace}}/gflags" \ 119 | -G "${{matrix.generator}}" 120 | 121 | - name: Build 122 | run: | 123 | cmake --build build_${{matrix.build_type}} \ 124 | --config ${{matrix.build_type}} 125 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: Linux 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | Ubuntu-latest: 7 | name: GCC-C++${{matrix.std}}-${{matrix.build_type}} 8 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 9 | # You can convert this to a matrix build if you need cross-platform coverage. 10 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 11 | runs-on: ubuntu-latest 12 | strategy: 13 | fail-fast: true 14 | matrix: 15 | build_type: [Release, Debug] 16 | std: [17, 20] 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | - run: sudo apt update 21 | - run: sudo apt install build-essential cmake ninja-build 22 | - run: sudo apt install libunwind-dev libgflags-dev 23 | 24 | - name: Setup Ninja 25 | uses: ashutoshvarma/setup-ninja@master 26 | with: 27 | version: 1.10.0 28 | 29 | - name: Cache jsoncpp 30 | id: cache-jsoncpp 31 | uses: actions/cache@v4 32 | with: 33 | path: jsoncpp/ 34 | key: ${{runner.os}}-jsoncpp-1.9.5 35 | 36 | - name: Download jsoncpp 37 | if: steps.cache-jsoncpp.outputs.cache-hit != 'true' 38 | run: | 39 | wget https://github.com/open-source-parsers/jsoncpp/archive/refs/tags/1.9.5.tar.gz 40 | tar xvf 1.9.5.tar.gz 41 | 42 | - name: Build jsoncpp 43 | if: steps.cache-jsoncpp.outputs.cache-hit != 'true' 44 | run: | 45 | cmake -S jsoncpp-1.9.5 -B build-jsoncpp \ 46 | -DBUILD_SHARED_LIBS=ON \ 47 | -DBUILD_STATIC_LIBS=OFF \ 48 | -DBUILD_OBJECT_LIBS=OFF \ 49 | -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ 50 | -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/jsoncpp \ 51 | -DJSONCPP_WITH_TESTS=OFF \ 52 | -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF \ 53 | -DJSONCPP_WITH_PKGCONFIG_SUPPORT=OFF \ 54 | -G Ninja 55 | cmake --build build-jsoncpp --target install 56 | 57 | - name: Cache glog 58 | id: cache-glog 59 | uses: actions/cache@v4 60 | with: 61 | path: glog/ 62 | key: ${{runner.os}}-glog-0.6.0 63 | 64 | - name: Download glog 65 | if: steps.cache-glog.outputs.cache-hit != 'true' 66 | run: | 67 | wget https://github.com/google/glog/archive/refs/tags/v0.6.0.tar.gz 68 | tar xvf v0.6.0.tar.gz 69 | 70 | - name: Build glog 71 | if: steps.cache-glog.outputs.cache-hit != 'true' 72 | run: | 73 | cmake -S glog-0.6.0 -B build-glog \ 74 | -DBUILD_SHARED_LIBS=ON \ 75 | -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ 76 | -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/glog \ 77 | -DWITH_PKGCONFIG=OFF \ 78 | -DWITH_GTEST=OFF \ 79 | -G Ninja 80 | cmake --build build-glog --target install 81 | 82 | - name: Configure CMake 83 | env: 84 | # unit test option: 85 | # -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined 86 | CXXFLAGS: ${{env.CXXFLAGS}} -Wall -Wextra -Werror -pedantic-errors -Wswitch-default -Wfloat-equal -Wshadow -Wcast-qual -Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virtual -Wsign-promo -Wsuggest-override -Wextra-semi 87 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 88 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 89 | run: | 90 | cmake -S . -B ${{github.workspace}}/build_${{matrix.build_type}} \ 91 | -DBUILD_SHARED_LIBS=ON \ 92 | -DCMAKE_CXX_STANDARD=${{matrix.std}} \ 93 | -DCMAKE_CXX_STANDARD_REQUIRED=ON \ 94 | -DMYFRAME_USE_CV=OFF \ 95 | -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ 96 | -DCMAKE_PREFIX_PATH="${{github.workspace}}/jsoncpp;${{github.workspace}}/glog" \ 97 | -DCMAKE_INSTALL_PREFIX=${{github.workspace}}/install \ 98 | -G Ninja \ 99 | -Werror 100 | 101 | - name: Build 102 | # Build your program with the given configuration 103 | run: cmake --build ${{github.workspace}}/build_${{matrix.build_type}} --config ${{matrix.build_type}} 104 | 105 | - name: Install 106 | run: | 107 | cmake --build build_${{matrix.build_type}} \ 108 | --config ${{matrix.build_type}} \ 109 | --target install 110 | -------------------------------------------------------------------------------- /test/performance_trans10_cost_test.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "myframe/log.h" 14 | #include "myframe/common.h" 15 | #include "myframe/msg.h" 16 | #include "myframe/actor.h" 17 | #include "myframe/mod_manager.h" 18 | #include "myframe/app.h" 19 | 20 | #include "performance_test_config.h" 21 | 22 | 23 | class Trans10ActorCostTest : public myframe::Actor { 24 | public: 25 | Trans10ActorCostTest() : msg_(8192, 'y') {} 26 | 27 | int Init() override { 28 | task_num_ = GetConfig()->get("task_num", 0).asInt(); 29 | // 启动测试 30 | if (task_num_ == 0) { 31 | cost_us_list_.reserve(6000); 32 | auto mailbox = GetMailbox(); 33 | mailbox->Send("actor.Trans10ActorCostTest.0", 34 | std::make_shared(msg_)); 35 | } 36 | LOG(INFO) << "init trans msg num " << task_num_; 37 | return 0; 38 | } 39 | 40 | void Proc(const std::shared_ptr& msg) override { 41 | (void)msg; 42 | if (!init_) { 43 | init_ = true; 44 | total_ = std::chrono::high_resolution_clock::now(); 45 | } 46 | if (task_num_ == 0) { 47 | begin_ = std::chrono::high_resolution_clock::now(); 48 | } 49 | if (task_num_ == 9) { 50 | auto now = std::chrono::high_resolution_clock::now(); 51 | auto us = 52 | std::chrono::duration_cast(now - begin_) 53 | .count(); 54 | LOG(INFO) << GetActorName() << " trans 10 actor msg cost(us) " << us; 55 | cost_us_list_.push_back(us); 56 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 57 | begin_ = std::chrono::high_resolution_clock::now(); 58 | } 59 | auto sec = std::chrono::duration_cast( 60 | std::chrono::high_resolution_clock::now() - total_) 61 | .count(); 62 | std::string next_actor_name = 63 | "actor.Trans10ActorCostTest." + std::to_string((task_num_ + 1) % 10); 64 | if (sec < 60) { 65 | auto mailbox = GetMailbox(); 66 | mailbox->Send(next_actor_name, std::make_shared(msg_)); 67 | } else { 68 | LOG(INFO) << "runing Trans10ActorCostTest end"; 69 | // 耗时 70 | // 平均值: 71 | // 99分位: 72 | int sum = std::accumulate(cost_us_list_.begin(), cost_us_list_.end(), 0); 73 | int avg = sum / cost_us_list_.size(); 74 | LOG(INFO) << "trans 10 actor cnt: " << cost_us_list_.size(); 75 | LOG(INFO) << "trans 10 actor avg(us): " << avg; 76 | std::sort(cost_us_list_.begin(), cost_us_list_.end()); 77 | LOG(INFO) << "trans 10 actor 99(us): " << 78 | cost_us_list_[ 79 | static_cast(cost_us_list_.size() * 0.99)]; 80 | GetApp()->Quit(); 81 | } 82 | } 83 | 84 | private: 85 | static bool init_; 86 | static std::chrono::high_resolution_clock::time_point total_; 87 | static std::chrono::high_resolution_clock::time_point begin_; 88 | static std::vector cost_us_list_; 89 | int task_num_{0}; 90 | std::string msg_; 91 | }; 92 | bool Trans10ActorCostTest::init_{false}; 93 | std::chrono::high_resolution_clock::time_point Trans10ActorCostTest::total_; 94 | std::chrono::high_resolution_clock::time_point Trans10ActorCostTest::begin_; 95 | std::vector Trans10ActorCostTest::cost_us_list_; 96 | 97 | 98 | int main() { 99 | auto lib_dir = 100 | myframe::Common::GetAbsolutePath(MYFRAME_LIB_DIR).string(); 101 | auto log_dir = 102 | myframe::Common::GetAbsolutePath(MYFRAME_LOG_DIR).string(); 103 | 104 | myframe::InitLog(log_dir, "performance_trans10_cost_test"); 105 | 106 | auto app = std::make_shared(); 107 | if (false == app->Init(lib_dir, 4)) { 108 | LOG(ERROR) << "Init failed"; 109 | return -1; 110 | } 111 | 112 | // mod manager 113 | auto& mod = app->GetModManager(); 114 | 115 | // 消息流转10个actor耗时(测试时长1分钟,每隔10毫秒发送1条消息) 116 | // 耗时 117 | // 平均值: 118 | // 99分位: 119 | mod->RegActor("Trans10ActorCostTest", [](const std::string&) { 120 | return std::make_shared(); 121 | }); 122 | for (int i = 0; i < 10; ++i) { 123 | auto actor = mod->CreateActorInst("class", 124 | "Trans10ActorCostTest", std::to_string(i)); 125 | Json::Value conf; 126 | conf["task_num"] = i; 127 | app->AddActor(actor, conf); 128 | } 129 | 130 | return app->Exec(); 131 | } 132 | -------------------------------------------------------------------------------- /myframe/platform/cmd_channel_linux.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | Copyright (c) 2019, 李柯鹏 3 | All rights reserved. 4 | 5 | Author: 李柯鹏 6 | ****************************************************************************/ 7 | #pragma once 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "myframe/log.h" 15 | #include "myframe/export.h" 16 | #include "myframe/macros.h" 17 | #include "myframe/event.h" 18 | #include "myframe/cmd_channel.h" 19 | 20 | namespace myframe { 21 | 22 | class CmdChannelLinux final : public CmdChannel { 23 | public: 24 | explicit CmdChannelLinux(std::shared_ptr); 25 | virtual ~CmdChannelLinux(); 26 | 27 | ev_handle_t GetOwnerHandle() const override; 28 | ev_handle_t GetMainHandle() const override; 29 | 30 | int SendToOwner(const Cmd& cmd) override; 31 | int RecvFromOwner(Cmd* cmd) override; 32 | 33 | int SendToMain(const Cmd& cmd) override; 34 | int RecvFromMain(Cmd* cmd, int timeout_ms = -1) override; 35 | 36 | private: 37 | void CreateSockpair(); 38 | void CloseSockpair(); 39 | 40 | bool SetSockRecvTimeout(int fd, int timeout_ms) const; 41 | bool SetNonblockFd(int fd, bool b) const; 42 | bool IsBlockFd(int fd) const; 43 | 44 | ev_handle_t sockpair_[2] {-1, -1}; 45 | 46 | DISALLOW_COPY_AND_ASSIGN(CmdChannelLinux) 47 | }; 48 | 49 | CmdChannelLinux::CmdChannelLinux(std::shared_ptr poller) 50 | : CmdChannel(poller) { 51 | CreateSockpair(); 52 | } 53 | 54 | CmdChannelLinux::~CmdChannelLinux() { 55 | CloseSockpair(); 56 | } 57 | 58 | ev_handle_t CmdChannelLinux::GetOwnerHandle() const { 59 | return sockpair_[0]; 60 | } 61 | 62 | ev_handle_t CmdChannelLinux::GetMainHandle() const { 63 | return sockpair_[1]; 64 | } 65 | 66 | void CmdChannelLinux::CreateSockpair() { 67 | int res = -1; 68 | res = socketpair(AF_UNIX, SOCK_DGRAM, 0, sockpair_); 69 | if (res) { 70 | LOG(ERROR) << "create sockpair failed"; 71 | return; 72 | } 73 | if (!SetNonblockFd(sockpair_[0], false)) { 74 | LOG(ERROR) << "set sockpair[0] block failed, " << strerror(errno); 75 | return; 76 | } 77 | if (!SetNonblockFd(sockpair_[1], false)) { 78 | LOG(ERROR) << "set sockpair[1] block failed, " << strerror(errno); 79 | return; 80 | } 81 | } 82 | 83 | void CmdChannelLinux::CloseSockpair() { 84 | if (close(sockpair_[0])) { 85 | LOG(ERROR) << "close sockpair[0]: " << strerror(errno); 86 | } 87 | if (close(sockpair_[1])) { 88 | LOG(ERROR) << "close sockpair[1]: " << strerror(errno); 89 | } 90 | } 91 | 92 | int CmdChannelLinux::SendToOwner(const Cmd& cmd) { 93 | char cmd_char = static_cast(cmd); 94 | int ret = write(sockpair_[1], &cmd_char, 1); 95 | if (ret < 0) { 96 | LOG(ERROR) << "write 1 cmd failed, " << strerror(errno); 97 | } 98 | return ret; 99 | } 100 | 101 | int CmdChannelLinux::RecvFromOwner(Cmd* cmd) { 102 | char cmd_char; 103 | int ret = read(sockpair_[1], &cmd_char, 1); 104 | if (ret < 0) { 105 | LOG(ERROR) << "read 1 cmd failed, " << strerror(errno); 106 | return ret; 107 | } 108 | *cmd = static_cast(cmd_char); 109 | return ret; 110 | } 111 | 112 | int CmdChannelLinux::RecvFromMain(Cmd* cmd, int timeout_ms) { 113 | (void)timeout_ms; 114 | if (timeout_ms < 0) { 115 | // block 116 | if (!IsBlockFd(sockpair_[0])) { 117 | SetNonblockFd(sockpair_[0], false); 118 | } 119 | } else if (timeout_ms == 0) { 120 | // nonblock 121 | if (IsBlockFd(sockpair_[0])) { 122 | SetNonblockFd(sockpair_[0], true); 123 | } 124 | } else { 125 | // timeout 126 | SetSockRecvTimeout(sockpair_[0], timeout_ms); 127 | } 128 | char cmd_char; 129 | int ret = read(sockpair_[0], &cmd_char, 1); 130 | if (ret < 0) { 131 | LOG(ERROR) << "read 0 cmd failed, " << strerror(errno); 132 | return ret; 133 | } 134 | *cmd = static_cast(cmd_char); 135 | return ret; 136 | } 137 | 138 | int CmdChannelLinux::SendToMain(const Cmd& cmd) { 139 | char cmd_char = static_cast(cmd); 140 | int ret = write(sockpair_[0], &cmd_char, 1); 141 | if (ret < 0) { 142 | LOG(ERROR) << "write 0 cmd failed, " << strerror(errno); 143 | } 144 | return ret; 145 | } 146 | 147 | bool CmdChannelLinux::SetSockRecvTimeout(int fd, int timeout_ms) const { 148 | struct timeval timeout = {timeout_ms / 1000, (timeout_ms % 1000) * 1000}; 149 | return 0 == setsockopt( 150 | fd, SOL_SOCKET, SO_RCVTIMEO, 151 | reinterpret_cast(&timeout), 152 | sizeof(timeout)); 153 | } 154 | 155 | bool CmdChannelLinux::SetNonblockFd(int fd, bool b) const { 156 | int flags = fcntl(fd, F_GETFL, 0); 157 | if (b) { 158 | flags |= O_NONBLOCK; 159 | } else { 160 | flags &= ~O_NONBLOCK; 161 | } 162 | return fcntl(fd, F_SETFL, flags) != -1; 163 | } 164 | 165 | bool CmdChannelLinux::IsBlockFd(int fd) const { 166 | int flags = fcntl(fd, F_GETFL, 0); 167 | return !(flags & O_NONBLOCK); 168 | } 169 | 170 | } // namespace myframe 171 | --------------------------------------------------------------------------------