├── .circleci └── config.yml ├── .clang-format ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── appveyor.yml ├── cmake ├── Modules │ └── FindLibev.cmake └── rotor-config.cmake.in ├── conanfile.py ├── docs ├── Changelog.md ├── Compilation.md ├── Design.md ├── Doxyfile.in ├── Examples.md ├── Introduction.md ├── License.md ├── Loops.md ├── Patterns.md ├── Rationale.md ├── References.md ├── extra.css ├── header.html ├── lifecycle-full.drawio ├── lifecycle-full.png ├── lifecycle-simplified.drawio ├── lifecycle-simplified.png ├── supervising.dot └── supervising.png ├── examples ├── CMakeLists.txt ├── autoshutdown.cpp ├── boost-asio │ ├── CMakeLists.txt │ ├── beast-scrapper.cpp │ ├── hello_asio.cpp │ ├── one-shot-lambda-subscriber.cpp │ ├── ping-pong-2-threads-preemt.cpp │ ├── ping-pong-2-threads.cpp │ ├── ping-pong-single-simple.cpp │ ├── ping-pong-timer.cpp │ ├── request-response-discovery.cpp │ └── request-response.cpp ├── dummy_supervisor.h ├── escalate-failure.cpp ├── ev │ ├── CMakeLists.txt │ ├── ping-pong-comp.cpp │ ├── ping-pong-ev-2-threads.cpp │ ├── ping-pong-ev.cpp │ ├── ping-pong-req.cpp │ └── pong-registry.cpp ├── hello_loopless.cpp ├── ping-pong-ev_and_asio.cpp ├── ping-pong-fltk.cpp ├── ping-pong-fltk_and_thread.cpp ├── ping_pong-lambda.cpp ├── ping_pong.cpp ├── pub_sub.cpp └── thread │ ├── CMakeLists.txt │ ├── ping-pong-spawner.cpp │ ├── ping-pong-thread.cpp │ └── sha512.cpp ├── include ├── rotor.hpp └── rotor │ ├── actor_base.h │ ├── actor_config.h │ ├── address.hpp │ ├── address_mapping.h │ ├── arc.hpp │ ├── asio.hpp │ ├── asio │ ├── forwarder.hpp │ ├── supervisor_asio.h │ ├── supervisor_config_asio.h │ └── system_context_asio.h │ ├── detail │ └── child_info.h │ ├── error_code.h │ ├── ev.hpp │ ├── ev │ ├── supervisor_config_ev.h │ ├── supervisor_ev.h │ └── system_context_ev.h │ ├── extended_error.h │ ├── fltk.hpp │ ├── fltk │ ├── supervisor_config_fltk.h │ ├── supervisor_fltk.h │ └── system_context_fltk.h │ ├── forward.hpp │ ├── handler.h │ ├── message.h │ ├── message_stringifier.h │ ├── messages.hpp │ ├── misc │ └── default_stringifier.h │ ├── plugin │ ├── address_maker.h │ ├── child_manager.h │ ├── delivery.h │ ├── foreigners_support.h │ ├── init_shutdown.h │ ├── lifetime.h │ ├── link_client.h │ ├── link_server.h │ ├── locality.h │ ├── plugin_base.h │ ├── registry.h │ ├── resources.h │ └── starter.h │ ├── plugins.h │ ├── policy.h │ ├── registry.h │ ├── request.hpp │ ├── spawner.h │ ├── state.h │ ├── subscription.h │ ├── subscription_point.h │ ├── supervisor.h │ ├── supervisor_config.h │ ├── system_context.h │ ├── thread.hpp │ ├── thread │ ├── supervisor_thread.h │ └── system_context_thread.h │ ├── timer_handler.hpp │ ├── wx.hpp │ └── wx │ ├── supervisor_config_wx.h │ ├── supervisor_wx.h │ └── system_context_wx.h ├── src └── rotor │ ├── actor_base.cpp │ ├── address_mapping.cpp │ ├── asio │ └── supervisor_asio.cpp │ ├── detail │ └── child_info.cpp │ ├── error_code.cpp │ ├── ev │ ├── supervisor_ev.cpp │ └── system_context_ev.cpp │ ├── extended_error.cpp │ ├── fltk │ ├── supervisor_fltk.cpp │ └── system_context_fltk.cpp │ ├── handler.cpp │ ├── message.cpp │ ├── message_stringifier.cpp │ ├── messages.cpp │ ├── misc │ └── default_stringifier.cpp │ ├── plugin │ ├── address_maker.cpp │ ├── child_manager.cpp │ ├── delivery.cpp │ ├── foreigners_support.cpp │ ├── init_shutdown.cpp │ ├── lifetime.cpp │ ├── link_client.cpp │ ├── link_server.cpp │ ├── locality.cpp │ ├── plugin_base.cpp │ ├── registry.cpp │ ├── resources.cpp │ └── starter.cpp │ ├── registry.cpp │ ├── spawner.cpp │ ├── subscription.cpp │ ├── subscription_point.cpp │ ├── supervisor.cpp │ ├── system_context.cpp │ ├── thread │ ├── supervisor_thread.cpp │ └── system_context_thread.cpp │ └── wx │ ├── supervisor_wx.cpp │ └── system_context_wx.cpp ├── test_package ├── CMakeLists.txt ├── conanfile.py └── test_package.cpp └── tests ├── 007-plugins.cpp ├── 008-config-builder.cpp ├── 009-system-context.cpp ├── 010-sup-start_stop.cpp ├── 011-ping_pong.cpp ├── 012-actor-lifetime.cpp ├── 013-observer.cpp ├── 014-lifetime-observer.cpp ├── 015-resources.cpp ├── 016-pub_sub.cpp ├── 017-req-res.cpp ├── 018-lambda-handlers.cpp ├── 019-link-unlink.cpp ├── 020-two-supervisors.cpp ├── 021-ping_pong-external.cpp ├── 022-supervisor-tree.cpp ├── 023-supervisor-children.cpp ├── 024-supervisor-spawner.cpp ├── 030-registry.cpp ├── 031-next-address.cpp ├── 098-message-stringifier.cpp ├── 099-misc.cpp ├── 101-asio_ping-pong-1-strand.cpp ├── 102-asio_ping-pong-2-strands.cpp ├── 103-asio_ping-pong-2-threads.cpp ├── 104-asio_timer.cpp ├── 121-wx_ping_ping.cpp ├── 122-wx_timer.cpp ├── 131-ev_ping-pong.cpp ├── 132-ev_timer.cpp ├── 141-thread_ping-pong.cpp ├── 142-thread_timer.cpp ├── 143-thread-shutdown_flag.cpp ├── 151-fltk-ping-pong.cpp ├── CMakeLists.txt ├── access.cpp ├── access.h ├── actor_test.cpp ├── actor_test.h ├── supervisor_asio_test.h ├── supervisor_test.cpp ├── supervisor_test.h ├── supervisor_wx_test.h └── system_context_test.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: true 6 | AlignConsecutiveAssignments: false 7 | AlignEscapedNewlinesLeft: false 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: All 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakBeforeMultilineStrings: false 18 | AlwaysBreakTemplateDeclarations: false 19 | BinPackArguments: true 20 | BinPackParameters: true 21 | BreakBeforeBinaryOperators: None 22 | BreakBeforeBraces: Attach 23 | BreakBeforeTernaryOperators: true 24 | BreakConstructorInitializersBeforeComma: false 25 | ColumnLimit: 120 26 | CommentPragmas: '^ IWYU pragma:' 27 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 28 | ConstructorInitializerIndentWidth: 4 29 | ContinuationIndentWidth: 4 30 | Cpp11BracedListStyle: true 31 | DerivePointerAlignment: false 32 | DisableFormat: false 33 | ExperimentalAutoDetectBinPacking: false 34 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 35 | IndentCaseLabels: false 36 | IndentWidth: 4 37 | IndentWrappedFunctionNames: false 38 | KeepEmptyLinesAtTheStartOfBlocks: true 39 | MacroBlockBegin: '' 40 | MacroBlockEnd: '' 41 | MaxEmptyLinesToKeep: 1 42 | NamespaceIndentation: None 43 | ObjCBlockIndentWidth: 4 44 | ObjCSpaceAfterProperty: false 45 | ObjCSpaceBeforeProtocolList: true 46 | PenaltyBreakBeforeFirstCallParameter: 19 47 | PenaltyBreakComment: 300 48 | PenaltyBreakFirstLessLess: 120 49 | PenaltyBreakString: 1000 50 | PenaltyExcessCharacter: 1000000 51 | PenaltyReturnTypeOnItsOwnLine: 60 52 | PointerAlignment: Right 53 | SortIncludes: false 54 | SpaceAfterCStyleCast: false 55 | SpaceBeforeAssignmentOperators: true 56 | SpaceBeforeParens: ControlStatements 57 | SpaceInEmptyParentheses: false 58 | SpacesBeforeTrailingComments: 1 59 | SpacesInAngles: false 60 | SpacesInContainerLiterals: true 61 | SpacesInCStyleCastParentheses: false 62 | SpacesInParentheses: false 63 | SpacesInSquareBrackets: false 64 | Standard: Cpp11 65 | TabWidth: 8 66 | UseTab: Never 67 | ... 68 | 69 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "doxygen"] 2 | path = doxygen 3 | url = https://github.com/basiliscos/cpp-rotor-docs.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ivan Baidakou 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/Modules/FindLibev.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find libev 2 | # Once done this will define 3 | # LIBEV_FOUND - System has libev 4 | # LIBEV_INCLUDE_DIRS - The libev include directories 5 | # LIBEV_LIBRARIES - The libraries needed to use libev 6 | 7 | find_path(LIBEV_INCLUDE_DIR 8 | NAMES ev.h 9 | ) 10 | find_library(LIBEV_LIBRARY 11 | NAMES ev 12 | ) 13 | 14 | if(LIBEV_INCLUDE_DIR) 15 | file(STRINGS "${LIBEV_INCLUDE_DIR}/ev.h" 16 | LIBEV_VERSION_MAJOR REGEX "^#define[ \t]+EV_VERSION_MAJOR[ \t]+[0-9]+") 17 | file(STRINGS "${LIBEV_INCLUDE_DIR}/ev.h" 18 | LIBEV_VERSION_MINOR REGEX "^#define[ \t]+EV_VERSION_MINOR[ \t]+[0-9]+") 19 | string(REGEX REPLACE "[^0-9]+" "" LIBEV_VERSION_MAJOR "${LIBEV_VERSION_MAJOR}") 20 | string(REGEX REPLACE "[^0-9]+" "" LIBEV_VERSION_MINOR "${LIBEV_VERSION_MINOR}") 21 | set(LIBEV_VERSION "${LIBEV_VERSION_MAJOR}.${LIBEV_VERSION_MINOR}") 22 | unset(LIBEV_VERSION_MINOR) 23 | unset(LIBEV_VERSION_MAJOR) 24 | endif() 25 | 26 | include(FindPackageHandleStandardArgs) 27 | # handle the QUIETLY and REQUIRED arguments and set LIBEV_FOUND to TRUE 28 | # if all listed variables are TRUE and the requested version matches. 29 | find_package_handle_standard_args(Libev REQUIRED_VARS 30 | LIBEV_LIBRARY LIBEV_INCLUDE_DIR 31 | VERSION_VAR LIBEV_VERSION) 32 | 33 | if(LIBEV_FOUND) 34 | set(LIBEV_LIBRARIES ${LIBEV_LIBRARY}) 35 | set(LIBEV_INCLUDE_DIRS ${LIBEV_INCLUDE_DIR}) 36 | endif() 37 | 38 | mark_as_advanced(LIBEV_INCLUDE_DIR LIBEV_LIBRARY) 39 | -------------------------------------------------------------------------------- /cmake/rotor-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(${CMAKE_CURRENT_LIST_DIR}/rotor-targets.cmake) 4 | -------------------------------------------------------------------------------- /docs/Compilation.md: -------------------------------------------------------------------------------- 1 | # Compiling & building 2 | 3 | ## Conan 4 | 5 | The recommended way to install [rotor](https://github.com/basiliscos/cpp-rotor/) is to 6 | use [conan](https://conan.io/center/rotor) package manager. 7 | 8 | Among the [various ways](https://docs.conan.io/en/latest/using_packages.html) to 9 | [include rotor](https://conan.io/center/rotor?tab=recipe) in a project, but the 10 | most trivial one is to just check install it to local conan cache via: 11 | 12 | ~~~bash 13 | conan install rotor/0.26 14 | ~~~ 15 | 16 | Please note, that it might take some some time before the new `rotor` release 17 | appears in [conan center](https://conan.io/center/rotor) as this is intentionally 18 | non-automated and human-supervised process for inclusion. 19 | 20 | ## Dependencies 21 | 22 | [boost-smartptr]: https://www.boost.org/doc/libs/release/libs/smart_ptr/ "Boost Smart Pointers" 23 | [boost-asio]: https://www.boost.org/doc/libs/release/libs/asio/ "Boost Asio" 24 | [wx-widgets]: https://www.wxwidgets.org/ "wxWidgets" 25 | [libev]: http://software.schmorp.de/pkg/libev.html "libev" 26 | 27 | C++ 17 is required to use rotor. 28 | 29 | The core dependency `rotor-core` needs intrusive pointer support from [boost-smartptr] 30 | and `boost::posix_time::time_duration`. (That might be changed in future, PRs are welcome). 31 | 32 | The optional event-loop specific supervisors depend on corresponding loop libraries, i.e. 33 | `rotor-asio` depends on [boost-asio]; the `rotor-wx` depends [wx-widgets]. 34 | 35 | ## Compiling 36 | 37 | `rotor` uses `cmake` for building; it supports the following building options 38 | 39 | - `ROTOR_BUILD_ASIO` - build with [boost-asio] support (`off` by default) 40 | - `ROTOR_BUILD_WX` build with [wx-widgets] support (`off` by default) 41 | - `ROTOR_BUILD_EV` build with [libev] support (`off` by default) 42 | - `ROTOR_BUILD_EXAMPLES` build examples (`off` by default) 43 | - `ROTOR_BUILD_DOC` generate doxygen documentation (`off` by default, only for release builds) 44 | - `ROTOR_BUILD_THREAD_UNSAFE` builds thread-unsafe library (`off` by default). Enable this option if you are sure, that 45 | rotor-objects (i.e. messages and actors) are accessed only from single thread. 46 | - `ROTOR_DEBUG_DELIVERY` allow runtime messages inspection (`off` by default, enabled by default for debug builds) 47 | 48 | ~~~bash 49 | git clone https://github.com/basiliscos/cpp-rotor rotor 50 | cd rotor 51 | mkdir build 52 | cd build 53 | cmake --build .. --config Release -DROTOR_BUILD_ASIO=on -DROTOR_BUILD_WX=on 54 | ~~~ 55 | 56 | ## Adding rotor into a project (modern way) 57 | 58 | Your `CMakeLists.txt` should have something like 59 | 60 | ~~~cmake 61 | include(FetchContent) 62 | 63 | set(ROTOR_BUILD_ASIO ON CACHE BOOL "with asio") # pick options you need 64 | FetchContent_Declare( 65 | rotor 66 | GIT_REPOSITORY https://github.com/basiliscos/cpp-rotor.git 67 | GIT_TAG v0.07 68 | ) 69 | FetchContent_MakeAvailable(rotor) 70 | 71 | target_include_directories(my_target PUBLIC ${rotor_SOURCE_DIR}/include) 72 | target_link_libraries(my_target rotor::asio) 73 | ~~~ 74 | 75 | ## Adding rotor into a project (classical way) 76 | 77 | ~~~bash 78 | git clone https://github.com/basiliscos/cpp-rotor.git external/rotor lib/rotor --branch=v0.09 79 | ~~~ 80 | 81 | Your `CMakeLists.txt` should have something like 82 | 83 | ~~~cmake 84 | set(ROTOR_BUILD_ASIO ON CACHE BOOL "with asio") 85 | add_subdirectory("lib/rotor") 86 | 87 | target_include_directories(my_target PUBLIC 88 | ${PROJECT_SOURCE_DIR}/lib/rotor/include 89 | ) 90 | target_link_libraries(my_lib rotor::asio) 91 | ~~~ 92 | -------------------------------------------------------------------------------- /docs/License.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | `rotor` is distributed under terms and conditions of `MIT` license. For license information 4 | please see LICENSE file. 5 | -------------------------------------------------------------------------------- /docs/References.md: -------------------------------------------------------------------------------- 1 | # References 2 | 3 | ## Other C++ actor frameworks 4 | 5 | [sobjectizer]: https://github.com/Stiffstream/sobjectizer 6 | [caf]: https://actor-framework.org/ 7 | [qpcpp]: https://www.state-machine.com/qpcpp/ 8 | [qb]: https://github.com/isndev/qb 9 | [actor-zeta]: https://github.com/cyberduckninja/actor-zeta 10 | [orca]: https://github.com/wlgq2/orca 11 | [gaylord]: https://github.com/IronsDu/gaylord 12 | [protoactor]: https://github.com/whitglint/protoactor-cpp 13 | [libscoa]: https://github.com/anqurvanillapy/libscoa 14 | 15 | - c++ actor framework, aka [caf][caf] 16 | - [sobjectizer] 17 | - [qpcpp] 18 | - [qb] 19 | - [actor-zeta] 20 | - [orca], seems having documentation in Chinese 21 | - [gaylord] 22 | - [protoactor] 23 | - [libscoa] 24 | 25 | 26 | ## Other materials 27 | 28 | [blog-cpp-supervisors]: https://basiliscos.github.io/blog/2019/08/19/cpp-supervisors/ 29 | [blog-cpp-permissions]: https://basiliscos.github.io/blog/2020/07/23/permission-model/ 30 | [blog-cpp-req_res]: https://basiliscos.github.io/blog/2019/10/05/request-response-message-exchange-pattern/ 31 | [akka]: https://akka.io 32 | [akka-doc]: https://doc.akka.io/docs/akka/current/general/index.html 33 | [yt-my]: https://www.youtube.com/watch?v=kOFSVJp_gMA 34 | [erlang-book]: https://learnyousomeerlang.com/ 35 | 36 | 37 | - [blog-cpp-supervisors] "Trees of Supervisors in C++" (my blog article) 38 | - [blog-cpp-permissions] "Experimental C++ access model" (my blog article) 39 | - [blog-cpp-req_res] "Request-Response pattern" (my blog article) 40 | - [Learn You Some Erlang for great good!][erlang-book] very recommended free ebook about erlang reliability and supervisors 41 | - [documentation][akka-doc] of famous java actor framework [akka][akka]; good for gaining insights 42 | - [overview of actor frameworks][yt-my] (my video in Russian) 43 | - you can view examples in the `examples` source dir as well as in the `tests` dir 44 | 45 | -------------------------------------------------------------------------------- /docs/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | $projectname: $title 10 | $title 11 | 12 | 13 | 14 | $treeview 15 | $search 16 | $mathjax 17 | 18 | $extrastylesheet 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 |
35 |
$projectname 36 |  $projectnumber 37 |
38 |
$projectbrief
39 |
44 |
$projectbrief
45 |
$searchbox
56 |
57 | 58 | 59 | -------------------------------------------------------------------------------- /docs/lifecycle-full.drawio: -------------------------------------------------------------------------------- 1 | 5Vhdd6IwEP01PnYPEkF99Gst1Wq3use6Lz2pRGA3GhujYn99gwwCYi3r1iO6fWlmMpPJzNwbYnKoNnGbHM/se2YSmlMV082hek5V8wVVkf88zdrXFDXdV1jcMcEoVPScNwJK8LMWjknmMUPBGBXOLK4csemUjERMhzlnq7jZmNF41Bm2SELRG2Ga1A4cU9ig1bVCOHFLHMuG0CpCkOAEB9aQytzGJltFVKiRQzXOmPBHE7dGqFe9oDC+3/cPZrc742Qq0jjw/vjhbkhbkxf+6lbvlZd6rXpT9FdZYrqAjGGzYh2UgJiyIiAyLmxmsSmmjVBb5WwxNYkXRpFSaNNmbCaVean8TYRYQ3vxQjCpssWEwuzYobTGKOObiGgs/0olqZ8Lzv6QyAzSURnJQNVk8lCPOVvwETmQcYAizC0iDthBG73sIwGgtE3CJkTwtTTghGLhLON4wQA7a2sXNkYOoDd/0ad8ok+dxiDRqrARXlVXtiNIb4Y35VhJfp6s6EvCBXEPlglmdSADHAf5oubLqwi5AsLYUV4pJyps+b8jgJ6SAKVMEUDf0yedyv1XTWcph5Y3NDpG36i0jV9GpxlMy2gRiwukzPZbmBnO5JOn0bWTppSSNEGvMsKaUqJRW4o06hfIBVQ8IxfQYzv/vGoNn17f1Lr2o8U7d8aNmgkq7AXdkfcjJS3U1bNeiJRE4bsPjcdK3+h2Ku0LhLaGsgZtdHXQVtNCu3BWaCfPlN7tz35fXmqe691B5wLBrZczd4cp7EF34kLp1R1qfiW3yVIha8fMvkZcwmWSuI548gJ+kyX0xSEs5o3rLmxmI6wDYSpL5nupWiBv3Twh9NtIgeMXHoOp77L/+oEH1wfmyC2GHzplB4FlLb6EnwB4RR+0dhYq7iykFnYg6meYWGiD0m0+xwNXu1TghhiMQVD5BIIfAF45CPgvxG3ah4sT4Rbt/g7fhVtq3GqfLHQ0bqUYvif75uGzPGq8Aw== -------------------------------------------------------------------------------- /docs/lifecycle-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basiliscos/cpp-rotor/2edf6807eeb04b43a1d78a137f6db0816af8503e/docs/lifecycle-full.png -------------------------------------------------------------------------------- /docs/lifecycle-simplified.drawio: -------------------------------------------------------------------------------- 1 | 1VZNc5swEP01HNPByGD76o+mjZNOWncm8amjGBnUCpYIYSC/vsIsBkxCk7TNOL7Y+7Srld7bXcsgsyA7lzTyr8BlwrBMNzPI3LCswdAy9VeB5CUysp0S8CR30akGVvyBIYhxXsJdFrccFYBQPGqDGwhDtlEtjEoJadttC6KdNaIe6wCrDRVd9Ia7ykfUsYf1wifGPR9TW4TgBQNaeeNVYp+6kDYgsjDITAKo8leQzZgo2KuIKeM+PrF6OJlkoXpOgPy+vb5Yi2VwJ++z6ZV5N59NzyblLjsqErwxHlblFQXM1YygCVL54EFIxaJGpxKS0GVFGlNbtc8lQKTBgQZ/MqVylJcmCjTkq0Dg6pYLMQMBcp+RbPVnPNZ4rCT8Yo0V4pAJ0Ymm3csjHzEkcsN6bozyFLdqBCJl5wwCpmSuHSQTVPFduw4olpN38DuEXgPXR7FMLH2boOxY+IOx3d5CUekxhVG1bPpH4xg1tBfzBcI6HWF5yFVH3Fq6QofU54qtIronMNUd/d9k2jGpWNYrAK6S0RGRIyQybfRj1WN+sxXNpzVrsd1DLfl2OfiRLte39w/W3P66lF8uPp9ZJ9Ezj5bx61qiGrZlSfY5Wn/ZK8/lvfeYDeIhYlJnLoh7h6XdmRFvWdo9Arcojv2kGGv6ryt8hxQ7k1ObHvZJTI+Xi6E1kPltkfGDXZlrPMDemGctK6+sjKsybGSjuW4s1VGFUQX9w+HmvNVse/wdQAZHBTg8KqzyAp13QGejkf2HjV79oNBm/Qot3evHPFn8Bg== -------------------------------------------------------------------------------- /docs/lifecycle-simplified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basiliscos/cpp-rotor/2edf6807eeb04b43a1d78a137f6db0816af8503e/docs/lifecycle-simplified.png -------------------------------------------------------------------------------- /docs/supervising.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | "system_context" [ shape = box]; 3 | 4 | "system_context" -> "supervisor_root"; 5 | "supervisor_root" -> "supervisor_child" 6 | "supervisor_child" -> "A1"; 7 | "supervisor_child" -> "A2"; 8 | "supervisor_child" -> "A3"; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /docs/supervising.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/basiliscos/cpp-rotor/2edf6807eeb04b43a1d78a137f6db0816af8503e/docs/supervising.png -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | if (BUILD_SHARED_LIBS) 4 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ../bin) 5 | endif () 6 | 7 | if (ROTOR_BUILD_ASIO) 8 | add_subdirectory("boost-asio") 9 | endif() 10 | 11 | if (ROTOR_BUILD_EV) 12 | add_subdirectory("ev") 13 | endif() 14 | 15 | if (ROTOR_BUILD_ASIO AND ROTOR_BUILD_EV AND NOT ROTOR_BUILD_THREAD_UNSAFE) 16 | add_executable(ping-pong-ev_and_asio ping-pong-ev_and_asio.cpp) 17 | target_link_libraries(ping-pong-ev_and_asio rotor_ev rotor_asio) 18 | add_test(ping-pong-ev_and_asio "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping-pong-ev_and_asio") 19 | endif() 20 | 21 | if (ROTOR_BUILD_THREAD) 22 | add_subdirectory("thread") 23 | endif() 24 | 25 | add_executable(autoshutdown autoshutdown.cpp) 26 | target_link_libraries(autoshutdown rotor) 27 | add_test(autoshutdown "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/autoshutdown") 28 | 29 | add_executable(hello_loopless hello_loopless.cpp) 30 | target_link_libraries(hello_loopless rotor) 31 | add_test(hello_loopless "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/hello_loopless") 32 | 33 | add_executable(escalate-failure escalate-failure.cpp) 34 | target_link_libraries(escalate-failure rotor) 35 | add_test(escalate-failure "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/escalate-failure") 36 | 37 | add_executable(ping_pong ping_pong.cpp) 38 | target_link_libraries(ping_pong rotor) 39 | add_test(ping_pong "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping_pong") 40 | 41 | add_executable(ping_pong-lambda ping_pong-lambda.cpp) 42 | target_link_libraries(ping_pong-lambda rotor) 43 | add_test(ping_pong-lambda "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping_pong-lambda") 44 | 45 | add_executable(pub_sub pub_sub.cpp) 46 | target_link_libraries(pub_sub rotor) 47 | add_test(pub_sub "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/pub_sub") 48 | 49 | if (ROTOR_BUILD_FLTK) 50 | find_package(JPEG REQUIRED) 51 | 52 | add_executable(ping-pong-fltk ping-pong-fltk.cpp) 53 | target_link_libraries(ping-pong-fltk 54 | rotor::fltk 55 | $<$:jpeg> 56 | $<$:png> 57 | ) 58 | add_test(ping-pong-fltk "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping-pong-fltk") 59 | 60 | if (ROTOR_BUILD_THREAD) 61 | add_executable(ping-pong-fltk_and_thread ping-pong-fltk_and_thread.cpp) 62 | target_link_libraries(ping-pong-fltk_and_thread 63 | rotor::fltk rotor::thread 64 | $<$:jpeg> 65 | $<$:png> 66 | ) 67 | add_test(ping-pong-fltk_and_thread "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping-pong-fltk_and_thread") 68 | endif() 69 | endif() 70 | -------------------------------------------------------------------------------- /examples/autoshutdown.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | // This example is identical to hello_loopless, except that it demonstrates 8 | // how to autoshutdown supervisor when an actor is down 9 | 10 | #include "rotor.hpp" 11 | #include "dummy_supervisor.h" 12 | #include 13 | 14 | struct hello_actor : public rotor::actor_base_t { 15 | using rotor::actor_base_t::actor_base_t; 16 | void on_start() noexcept override { 17 | rotor::actor_base_t::on_start(); 18 | std::cout << "hello world\n"; 19 | // no need of doing supervisor->do_shutdown() 20 | do_shutdown(); 21 | } 22 | }; 23 | 24 | int main() { 25 | rotor::system_context_t ctx{}; 26 | auto timeout = boost::posix_time::milliseconds{500}; /* does not matter */ 27 | auto sup = ctx.create_supervisor().timeout(timeout).finish(); 28 | sup->create_actor().timeout(timeout).autoshutdown_supervisor().finish(); 29 | sup->do_process(); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /examples/boost-asio/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | find_package(Boost 3 | COMPONENTS 4 | date_time system regex program_options 5 | REQUIRED) 6 | 7 | if (BUILD_SHARED_LIBS) 8 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ../../bin) 9 | endif () 10 | 11 | if (WIN32) 12 | add_compile_definitions( 13 | _SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING 14 | _WIN32_WINNT=0x600 15 | ) 16 | endif() 17 | 18 | add_executable(hello_asio hello_asio.cpp) 19 | target_link_libraries(hello_asio rotor_asio ${Boost_LIBRARIES}) 20 | add_test(hello_asio "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/hello_asio") 21 | 22 | add_executable(request-response request-response.cpp) 23 | target_link_libraries(request-response rotor_asio ${Boost_LIBRARIES}) 24 | add_test(request-response "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/request-response") 25 | 26 | add_executable(request-response-discovery request-response-discovery.cpp) 27 | target_link_libraries(request-response-discovery rotor_asio ${Boost_LIBRARIES}) 28 | add_test(request-response-discovery "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/request-response-discovery") 29 | 30 | add_executable(ping-pong-single-simple ping-pong-single-simple.cpp) 31 | target_link_libraries(ping-pong-single-simple rotor_asio ${Boost_LIBRARIES}) 32 | add_test(ping-pong-single-simple "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping-pong-single-simple") 33 | 34 | add_executable(ping-pong-timer ping-pong-timer.cpp) 35 | target_link_libraries(ping-pong-timer rotor_asio ${Boost_LIBRARIES}) 36 | add_test(ping-pong-timer "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping-pong-timer") 37 | 38 | add_executable(one-shot-lambda-subscriber one-shot-lambda-subscriber.cpp) 39 | target_link_libraries(one-shot-lambda-subscriber rotor_asio ${Boost_LIBRARIES}) 40 | add_test(one-shot-lambda-subscriber "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/one-shot-lambda-subscriber") 41 | 42 | if (NOT ROTOR_BUILD_THREAD_UNSAFE) 43 | add_executable(ping-pong-2-threads ping-pong-2-threads.cpp) 44 | target_link_libraries(ping-pong-2-threads rotor_asio ${Boost_LIBRARIES}) 45 | add_test(ping-pong-2-threads "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping-pong-2-threads") 46 | 47 | add_executable(ping-pong-2-threads-preemt ping-pong-2-threads-preemt.cpp) 48 | target_link_libraries(ping-pong-2-threads-preemt rotor_asio ${Boost_LIBRARIES}) 49 | add_test(ping-pong-2-threads-preemt "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping-pong-2-threads-preemt") 50 | endif() 51 | 52 | add_executable(beast-scrapper beast-scrapper.cpp) 53 | target_link_libraries(beast-scrapper rotor_asio ${Boost_LIBRARIES}) 54 | add_test(beast-scrapper "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/beast-scrapper") 55 | -------------------------------------------------------------------------------- /examples/boost-asio/hello_asio.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/asio.hpp" 8 | 9 | namespace asio = boost::asio; 10 | namespace pt = boost::posix_time; 11 | 12 | struct server_actor : public rotor::actor_base_t { 13 | using rotor::actor_base_t::actor_base_t; 14 | void on_start() noexcept override { 15 | rotor::actor_base_t::on_start(); 16 | std::cout << "hello world\n"; 17 | do_shutdown(); 18 | } 19 | }; 20 | 21 | int main() { 22 | asio::io_context io_context; 23 | auto system_context = rotor::asio::system_context_asio_t::ptr_t{new rotor::asio::system_context_asio_t(io_context)}; 24 | auto strand = std::make_shared(io_context); 25 | auto timeout = boost::posix_time::milliseconds{500}; 26 | auto sup = 27 | system_context->create_supervisor().strand(strand).timeout(timeout).finish(); 28 | 29 | sup->create_actor().timeout(timeout).autoshutdown_supervisor().finish(); 30 | 31 | sup->start(); 32 | io_context.run(); 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /examples/dummy_supervisor.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #pragma once 8 | #include 9 | #include 10 | 11 | namespace { 12 | namespace to { 13 | struct on_timer_trigger {}; 14 | } // namespace to 15 | } // namespace 16 | 17 | namespace rotor { 18 | template <> 19 | inline auto rotor::actor_base_t::access(request_id_t request_id, 20 | bool cancelled) noexcept { 21 | on_timer_trigger(request_id, cancelled); 22 | } 23 | } // namespace rotor 24 | 25 | struct dummy_supervisor_t : public rotor::supervisor_t { 26 | using rotor::supervisor_t::supervisor_t; 27 | using timers_map_t = std::unordered_map; 28 | 29 | timers_map_t timers_map; 30 | 31 | void do_start_timer(const rotor::pt::time_duration &, rotor::timer_handler_base_t &handler) noexcept override { 32 | timers_map.emplace(handler.request_id, &handler); 33 | } 34 | 35 | void do_cancel_timer(rotor::request_id_t timer_id) noexcept override { 36 | auto it = timers_map.find(timer_id); 37 | if (it != timers_map.end()) { 38 | auto &actor_ptr = it->second->owner; 39 | actor_ptr->access(timer_id, true); 40 | timers_map.erase(it); 41 | } 42 | } 43 | 44 | void start() noexcept override {} 45 | void shutdown() noexcept override {} 46 | void enqueue(rotor::message_ptr_t) noexcept override {} 47 | }; 48 | -------------------------------------------------------------------------------- /examples/ev/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | if (BUILD_SHARED_LIBS) 4 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ../../bin) 5 | endif () 6 | 7 | if (WIN32) 8 | add_compile_definitions( 9 | _SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING 10 | _WIN32_WINNT=0x600 11 | ) 12 | endif() 13 | 14 | find_package(Threads) 15 | 16 | add_executable(ping-pong-ev ping-pong-ev.cpp) 17 | target_link_libraries(ping-pong-ev rotor_ev) 18 | add_test(ping-pong-ev "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping-pong-ev") 19 | 20 | if (Threads_FOUND AND NOT ROTOR_BUILD_THREAD_UNSAFE) 21 | add_executable(ping-pong-ev-2-threads ping-pong-ev-2-threads.cpp) 22 | target_link_libraries(ping-pong-ev-2-threads rotor_ev Threads::Threads) 23 | add_test(ping-pong-ev-2-threads "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping-pong-ev-2-threads") 24 | endif() 25 | 26 | add_executable(ping-pong-req ping-pong-req.cpp) 27 | target_link_libraries(ping-pong-req rotor_ev) 28 | add_test(ping-pong-req "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping-pong-req") 29 | 30 | add_executable(ping-pong-comp ping-pong-comp.cpp) 31 | target_link_libraries(ping-pong-comp rotor_ev) 32 | add_test(ping-pong-comp "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping-pong-comp") 33 | 34 | add_executable(pong-registry pong-registry.cpp) 35 | target_link_libraries(pong-registry rotor_ev) 36 | add_test(pong-registry "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/pong-registry") 37 | -------------------------------------------------------------------------------- /examples/ev/ping-pong-req.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include 8 | #include 9 | #include 10 | #include // for calling WSAStartup on Windows 11 | 12 | namespace payload { 13 | struct pong_t {}; 14 | struct ping_t { 15 | using response_t = pong_t; 16 | }; 17 | } // namespace payload 18 | 19 | namespace message { 20 | using ping_t = rotor::request_traits_t::request::message_t; 21 | using pong_t = rotor::request_traits_t::response::message_t; 22 | } // namespace message 23 | 24 | struct pinger_t : public rotor::actor_base_t { 25 | 26 | using rotor::actor_base_t::actor_base_t; 27 | 28 | void set_ponger_addr(const rotor::address_ptr_t &addr) { ponger_addr = addr; } 29 | 30 | void configure(rotor::plugin::plugin_base_t &plugin) noexcept override { 31 | rotor::actor_base_t::configure(plugin); 32 | plugin.with_casted([](auto &p) { p.subscribe_actor(&pinger_t::on_pong); }); 33 | } 34 | 35 | void on_start() noexcept override { 36 | rotor::actor_base_t::on_start(); 37 | request(ponger_addr).send(rotor::pt::seconds(1)); 38 | } 39 | 40 | void on_pong(message::pong_t &msg) noexcept { 41 | auto &ec = msg.payload.ee; 42 | if (!ec) { 43 | std::cout << "pong received\n"; 44 | } else { 45 | std::cout << "pong was NOT received: " << ec->message() << "\n"; 46 | } 47 | do_shutdown(); 48 | } 49 | 50 | rotor::address_ptr_t ponger_addr; 51 | }; 52 | 53 | struct ponger_t : public rotor::actor_base_t { 54 | using generator_t = std::mt19937; 55 | using distribution_t = std::uniform_real_distribution; 56 | 57 | std::random_device rd; 58 | generator_t gen; 59 | distribution_t dist; 60 | 61 | explicit ponger_t(config_t &cfg) : rotor::actor_base_t(cfg), gen(rd()) {} 62 | 63 | void configure(rotor::plugin::plugin_base_t &plugin) noexcept override { 64 | rotor::actor_base_t::configure(plugin); 65 | plugin.with_casted([](auto &p) { p.subscribe_actor(&ponger_t::on_ping); }); 66 | } 67 | 68 | void on_ping(message::ping_t &req) noexcept { 69 | auto dice = dist(gen); 70 | std::cout << "pong, dice = " << dice << std::endl; 71 | if (dice > 0.5) { 72 | reply_to(req); 73 | } 74 | } 75 | }; 76 | 77 | int main() { 78 | try { 79 | auto *loop = ev_loop_new(0); 80 | auto system_context = rotor::ev::system_context_ptr_t{new rotor::ev::system_context_ev_t()}; 81 | auto timeout = boost::posix_time::milliseconds{10}; 82 | auto sup = system_context->create_supervisor() 83 | .loop(loop) 84 | .loop_ownership(true) /* let supervisor takes ownership on the loop */ 85 | .timeout(timeout) 86 | .finish(); 87 | 88 | auto pinger = sup->create_actor().timeout(timeout).autoshutdown_supervisor().finish(); 89 | auto ponger = sup->create_actor().timeout(timeout).finish(); 90 | pinger->set_ponger_addr(ponger->get_address()); 91 | 92 | sup->start(); 93 | ev_run(loop); 94 | } catch (const std::exception &ex) { 95 | std::cout << "exception : " << ex.what(); 96 | } 97 | 98 | std::cout << "exiting...\n"; 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /examples/hello_loopless.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include "dummy_supervisor.h" 9 | #include 10 | 11 | struct hello_actor : public rotor::actor_base_t { 12 | using rotor::actor_base_t::actor_base_t; 13 | void on_start() noexcept override { 14 | rotor::actor_base_t::on_start(); 15 | std::cout << "hello world\n"; 16 | supervisor->do_shutdown(); 17 | } 18 | }; 19 | 20 | int main() { 21 | rotor::system_context_t ctx{}; 22 | auto timeout = boost::posix_time::milliseconds{500}; /* does not matter */ 23 | auto sup = ctx.create_supervisor().timeout(timeout).finish(); 24 | sup->create_actor().timeout(timeout).finish(); 25 | sup->do_process(); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /examples/ping-pong-fltk.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifndef _WIN32 8 | #include 9 | #include 10 | #endif 11 | 12 | namespace r = rotor; 13 | namespace rf = r::fltk; 14 | 15 | namespace payload { 16 | 17 | struct ping_t {}; 18 | struct pong_t {}; 19 | 20 | } // namespace payload 21 | 22 | namespace message { 23 | 24 | using ping_t = r::message_t; 25 | using pong_t = r::message_t; 26 | 27 | } // namespace message 28 | 29 | struct pinger_t : public r::actor_base_t { 30 | using rotor::actor_base_t::actor_base_t; 31 | 32 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 33 | r::actor_base_t::configure(plugin); 34 | plugin.with_casted([](auto &p) { p.subscribe_actor(&pinger_t::on_pong); }); 35 | } 36 | 37 | void on_start() noexcept override { 38 | r::actor_base_t::on_start(); 39 | auto timeout = r::pt::seconds{2}; 40 | start_timer(timeout, *this, &pinger_t::on_ping_timer); 41 | } 42 | 43 | void on_ping_timer(r::request_id_t, bool) noexcept { 44 | box->label("ping has been sent"); 45 | send(ponger_addr); 46 | } 47 | 48 | void on_pong(message::pong_t &) noexcept { 49 | box->label("pong has been received, going to shutdown"); 50 | auto timeout = r::pt::seconds{1}; 51 | start_timer(timeout, *this, &pinger_t::on_shutdown_timer); 52 | } 53 | 54 | void on_shutdown_timer(r::request_id_t, bool) noexcept { supervisor->shutdown(); } 55 | 56 | rotor::address_ptr_t ponger_addr; 57 | Fl_Box *box; 58 | }; 59 | 60 | struct ponger_t : r::actor_base_t { 61 | using rotor::actor_base_t::actor_base_t; 62 | 63 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 64 | r::actor_base_t::configure(plugin); 65 | plugin.with_casted([](auto &p) { p.subscribe_actor(&ponger_t::on_ping); }); 66 | } 67 | 68 | void on_ping(message::ping_t &) noexcept { 69 | auto timeout = r::pt::seconds{2}; 70 | start_timer(timeout, *this, &ponger_t::on_timer); 71 | } 72 | 73 | void on_timer(r::request_id_t, bool) noexcept { send(pinger_addr); } 74 | 75 | rotor::address_ptr_t pinger_addr; 76 | }; 77 | 78 | bool is_display_available() { 79 | #ifndef _WIN32 80 | char *disp = getenv("DISPLAY"); 81 | if (disp == nullptr) 82 | return false; 83 | Display *dpy = XOpenDisplay(disp); 84 | if (dpy == nullptr) 85 | return false; 86 | XCloseDisplay(dpy); 87 | #endif 88 | return true; 89 | } 90 | 91 | int main(int argc, char **argv) { 92 | if (!is_display_available()) { 93 | return 0; 94 | } 95 | 96 | // let fltk-core be aware of lockin mechanism (i.e. Fl::awake will work) 97 | Fl::lock(); 98 | 99 | Fl_Window window = Fl_Window(400, 180); 100 | Fl_Box *box = new Fl_Box(20, 40, 360, 100, "auto shutdown in 5 seconds"); 101 | box->box(FL_UP_BOX); 102 | window.end(); 103 | window.show(argc, argv); 104 | 105 | auto system_context = rf::system_context_fltk_t(); 106 | auto timeout = r::pt::millisec{250}; 107 | auto supervisor = 108 | system_context.create_supervisor().timeout(timeout).create_registry().finish(); 109 | auto pinger = supervisor->create_actor().timeout(timeout).finish(); 110 | auto ponger = supervisor->create_actor().timeout(timeout).finish(); 111 | 112 | pinger->box = box; 113 | pinger->ponger_addr = ponger->get_address(); 114 | ponger->pinger_addr = pinger->get_address(); 115 | 116 | supervisor->start(); 117 | 118 | while (!supervisor->get_shutdown_reason()) { 119 | supervisor->do_process(); 120 | Fl::wait(0.1); 121 | } 122 | 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /examples/ping_pong-lambda.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include "dummy_supervisor.h" 9 | #include 10 | 11 | namespace payload { 12 | 13 | struct ping_t {}; 14 | struct pong_t {}; 15 | 16 | } // namespace payload 17 | 18 | namespace message { 19 | using ping_t = rotor::message_t; 20 | using pong_t = rotor::message_t; 21 | } // namespace message 22 | 23 | struct lambda_pinger_t : public rotor::actor_base_t { 24 | using rotor::actor_base_t::actor_base_t; 25 | 26 | void set_ponger_addr(const rotor::address_ptr_t &addr) { ponger_addr = addr; } 27 | 28 | void configure(rotor::plugin::plugin_base_t &plugin) noexcept override { 29 | rotor::actor_base_t::configure(plugin); 30 | plugin.with_casted([&](auto &p) { 31 | auto handler = rotor::lambda([&](auto &) noexcept { 32 | std::cout << "pong\n"; 33 | do_shutdown(); 34 | }); 35 | p.subscribe_actor(std::move(handler)); 36 | }); 37 | } 38 | 39 | void on_start() noexcept override { 40 | rotor::actor_base_t::on_start(); 41 | send(ponger_addr); 42 | } 43 | 44 | rotor::address_ptr_t ponger_addr; 45 | }; 46 | 47 | struct ponger_t : public rotor::actor_base_t { 48 | using rotor::actor_base_t::actor_base_t; 49 | void set_pinger_addr(const rotor::address_ptr_t &addr) { pinger_addr = addr; } 50 | 51 | void configure(rotor::plugin::plugin_base_t &plugin) noexcept override { 52 | rotor::actor_base_t::configure(plugin); 53 | plugin.with_casted([](auto &p) { p.subscribe_actor(&ponger_t::on_ping); }); 54 | } 55 | 56 | void on_ping(message::ping_t &) noexcept { 57 | std::cout << "ping\n"; 58 | send(pinger_addr); 59 | } 60 | 61 | private: 62 | rotor::address_ptr_t pinger_addr; 63 | }; 64 | 65 | int main() { 66 | rotor::system_context_t ctx{}; 67 | auto timeout = boost::posix_time::milliseconds{500}; /* does not matter */ 68 | auto sup = ctx.create_supervisor().timeout(timeout).finish(); 69 | 70 | auto pinger = sup->create_actor().timeout(timeout).autoshutdown_supervisor().finish(); 71 | auto ponger = sup->create_actor().timeout(timeout).finish(); 72 | pinger->set_ponger_addr(ponger->get_address()); 73 | ponger->set_pinger_addr(pinger->get_address()); 74 | 75 | sup->do_process(); 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /examples/ping_pong.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include "dummy_supervisor.h" 9 | #include 10 | 11 | struct ping_t {}; 12 | struct pong_t {}; 13 | 14 | struct pinger_t : public rotor::actor_base_t { 15 | using rotor::actor_base_t::actor_base_t; 16 | 17 | void set_ponger_addr(const rotor::address_ptr_t &addr) { ponger_addr = addr; } 18 | 19 | void configure(rotor::plugin::plugin_base_t &plugin) noexcept override { 20 | rotor::actor_base_t::configure(plugin); 21 | plugin.with_casted([](auto &p) { p.subscribe_actor(&pinger_t::on_pong); }); 22 | } 23 | 24 | void on_start() noexcept override { 25 | rotor::actor_base_t::on_start(); 26 | send(ponger_addr); 27 | } 28 | 29 | void on_pong(rotor::message_t &) noexcept { 30 | std::cout << "pong\n"; 31 | do_shutdown(); 32 | } 33 | 34 | rotor::address_ptr_t ponger_addr; 35 | }; 36 | 37 | struct ponger_t : public rotor::actor_base_t { 38 | using rotor::actor_base_t::actor_base_t; 39 | void set_pinger_addr(const rotor::address_ptr_t &addr) { pinger_addr = addr; } 40 | 41 | void configure(rotor::plugin::plugin_base_t &plugin) noexcept override { 42 | rotor::actor_base_t::configure(plugin); 43 | plugin.with_casted([](auto &p) { p.subscribe_actor(&ponger_t::on_ping); }); 44 | } 45 | 46 | void on_ping(rotor::message_t &) noexcept { 47 | std::cout << "ping\n"; 48 | send(pinger_addr); 49 | } 50 | 51 | private: 52 | rotor::address_ptr_t pinger_addr; 53 | }; 54 | 55 | int main() { 56 | rotor::system_context_t ctx{}; 57 | auto timeout = boost::posix_time::milliseconds{500}; /* does not matter */ 58 | auto sup = ctx.create_supervisor().timeout(timeout).finish(); 59 | 60 | auto pinger = sup->create_actor() 61 | .init_timeout(timeout) 62 | .shutdown_timeout(timeout) 63 | .autoshutdown_supervisor() 64 | .finish(); 65 | auto ponger = sup->create_actor() 66 | .timeout(timeout) // shortcut for init/shutdown 67 | .finish(); 68 | pinger->set_ponger_addr(ponger->get_address()); 69 | ponger->set_pinger_addr(pinger->get_address()); 70 | 71 | sup->do_process(); 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /examples/pub_sub.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include "dummy_supervisor.h" 9 | #include 10 | 11 | namespace r = rotor; 12 | 13 | struct payload_t {}; 14 | using sample_message_t = r::message_t; 15 | 16 | struct pub_t : public r::actor_base_t { 17 | 18 | using r::actor_base_t::actor_base_t; 19 | 20 | void set_pub_addr(const r::address_ptr_t &addr) { pub_addr = addr; } 21 | 22 | void on_start() noexcept override { 23 | r::actor_base_t::on_start(); 24 | send(pub_addr); 25 | } 26 | 27 | r::address_ptr_t pub_addr; 28 | }; 29 | 30 | struct sub_t : public r::actor_base_t { 31 | using r::actor_base_t::actor_base_t; 32 | 33 | void set_pub_addr(const r::address_ptr_t &addr) { pub_addr = addr; } 34 | 35 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 36 | rotor::actor_base_t::configure(plugin); 37 | plugin.with_casted( 38 | [&](auto &p) { p.subscribe_actor(&sub_t::on_payload, pub_addr); }); 39 | } 40 | 41 | void on_payload(sample_message_t &) noexcept { std::cout << "received on " << static_cast(this) << "\n"; } 42 | 43 | r::address_ptr_t pub_addr; 44 | }; 45 | 46 | int main() { 47 | rotor::system_context_t ctx{}; 48 | auto timeout = boost::posix_time::milliseconds{500}; /* does not matter */ 49 | auto sup = ctx.create_supervisor().timeout(timeout).finish(); 50 | 51 | auto pub_addr = sup->create_address(); // (1) 52 | auto pub = sup->create_actor().timeout(timeout).finish(); 53 | auto sub1 = sup->create_actor().timeout(timeout).finish(); 54 | auto sub2 = sup->create_actor().timeout(timeout).finish(); 55 | 56 | pub->set_pub_addr(pub_addr); 57 | sub1->set_pub_addr(pub_addr); 58 | sub2->set_pub_addr(pub_addr); 59 | 60 | sup->do_process(); 61 | 62 | sup->do_shutdown(); 63 | sup->do_process(); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /examples/thread/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | find_package(OpenSSL 1.1.1...3.2.0 COMPONENTS Crypto) 3 | 4 | if (BUILD_SHARED_LIBS) 5 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ../../bin) 6 | endif () 7 | 8 | if (OPENSSL_FOUND) 9 | add_executable(sha512 sha512.cpp) 10 | target_link_libraries(sha512 rotor::thread OpenSSL::Crypto) 11 | add_test(sha512 "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/sha512") 12 | endif() 13 | 14 | add_executable(ping-pong-spawner ping-pong-spawner.cpp) 15 | target_link_libraries(ping-pong-spawner rotor::thread) 16 | add_test(ping-pong-spawner "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping-pong-spawner") 17 | 18 | if (NOT ROTOR_BUILD_THREAD_UNSAFE) 19 | add_executable(ping-pong-thread ping-pong-thread.cpp) 20 | target_link_libraries(ping-pong-thread rotor::thread) 21 | add_test(ping-pong-thread "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ping-pong-thread") 22 | endif() 23 | -------------------------------------------------------------------------------- /include/rotor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | /** \file rotor.hpp 10 | * A convenience header to include rotor core. 11 | */ 12 | 13 | #include "rotor/actor_base.h" 14 | #include "rotor/address.hpp" 15 | #include "rotor/message.h" 16 | #include "rotor/registry.h" 17 | #include "rotor/supervisor.h" 18 | #include "rotor/system_context.h" 19 | 20 | /// Basic namespace for all rotor functionalities 21 | namespace rotor {} 22 | -------------------------------------------------------------------------------- /include/rotor/address.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "arc.hpp" 10 | #include "forward.hpp" 11 | 12 | namespace rotor { 13 | 14 | /** \struct address_t 15 | * \brief Message subscription and delivery point 16 | * 17 | * Address is an abstraction of "point of service", i.e. any actor can send 18 | * an message to an address, and any actor can subscribe on any address to 19 | * handle certain kind of messages. 20 | * 21 | * An actor has "main" address, and in addition it may have "virtual" private 22 | * addresses to perform routing messages for the specific methods. 23 | * 24 | * Addresses are produced by {@link supervisor_t}, which also is responsible 25 | * for initial delivery of messages on the address. Address lifetime *should* 26 | * be no longer then corresponding supervisor lifetime. 27 | * 28 | * Addresses are non-copyable and non-moveable. The constructor is private 29 | * and it is intended to be created by supervisor only. 30 | * 31 | */ 32 | 33 | struct address_t : public arc_base_t { 34 | /// reference to {@link supervisor_t}, which generated the address 35 | supervisor_t &supervisor; 36 | 37 | /** \brief runtime label, describing some execution group */ 38 | const void *locality; 39 | 40 | address_t(const address_t &) = delete; 41 | address_t(address_t &&) = delete; 42 | 43 | /** \brief returns true if two addresses are the same, i.e. are located in the 44 | * same memory region 45 | */ 46 | inline bool operator==(const address_t &other) const noexcept { return this == &other; } 47 | 48 | /** \brief compares locality fields of the addresses */ 49 | inline bool same_locality(const address_t &other) const noexcept { return this->locality == other.locality; } 50 | 51 | private: 52 | friend struct supervisor_t; 53 | address_t(supervisor_t &sup, const void *locality_) : supervisor{sup}, locality{locality_} {} 54 | }; 55 | 56 | /** \brief intrusive pointer for address */ 57 | using address_ptr_t = intrusive_ptr_t; 58 | 59 | } // namespace rotor 60 | 61 | namespace std { 62 | /** \struct hash 63 | * \brief Hash calculator for address 64 | */ 65 | template <> struct hash { 66 | /** \brief Calculates hash for the address */ 67 | inline size_t operator()(const rotor::address_ptr_t &address) const noexcept { 68 | return reinterpret_cast(address.get()); 69 | } 70 | }; 71 | 72 | } // namespace std 73 | -------------------------------------------------------------------------------- /include/rotor/address_mapping.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "arc.hpp" 10 | #include "subscription.h" 11 | #include 12 | #include 13 | 14 | #if defined(_MSC_VER) 15 | #pragma warning(push) 16 | #pragma warning(disable : 4251) 17 | #endif 18 | 19 | namespace rotor { 20 | 21 | /** \struct address_mapping_t 22 | * \brief NAT mechanism for `rotor` 23 | * 24 | * It plays the similar role as https://en.wikipedia.org/wiki/Network_address_translation 25 | * in routers, i.e. the original message (request) is dispatched from different address 26 | * then source actor. It's needed for supervising it. 27 | * 28 | * The `address_mapping_t` does not hold the original reply address. That kind of 29 | * mapping is done on `per-request` basis. The `address_mapping_t` performs only 30 | * `per-message-type` mapping. 31 | * 32 | */ 33 | struct ROTOR_API address_mapping_t { 34 | /** \brief associates temporal destination point with actor's message type 35 | * 36 | * An actor is able to process message type identified by `message`. So, 37 | * the temporal subscription point (handler and temporal address) will 38 | * be associated with the actor/message type pair. 39 | * 40 | * In the routing the temporal destination address is usually some 41 | * supervisor's address. 42 | * 43 | */ 44 | void set(actor_base_t &actor, const subscription_info_ptr_t &info) noexcept; 45 | 46 | /** \brief returns temporal destination address for the actor/message type */ 47 | address_ptr_t get_mapped_address(actor_base_t &actor, const void *) noexcept; 48 | 49 | /** \brief iterates on all subscriptions for an actor */ 50 | template void each_subscription(const actor_base_t &actor, Fn &&fn) const noexcept { 51 | auto it_mappings = actor_map.find(static_cast(&actor)); 52 | for (auto it : it_mappings->second) { 53 | fn(it.second); 54 | } 55 | } 56 | 57 | /** \brief checks whether an actor has any subscriptions */ 58 | bool has_subscriptions(const actor_base_t &actor) const noexcept; 59 | 60 | /** \brief returns true if there is no any subscription for any actor */ 61 | bool empty() const noexcept { return actor_map.empty(); } 62 | 63 | /** \brief forgets subscription point */ 64 | void remove(const subscription_point_t &point) noexcept; 65 | 66 | private: 67 | using point_map_t = std::unordered_map; 68 | using actor_map_t = std::unordered_map; 69 | actor_map_t actor_map; 70 | }; 71 | 72 | } // namespace rotor 73 | 74 | #if defined(_MSC_VER) 75 | #pragma warning(pop) 76 | #endif 77 | -------------------------------------------------------------------------------- /include/rotor/arc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include 10 | #include 11 | #include "rotor/export.h" 12 | 13 | namespace rotor { 14 | 15 | #ifdef ROTOR_REFCOUNT_THREADUNSAFE 16 | /** \brief thread-unsafe intrusive pointer policy */ 17 | using counter_policy_t = boost::thread_unsafe_counter; 18 | #else 19 | /** \brief thread-safe intrusive pointer policy */ 20 | using counter_policy_t = boost::thread_safe_counter; 21 | #endif 22 | 23 | /** \brief base class to inject ref-counter with the specified policy */ 24 | template using arc_base_t = boost::intrusive_ref_counter; 25 | 26 | /** \brief alias for intrusive pointer */ 27 | template using intrusive_ptr_t = boost::intrusive_ptr; 28 | 29 | } // namespace rotor 30 | -------------------------------------------------------------------------------- /include/rotor/asio.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | /** \file asio.hpp 10 | * A convenience header to include rotor support for boost::asio 11 | */ 12 | 13 | #include "rotor/asio/export.h" 14 | #include "rotor/asio/forwarder.hpp" 15 | #include "rotor/asio/supervisor_asio.h" 16 | #include "rotor/asio/supervisor_config_asio.h" 17 | #include "rotor/asio/system_context_asio.h" 18 | 19 | namespace rotor { 20 | 21 | /// namespace for boost::asio adapters for `rotor` 22 | namespace asio {} 23 | 24 | } // namespace rotor 25 | -------------------------------------------------------------------------------- /include/rotor/asio/supervisor_config_asio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/supervisor_config.h" 10 | #include 11 | #include 12 | 13 | #if defined(_MSC_VER) 14 | #pragma warning(push) 15 | #pragma warning(disable : 4251) 16 | #endif 17 | 18 | namespace rotor { 19 | namespace asio { 20 | 21 | /** \struct supervisor_config_asio_t 22 | * \brief boost::asio supervisor config, which holds pointer to strand */ 23 | struct supervisor_config_asio_t : public supervisor_config_t { 24 | /** \brief alias for boost::asio strand type */ 25 | using strand_t = boost::asio::io_context::strand; 26 | 27 | /** \brief type for strand shared pointer */ 28 | using strand_ptr_t = std::shared_ptr; 29 | 30 | /** \brief boost::asio execution strand (shared pointer) */ 31 | strand_ptr_t strand; 32 | 33 | /** \brief should supervisor take ownership on the io_context */ 34 | bool guard_context = false; 35 | 36 | using supervisor_config_t::supervisor_config_t; 37 | }; 38 | 39 | /** \brief CRTP supervisor asio config builder */ 40 | template struct supervisor_config_asio_builder_t : supervisor_config_builder_t { 41 | /** \brief final builder class */ 42 | using builder_t = typename Supervisor::template config_builder_t; 43 | 44 | /** \brief parent config builder */ 45 | using parent_t = supervisor_config_builder_t; 46 | using parent_t::parent_t; 47 | 48 | /** \brief alias for strand smart pointer */ 49 | using strand_ptr_t = supervisor_config_asio_t::strand_ptr_t; 50 | 51 | /** \brief bit mask for strand validation */ 52 | constexpr static const std::uint32_t STRAND = 1 << 2; 53 | 54 | /** \brief bit mask for all required fields */ 55 | constexpr static const std::uint32_t requirements_mask = parent_t::requirements_mask | STRAND; 56 | 57 | /** \brief strand setter */ 58 | builder_t &&strand(strand_ptr_t &strand) && { 59 | parent_t::config.strand = strand; 60 | parent_t::mask = (parent_t::mask & ~STRAND); 61 | return std::move(*static_cast(this)); 62 | } 63 | 64 | /** \brief instructs to take ownership of the io_context */ 65 | builder_t &&guard_context(bool value) && { 66 | parent_t::config.guard_context = value; 67 | return std::move(*static_cast(this)); 68 | } 69 | }; 70 | 71 | } // namespace asio 72 | } // namespace rotor 73 | 74 | #if defined(_MSC_VER) 75 | #pragma warning(pop) 76 | #endif 77 | -------------------------------------------------------------------------------- /include/rotor/asio/system_context_asio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/arc.hpp" 10 | #include "rotor/system_context.h" 11 | #include 12 | 13 | #if defined(_MSC_VER) 14 | #pragma warning(push) 15 | #pragma warning(disable : 4251) 16 | #endif 17 | 18 | namespace rotor { 19 | namespace asio { 20 | 21 | namespace asio = boost::asio; 22 | 23 | struct supervisor_asio_t; 24 | 25 | /** \brief intrusive pointer for boost::asio supervisor */ 26 | using supervisor_ptr_t = intrusive_ptr_t; 27 | 28 | /** \struct system_context_asio_t 29 | * \brief The boost::asio system context, which holds a reference to 30 | * `boost::asio::io_context` and root supervisor 31 | */ 32 | struct system_context_asio_t : public system_context_t { 33 | /** \brief intrusive pointer type for boost::asio system context */ 34 | using ptr_t = rotor::intrusive_ptr_t; 35 | 36 | /** \brief construct the context from `boost::asio::io_context` reference */ 37 | system_context_asio_t(asio::io_context &io_context_) : io_context{io_context_} {} 38 | 39 | /** \brief returns a reference to `boost::asio::io_context` */ 40 | inline asio::io_context &get_io_context() noexcept { return io_context; } 41 | 42 | protected: 43 | friend struct supervisor_asio_t; 44 | 45 | /** \brief a reference to `boost::asio::io_context` */ 46 | asio::io_context &io_context; 47 | }; 48 | 49 | /** \brief intrusive pointer type for boost::asio system context */ 50 | using system_context_ptr_t = typename system_context_asio_t::ptr_t; 51 | 52 | } // namespace asio 53 | } // namespace rotor 54 | 55 | #if defined(_MSC_VER) 56 | #pragma warning(pop) 57 | #endif 58 | -------------------------------------------------------------------------------- /include/rotor/error_code.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include 10 | #include 11 | #include "rotor/export.h" 12 | 13 | #if defined(_MSC_VER) 14 | #pragma warning(push) 15 | #pragma warning(disable : 4251) 16 | #pragma warning(disable : 4275) 17 | #endif 18 | 19 | namespace rotor { 20 | 21 | /** \brief fatal error codes in rotor */ 22 | enum class error_code_t { 23 | success = 0, 24 | cancelled, 25 | request_timeout, 26 | supervisor_defined, 27 | already_registered, 28 | actor_misconfigured, 29 | actor_not_linkable, 30 | already_linked, 31 | failure_escalation, 32 | registration_failed, 33 | discovery_failed, 34 | unknown_service, 35 | }; 36 | 37 | /** \brief actor shutdown reasons as error code */ 38 | enum class shutdown_code_t { 39 | normal = 0, 40 | supervisor_shutdown, 41 | child_down, 42 | child_init_failed, 43 | init_failed, 44 | unlink_requested, 45 | }; 46 | 47 | namespace details { 48 | 49 | /** \brief category support for `rotor` error codes */ 50 | class ROTOR_API error_code_category : public std::error_category { 51 | public: 52 | /** error category name */ 53 | virtual const char *name() const noexcept override; 54 | 55 | /** message for error code */ 56 | virtual std::string message(int c) const override; 57 | }; 58 | 59 | /** \brief category support for `rotor` shutdown codes */ 60 | class ROTOR_API shutdown_code_category : public std::error_category { 61 | public: 62 | /** error category name */ 63 | virtual const char *name() const noexcept override; 64 | 65 | /** message for error code */ 66 | virtual std::string message(int c) const override; 67 | }; 68 | 69 | } // namespace details 70 | 71 | /** \brief returns error code category for `rotor` error codes */ 72 | ROTOR_API const details::error_code_category &error_code_category(); 73 | 74 | /** \brief returns error code category for `rotor` shutdown codes */ 75 | ROTOR_API const details::shutdown_code_category &shutdown_code_category(); 76 | 77 | /** \brief makes `std::error_code` from rotor error code enumerations */ 78 | ROTOR_API inline std::error_code make_error_code(const error_code_t e) { 79 | return {static_cast(e), error_code_category()}; 80 | } 81 | 82 | /** \brief makes `std::error_code` from rotor shutdown code enumerations */ 83 | ROTOR_API inline std::error_code make_error_code(const shutdown_code_t e) { 84 | return {static_cast(e), shutdown_code_category()}; 85 | } 86 | 87 | } // namespace rotor 88 | 89 | namespace std { 90 | template <> struct is_error_code_enum : std::true_type {}; 91 | template <> struct is_error_code_enum : std::true_type {}; 92 | } // namespace std 93 | 94 | #if defined(_MSC_VER) 95 | #pragma warning(pop) 96 | #endif 97 | -------------------------------------------------------------------------------- /include/rotor/ev.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | /** \file wx.hpp 10 | * A convenience header to include rotor support for libev 11 | */ 12 | 13 | #include "rotor/ev/supervisor_config_ev.h" 14 | #include "rotor/ev/supervisor_ev.h" 15 | #include "rotor/ev/system_context_ev.h" 16 | 17 | namespace rotor { 18 | 19 | /// namespace for ev adapters for `rotor` 20 | namespace ev {} 21 | 22 | } // namespace rotor 23 | -------------------------------------------------------------------------------- /include/rotor/ev/supervisor_config_ev.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/supervisor_config.h" 10 | #include 11 | 12 | namespace rotor { 13 | namespace ev { 14 | 15 | /** \struct supervisor_config_ev_t 16 | * \brief libev supervisor config, which holds a pointer to the ev 17 | * event loop and a loop ownership flag 18 | */ 19 | struct supervisor_config_ev_t : public supervisor_config_t { 20 | /** \brief a pointer to EV event loop */ 21 | struct ev_loop *loop; 22 | 23 | /** \brief whether loop should be destroyed by supervisor */ 24 | bool loop_ownership = false; 25 | 26 | using supervisor_config_t::supervisor_config_t; 27 | }; 28 | 29 | /** \brief CRTP supervisor ev config builder */ 30 | template struct supervisor_config_ev_builder_t : supervisor_config_builder_t { 31 | /** \brief final builder class */ 32 | using builder_t = typename Supervisor::template config_builder_t; 33 | 34 | /** \brief parent config builder */ 35 | using parent_t = supervisor_config_builder_t; 36 | using parent_t::parent_t; 37 | 38 | /** \brief bit mask for loop validation */ 39 | constexpr static const std::uint32_t LOOP = 1 << 2; 40 | 41 | /** \brief bit mask for loop ownership validation */ 42 | constexpr static const std::uint32_t LOOP_OWNERSHIP = 1 << 3; 43 | 44 | /** \brief bit mask for all required fields */ 45 | constexpr static const std::uint32_t requirements_mask = parent_t::requirements_mask | LOOP | LOOP_OWNERSHIP; 46 | 47 | /** \brief sets loop */ 48 | builder_t &&loop(struct ev_loop *loop) && { 49 | parent_t::config.loop = loop; 50 | parent_t::mask = (parent_t::mask & ~LOOP); 51 | return std::move(*static_cast(this)); 52 | } 53 | 54 | /** \brief instructs to take ownership on the loop (aka be destroyed by supervisor) */ 55 | builder_t &&loop_ownership(bool value) && { 56 | parent_t::config.loop_ownership = value; 57 | parent_t::mask = (parent_t::mask & ~LOOP_OWNERSHIP); 58 | return std::move(*static_cast(this)); 59 | } 60 | }; 61 | 62 | } // namespace ev 63 | } // namespace rotor 64 | -------------------------------------------------------------------------------- /include/rotor/ev/system_context_ev.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/arc.hpp" 10 | #include "rotor/ev/supervisor_config_ev.h" 11 | #include "rotor/system_context.h" 12 | #include 13 | 14 | namespace rotor { 15 | namespace ev { 16 | 17 | struct supervisor_ev_t; 18 | 19 | /** \brief intrusive pointer for ev supervisor */ 20 | using supervisor_ptr_t = intrusive_ptr_t; 21 | 22 | /** \brief alias for system main system context */ 23 | using system_context_ev_t = rotor::system_context_t; 24 | 25 | /** \brief intrusive pointer type for ev system context */ 26 | using system_context_ptr_t = typename rotor::intrusive_ptr_t; 27 | 28 | } // namespace ev 29 | } // namespace rotor 30 | -------------------------------------------------------------------------------- /include/rotor/extended_error.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2021-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "arc.hpp" 10 | #include "message.h" 11 | #include 12 | #include 13 | 14 | #if defined(_MSC_VER) 15 | #pragma warning(push) 16 | #pragma warning(disable : 4251) 17 | #endif 18 | 19 | namespace rotor { 20 | 21 | struct extended_error_t; 22 | struct message_stringifier_t; 23 | 24 | /** \brief intrusive pointer to extended error type */ 25 | using extended_error_ptr_t = intrusive_ptr_t; 26 | 27 | /** \struct extended_error_t 28 | * \brief Holds string context, error_code and the pointer to the following error. 29 | * 30 | * 31 | * This is extension over std::error_code, to make it possible to identify the 32 | * context of the error (usually it is actor identity), and make it possible to 33 | * construct the chain of failures, that's why there is a smart pointer to the 34 | * next error. 35 | * 36 | */ 37 | struct ROTOR_API extended_error_t : arc_base_t { 38 | /** \brief error context, usually actor identity */ 39 | std::string context; 40 | 41 | /** \brief abstract error code, describing occurred error */ 42 | std::error_code ec; 43 | 44 | /** \brief pointer to the parent error */ 45 | extended_error_ptr_t next; 46 | 47 | /** \brief pointer request caused error */ 48 | message_ptr_t request; 49 | 50 | /** \brief extened error constructor */ 51 | extended_error_t(const std::string &context_, const std::error_code &ec_, const extended_error_ptr_t &next_ = {}, 52 | const message_ptr_t &request_ = {}) noexcept 53 | : context{context_}, ec{ec_}, next{next_}, request{request_} {} 54 | 55 | /** \brief human-readable detailed description of the error 56 | * 57 | * First, it stringifies own error in accordance with the context. 58 | * 59 | * Second, it recursively ask details on all following errors, appending them 60 | * into the result. The result string is returned. 61 | */ 62 | std::string message(const message_stringifier_t *stringifier = nullptr) const noexcept; 63 | 64 | /** 65 | * \brief returns root (inner-most) extended error 66 | */ 67 | extended_error_ptr_t root() const noexcept; 68 | }; 69 | 70 | /** \brief constructs smart pointer to the extened error */ 71 | ROTOR_API extended_error_ptr_t make_error(const std::string &context_, const std::error_code &ec_, 72 | const extended_error_ptr_t &next_ = {}, 73 | const message_ptr_t &request_ = {}) noexcept; 74 | 75 | } // namespace rotor 76 | 77 | #if defined(_MSC_VER) 78 | #pragma warning(pop) 79 | #endif 80 | -------------------------------------------------------------------------------- /include/rotor/fltk.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | /** \file wx.hpp 10 | * A convenience header to include rotor support for fltk 11 | */ 12 | 13 | #include "rotor/fltk/supervisor_config_fltk.h" 14 | #include "rotor/fltk/supervisor_fltk.h" 15 | #include "rotor/fltk/system_context_fltk.h" 16 | 17 | namespace rotor { 18 | 19 | /// namespace for fltk adapters for `rotor` 20 | namespace fltk {} 21 | 22 | } // namespace rotor 23 | -------------------------------------------------------------------------------- /include/rotor/fltk/supervisor_config_fltk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/supervisor_config.h" 10 | 11 | namespace rotor { 12 | namespace fltk { 13 | 14 | /** \brief alias for supervisor config */ 15 | using supervisor_config_fltk_t = supervisor_config_t; 16 | 17 | /** \brief CRTP supervisor fltk config builder (just an alias for generic builder) */ 18 | template using supervisor_config_fltk_builder_t = supervisor_config_builder_t; 19 | 20 | } // namespace fltk 21 | } // namespace rotor 22 | -------------------------------------------------------------------------------- /include/rotor/fltk/supervisor_fltk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/supervisor.h" 10 | #include "rotor/fltk/export.h" 11 | #include "rotor/fltk/supervisor_config_fltk.h" 12 | 13 | #include 14 | #include 15 | 16 | namespace rotor { 17 | namespace fltk { 18 | 19 | /** \struct supervisor_fltk_t 20 | * \brief delivers rotor-messages on top of fltk async callback 21 | * 22 | * The fltk-supervisor (and it's actors) role in `rotor` messaging is 23 | * to **abstract** destination endpoints from other non-fltk (I/O) event 24 | * loops, i.e. show some information in corresponding field/window/frame/... 25 | * Hence, the receiving rotor-messages actors should be aware of your 26 | * fltk-application system, i.e. hold refences to appropriate fields/windows 27 | * /widgets etc. 28 | * 29 | * Since conceptually, fltk-event loop is main GUI-loop, i.e. singleton, 30 | * there are no different execution contexts, i.e. only one (main) 31 | * executing thread; hence, there is no sense of having multiple supervisors. 32 | * 33 | * The commands translator (i.e. from click on the button to the `rotor` 34 | * -message send) should be also performed fltk-widgets which hold corresponding 35 | * fltk- actor/supervisor. 36 | * 37 | */ 38 | struct ROTOR_FLTK_API supervisor_fltk_t : public supervisor_t { 39 | /** \brief injects an alias for supervisor_config_fltk_t */ 40 | using config_t = supervisor_config_fltk_t; 41 | 42 | /** \brief injects templated supervisor_config_fltk_builder_t */ 43 | template using config_builder_t = supervisor_config_fltk_builder_t; 44 | 45 | /** \brief constructs new supervisor from supervisor config */ 46 | supervisor_fltk_t(supervisor_config_fltk_t &config); 47 | 48 | void start() noexcept override; 49 | void shutdown() noexcept override; 50 | void enqueue(message_ptr_t message) noexcept override; 51 | 52 | /** \brief generic non-public fields accessor */ 53 | template auto &access() noexcept; 54 | 55 | /** \struct timer_t 56 | * \brief timer structure, adopted for fltk-supervisor needs. 57 | */ 58 | struct timer_t { 59 | /** \brief alias for intrusive pointer for the fltk supervisor */ 60 | using supervisor_ptr_t = intrusive_ptr_t; 61 | 62 | /** \brief constructs timer from flt supervisor and timer handler */ 63 | timer_t(supervisor_ptr_t supervisor, timer_handler_base_t *handler); 64 | 65 | /** \brief intrusive pointer to the supervisor */ 66 | supervisor_ptr_t sup; 67 | 68 | /** \brief non-owning pointer to timer handler */ 69 | timer_handler_base_t *handler; 70 | }; 71 | 72 | protected: 73 | /** \brief unique pointer to timer */ 74 | using timer_ptr_t = std::unique_ptr; 75 | 76 | /** \brief timer id to timer pointer mapping type */ 77 | using timers_map_t = std::unordered_map; 78 | 79 | void do_start_timer(const pt::time_duration &interval, timer_handler_base_t &handler) noexcept override; 80 | void do_cancel_timer(request_id_t timer_id) noexcept override; 81 | 82 | /** \brief timer id to timer pointer mapping */ 83 | timers_map_t timers_map; 84 | }; 85 | 86 | } // namespace fltk 87 | } // namespace rotor 88 | -------------------------------------------------------------------------------- /include/rotor/fltk/system_context_fltk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2025 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/arc.hpp" 10 | #include "rotor/fltk/export.h" 11 | #include "rotor/system_context.h" 12 | 13 | namespace rotor { 14 | namespace fltk { 15 | 16 | struct supervisor_fltk_t; 17 | 18 | /** \struct system_context_fltk_t 19 | * \brief The FLTK system context to allow rotor messaging with fltk-backend. 20 | */ 21 | struct ROTOR_FLTK_API system_context_fltk_t : system_context_t { 22 | using system_context_t::system_context_t; 23 | 24 | private: 25 | void enqueue(message_ptr_t message) noexcept; 26 | friend supervisor_fltk_t; 27 | }; 28 | 29 | /** \brief intrusive pointer type for fltk system context */ 30 | using system_context_ptr_t = rotor::intrusive_ptr_t; 31 | 32 | } // namespace fltk 33 | } // namespace rotor 34 | -------------------------------------------------------------------------------- /include/rotor/forward.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // 3 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 4 | // 5 | // Distributed under the MIT Software License 6 | // 7 | 8 | #include 9 | #include "arc.hpp" 10 | #include 11 | 12 | namespace rotor { 13 | 14 | struct address_t; 15 | struct actor_base_t; 16 | struct handler_base_t; 17 | struct supervisor_t; 18 | struct system_context_t; 19 | 20 | using address_ptr_t = intrusive_ptr_t; 21 | 22 | /** \brief intrusive pointer for actor*/ 23 | using actor_ptr_t = intrusive_ptr_t; 24 | 25 | /** \brief intrusive pointer for handler */ 26 | using handler_ptr_t = intrusive_ptr_t; 27 | 28 | /** \brief intrusive pointer for supervisor */ 29 | using supervisor_ptr_t = intrusive_ptr_t; 30 | 31 | namespace pt = boost::posix_time; 32 | 33 | /** \brief timer identifier type in the scope of the actor */ 34 | using request_id_t = std::size_t; 35 | 36 | /** \brief factory which allows to create actors lazily or on demand 37 | * 38 | * The spawner address MUST be set to the newly created actor to 39 | * allow further spawning. 40 | * 41 | * This function might throw an exception, which is however ignored, 42 | * but spawner might attempt to create new actor instance. 43 | * 44 | */ 45 | using factory_t = std::function; 46 | 47 | } // namespace rotor 48 | 49 | namespace rotor::plugin { 50 | struct plugin_base_t; 51 | } 52 | -------------------------------------------------------------------------------- /include/rotor/message_stringifier.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/export.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #if defined(_MSC_VER) 16 | #pragma warning(push) 17 | #pragma warning(disable : 4251) 18 | #endif 19 | 20 | namespace rotor { 21 | 22 | struct message_base_t; 23 | 24 | /** \struct message_stringifier_t 25 | * \brief Abstract interface for making textual/string representation of a message 26 | * 27 | * As the concrete/final message type might not be known at compile time 28 | * the message stringifier tries to do it's best by guessing final message type. 29 | * (In other words, because the double dispatch visitor pattern is not available). 30 | * 31 | * That means that the message stringifier potentially has **LOW PERFORMANCE** 32 | * and should NOT be used, for example, for logging every message at least in 33 | * production code. 34 | * 35 | */ 36 | struct ROTOR_API message_stringifier_t { 37 | virtual ~message_stringifier_t() = default; 38 | 39 | /** \brief returns string representation of a message */ 40 | virtual std::string stringify(const message_base_t &) const; 41 | 42 | /** \brief dumps string representation of a message to output stream */ 43 | virtual void stringify_to(std::ostream &, const message_base_t &) const = 0; 44 | }; 45 | 46 | /** \brief smart pointer for message stringifier */ 47 | using message_stringifier_ptr_t = std::unique_ptr; 48 | 49 | }; // namespace rotor 50 | 51 | #if defined(_MSC_VER) 52 | #pragma warning(pop) 53 | #endif 54 | -------------------------------------------------------------------------------- /include/rotor/plugin/address_maker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "plugin_base.h" 10 | 11 | namespace rotor::plugin { 12 | 13 | /** \struct address_maker_plugin_t 14 | * 15 | * \brief create actor's addresses 16 | * 17 | * The plugin is executed on very early stage of actor creation 18 | * to assign its main address as soon as possible. 19 | * 20 | * If additional addresses are needed by the actor, they can be 21 | * asked via the plugin. 22 | * 23 | */ 24 | struct ROTOR_API address_maker_plugin_t : public plugin_base_t { 25 | using plugin_base_t::plugin_base_t; 26 | 27 | /** The plugin unique identity to allow further static_cast'ing*/ 28 | static const std::type_index class_identity; 29 | 30 | const std::type_index &identity() const noexcept override; 31 | 32 | void activate(actor_base_t *actor) noexcept override; 33 | void deactivate() noexcept override; 34 | 35 | /** \brief smart identity setter 36 | * 37 | * It can set the actor identity, and optionally append actor's main address 38 | * to let it be something like "net::http10 0x7fc0d0013c60" 39 | * 40 | */ 41 | 42 | void set_identity(std::string_view name, bool append_addr = true) noexcept; 43 | 44 | /** \brief creates additional actor address (on demand) 45 | * 46 | * This is just a shortcut method to create_address() of supervisor 47 | * 48 | */ 49 | virtual address_ptr_t create_address() noexcept; 50 | 51 | /** generates default identity like "actor 0x7fc0d0013c60" */ 52 | virtual void generate_identity() noexcept; 53 | }; 54 | 55 | } // namespace rotor::plugin 56 | -------------------------------------------------------------------------------- /include/rotor/plugin/delivery.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "plugin_base.h" 10 | #include "../message_stringifier.h" 11 | #include 12 | 13 | #if !defined(NDEBUG) && !defined(ROTOR_DEBUG_DELIVERY) 14 | #define ROTOR_DO_DELIVERY_DEBUG 1 15 | #endif 16 | 17 | namespace rotor::plugin { 18 | 19 | /** \struct local_delivery_t 20 | * 21 | * \brief basic local message delivery implementation 22 | */ 23 | struct ROTOR_API local_delivery_t { 24 | 25 | /** \brief delivers an message for self of one of child-actors (non-supervisors) 26 | * 27 | * Supervisor iterates on subscriptions (handlers) on the message destination address: 28 | * 29 | * - If the handler is local (i.e. it's actor belongs to the same supervisor), 30 | * - Otherwise the message is forwarded for delivery for the foreign supervisor, 31 | * which owns the handler. 32 | * 33 | */ 34 | 35 | static void delivery(message_ptr_t &message, const subscription_t::joint_handlers_t &local_recipients) noexcept; 36 | }; 37 | 38 | /** \struct inspected_local_delivery_t 39 | * 40 | * \brief debugging local message delivery implementation with dumping details to stdout. 41 | */ 42 | struct ROTOR_API inspected_local_delivery_t { 43 | 44 | /** \brief delivers the message to the recipients, possibly dumping it to console */ 45 | static void delivery(message_ptr_t &message, const subscription_t::joint_handlers_t &local_recipients, 46 | const message_stringifier_t *stringifier) noexcept; 47 | 48 | /** \brief dumps discarded message */ 49 | static void discard(message_ptr_t &message, const message_stringifier_t *stringifier) noexcept; 50 | }; 51 | 52 | #if !defined(ROTOR_DO_DELIVERY_DEBUG) 53 | using default_local_delivery_t = local_delivery_t; 54 | #else 55 | using default_local_delivery_t = inspected_local_delivery_t; 56 | #endif 57 | 58 | /** \struct delivery_plugin_base_t 59 | * 60 | * \brief base implementation for messages delivery plugin 61 | */ 62 | struct ROTOR_API delivery_plugin_base_t : public plugin_base_t { 63 | using plugin_base_t::plugin_base_t; 64 | 65 | /** \brief main messages dispatcher interface */ 66 | virtual size_t process() noexcept = 0; 67 | void activate(actor_base_t *actor) noexcept override; 68 | 69 | protected: 70 | /** \brief non-owning raw pointer of supervisor's messages queue */ 71 | messages_queue_t *queue = nullptr; 72 | 73 | /** \brief non-owning raw pointer to supervisor's main address */ 74 | address_t *address = nullptr; 75 | 76 | /** \brief non-owning raw pointer to supervisor's subscriptions map */ 77 | subscription_t *subscription_map; 78 | 79 | /** \brief non-owning raw pointer to system's stringifier */ 80 | const message_stringifier_t *stringifier; 81 | }; 82 | 83 | /** \brief templated message delivery plugin, to allow local message delivery be customized */ 84 | template struct delivery_plugin_t : public delivery_plugin_base_t { 85 | using delivery_plugin_base_t::delivery_plugin_base_t; 86 | 87 | /** The plugin unique identity to allow further static_cast'ing*/ 88 | static const std::type_index class_identity; 89 | 90 | const std::type_index &identity() const noexcept override { return class_identity; } 91 | 92 | inline size_t process() noexcept override; 93 | }; 94 | 95 | template 96 | const std::type_index delivery_plugin_t::class_identity = typeid(local_delivery_t); 97 | 98 | } // namespace rotor::plugin 99 | -------------------------------------------------------------------------------- /include/rotor/plugin/foreigners_support.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "plugin_base.h" 10 | 11 | namespace rotor::plugin { 12 | 13 | /** \struct foreigners_support_plugin_t 14 | * 15 | * \brief allows non-local actors to subscribe on the local addresses of a supervisor. 16 | */ 17 | struct ROTOR_API foreigners_support_plugin_t : public plugin_base_t { 18 | using plugin_base_t::plugin_base_t; 19 | 20 | /** The plugin unique identity to allow further static_cast'ing*/ 21 | static const std::type_index class_identity; 22 | 23 | const std::type_index &identity() const noexcept override; 24 | 25 | void activate(actor_base_t *actor) noexcept override; 26 | void deactivate() noexcept override; 27 | 28 | /** \brief handler for message call */ 29 | virtual void on_call(message::handler_call_t &message) noexcept; 30 | 31 | /** \brief unsubscription message handler */ 32 | virtual void on_unsubscription(message::commit_unsubscription_t &message) noexcept; 33 | 34 | /** \brief external unsubscription message handler */ 35 | virtual void on_subscription_external(message::external_subscription_t &message) noexcept; 36 | 37 | private: 38 | subscription_container_t foreign_points; 39 | }; 40 | 41 | } // namespace rotor::plugin 42 | -------------------------------------------------------------------------------- /include/rotor/plugin/init_shutdown.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "plugin_base.h" 10 | 11 | namespace rotor::plugin { 12 | 13 | /** \struct init_shutdown_plugin_t 14 | * 15 | * \brief manages actors init and shutdown procedures 16 | * 17 | * All actor plugins are polled when init(shutdown) request is received 18 | * in the direct(reverse) order. When all actors confirmed that they are 19 | * ready then initialization (shutdown) is confirmed to uplink. 20 | * 21 | */ 22 | struct ROTOR_API init_shutdown_plugin_t : public plugin_base_t { 23 | using plugin_base_t::plugin_base_t; 24 | 25 | /** The plugin unique identity to allow further static_cast'ing*/ 26 | static const std::type_index class_identity; 27 | 28 | const std::type_index &identity() const noexcept override; 29 | 30 | void activate(actor_base_t *actor) noexcept override; 31 | 32 | /** \brief init message handler */ 33 | void on_init(message::init_request_t &message) noexcept; 34 | 35 | /** \brief shutdown message handler */ 36 | void on_shutdown(message::shutdown_request_t &message) noexcept; 37 | 38 | bool handle_shutdown(message::shutdown_request_t *message) noexcept override; 39 | bool handle_init(message::init_request_t *message) noexcept override; 40 | }; 41 | 42 | } // namespace rotor::plugin 43 | -------------------------------------------------------------------------------- /include/rotor/plugin/lifetime.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "plugin_base.h" 10 | 11 | namespace rotor::plugin { 12 | 13 | /** \struct lifetime_plugin_t 14 | * 15 | * \brief manages all actor subscriptions (i.e. from plugins or actor itself). 16 | * 17 | * The plugin main focus to properly cancel subscriptions, i.e. every address 18 | * where an actor plugin or the actor itself was subscribscribed to. 19 | * 20 | */ 21 | struct ROTOR_API lifetime_plugin_t : public plugin_base_t { 22 | using plugin_base_t::plugin_base_t; 23 | 24 | /** The plugin unique identity to allow further static_cast'ing*/ 25 | static const std::type_index class_identity; 26 | 27 | const std::type_index &identity() const noexcept override; 28 | 29 | void activate(actor_base_t *actor) noexcept override; 30 | void deactivate() noexcept override; 31 | 32 | /** \brief records initial subscription information */ 33 | void initate_subscription(const subscription_info_ptr_t &info) noexcept; 34 | 35 | /** \brief alias for unsubscription trigger (see below) */ 36 | void unsubscribe(const handler_ptr_t &h, const address_ptr_t &addr) noexcept; 37 | 38 | /** \brief triggers unsubscription 39 | * 40 | * For internal subscriptions it answers with unsubscription 41 | * confirmation; for foreign subscription it sends external unsubscription 42 | * request 43 | */ 44 | void unsubscribe(const subscription_info_ptr_t &info) noexcept; 45 | 46 | /** \brief reaction on subscription 47 | * 48 | * It just forwards it to the actor to poll related plugins 49 | */ 50 | virtual void on_subscription(message::subscription_t &) noexcept; 51 | 52 | /** \brief reaction on unsubscription 53 | * 54 | * It just forwards it to the actor to poll related plugins 55 | */ 56 | virtual void on_unsubscription(message::unsubscription_t &) noexcept; 57 | 58 | /** \brief reaction on external unsubscription 59 | * 60 | * It just forwards it to the actor to poll related plugins 61 | */ 62 | virtual void on_unsubscription_external(message::unsubscription_external_t &) noexcept; 63 | 64 | bool handle_unsubscription(const subscription_point_t &point, bool external) noexcept override; 65 | 66 | bool handle_shutdown(message::shutdown_request_t *message) noexcept override; 67 | 68 | /** \brief generic non-public fields accessor */ 69 | template auto &access() noexcept; 70 | 71 | private: 72 | void unsubscribe() noexcept; 73 | 74 | /** \brief recorded subscription points (i.e. handler/address pairs) */ 75 | subscription_container_t points; 76 | 77 | bool ready_to_shutdown() noexcept; 78 | }; 79 | 80 | } // namespace rotor::plugin 81 | -------------------------------------------------------------------------------- /include/rotor/plugin/link_client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "plugin_base.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #if defined(_MSC_VER) 16 | #pragma warning(push) 17 | #pragma warning(disable : 4251) 18 | #endif 19 | 20 | namespace rotor::plugin { 21 | 22 | /** \struct link_client_plugin_t 23 | * 24 | * \brief allows actor to have active (client) role in linking 25 | * 26 | * The plugin keeps records of all "servers" where it is connected to. 27 | * When actor is going to shutdown it will notify peers about unlinking. 28 | * 29 | * It is allowed to have a custom actors' callback on every link result. 30 | * connected clients will send unlink confirmation (or until 31 | * timeout will trigger). 32 | * 33 | */ 34 | struct ROTOR_API link_client_plugin_t : public plugin_base_t { 35 | /** \brief callback action upon link */ 36 | using link_callback_t = std::function; 37 | 38 | /** \brief unlink interceptor callback 39 | * 40 | * if it returns true, the unlink is not performed. It is assumed, 41 | * that `forget_link` will be called later 42 | */ 43 | using unlink_reaction_t = std::function; 44 | 45 | using plugin_base_t::plugin_base_t; 46 | 47 | /** The plugin unique identity to allow further static_cast'ing*/ 48 | static const std::type_index class_identity; 49 | 50 | const std::type_index &identity() const noexcept override; 51 | 52 | void activate(actor_base_t *actor) noexcept override; 53 | 54 | /** \brief attempt to link with the address 55 | * 56 | * If `operational_only` is set, then the server side will respond 57 | * only upon becoming operational. 58 | * 59 | * The link callback is always invoked upon response (successful or 60 | * nor) receiving. 61 | * 62 | */ 63 | virtual void link(const address_ptr_t &address, bool operational_only = true, 64 | const link_callback_t &callback = {}) noexcept; 65 | 66 | /** \brief sets actor's callback on unlink request */ 67 | template void on_unlink(F &&callback) noexcept { unlink_reaction = std::forward(callback); } 68 | 69 | /** \brief handler for link response */ 70 | virtual void on_link_response(message::link_response_t &message) noexcept; 71 | 72 | /** \brief handler for unlink request */ 73 | virtual void on_unlink_request(message::unlink_request_t &message) noexcept; 74 | 75 | bool handle_shutdown(message::shutdown_request_t *message) noexcept override; 76 | bool handle_init(message::init_request_t *message) noexcept override; 77 | 78 | /** \brief continues previously suspended unlink request */ 79 | void forget_link(message::unlink_request_t &message) noexcept; 80 | 81 | private: 82 | enum class link_state_t { LINKING, OPERATIONAL, UNLINKING }; 83 | struct server_record_t { 84 | link_callback_t callback; 85 | link_state_t state; 86 | request_id_t request_id; 87 | }; 88 | using servers_map_t = std::unordered_map; 89 | using unlink_req_t = intrusive_ptr_t; 90 | using unlink_queue_t = std::list; 91 | 92 | void try_forget_links(bool attempt_shutdown) noexcept; 93 | servers_map_t servers_map; 94 | unlink_reaction_t unlink_reaction; 95 | unlink_queue_t unlink_queue; 96 | bool configured = false; 97 | }; 98 | 99 | } // namespace rotor::plugin 100 | 101 | #if defined(_MSC_VER) 102 | #pragma warning(pop) 103 | #endif 104 | -------------------------------------------------------------------------------- /include/rotor/plugin/link_server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "plugin_base.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #if defined(_MSC_VER) 16 | #pragma warning(push) 17 | #pragma warning(disable : 4251) 18 | #endif 19 | 20 | namespace rotor::plugin { 21 | 22 | /** \struct link_server_plugin_t 23 | * 24 | * \brief allows actor to have passive (server) role in linking 25 | * 26 | * The plugin keeps records of connected clients. When actor is 27 | * going to shutdown it will block shutdown process until all 28 | * connected clients will send unlink confirmation (or until 29 | * timeout will trigger). 30 | * 31 | */ 32 | struct ROTOR_API link_server_plugin_t : public plugin_base_t { 33 | using plugin_base_t::plugin_base_t; 34 | 35 | /** The plugin unique identity to allow further static_cast'ing*/ 36 | static const std::type_index class_identity; 37 | 38 | const std::type_index &identity() const noexcept override; 39 | 40 | void activate(actor_base_t *actor) noexcept override; 41 | 42 | /** \brief reaction upon link request */ 43 | virtual void on_link_request(message::link_request_t &message) noexcept; 44 | 45 | /** \brief reaction upon link unlink response */ 46 | virtual void on_unlink_response(message::unlink_response_t &message) noexcept; 47 | 48 | /** \brief reaction upon link unlink notify */ 49 | virtual void on_unlink_notify(message::unlink_notify_t &message) noexcept; 50 | 51 | bool handle_shutdown(message::shutdown_request_t *message) noexcept override; 52 | void handle_start(message::start_trigger_t *message) noexcept override; 53 | 54 | /** \brief returns true if there is any connected client */ 55 | virtual bool has_clients() noexcept { return !linked_clients.empty(); } 56 | 57 | /** \brief let clients know that the actor is going to shut self down */ 58 | virtual void notify_shutdown() noexcept; 59 | 60 | /** \brief generic non-public fields accessor */ 61 | template auto &access() noexcept; 62 | 63 | private: 64 | enum class link_state_t { PENDING, OPERATIONAL, UNLINKING }; 65 | using link_request_ptr_t = intrusive_ptr_t; 66 | using request_option_t = std::optional; 67 | struct link_info_t { 68 | link_info_t(link_state_t state_, link_request_ptr_t request_) noexcept : state{state_}, request{request_} {} 69 | link_state_t state; 70 | link_request_ptr_t request; 71 | request_option_t unlink_request; 72 | bool shutdown_notified = false; 73 | }; 74 | 75 | using linked_clients_t = std::unordered_map; 76 | linked_clients_t linked_clients; 77 | }; 78 | 79 | } // namespace rotor::plugin 80 | 81 | #if defined(_MSC_VER) 82 | #pragma warning(pop) 83 | #endif 84 | -------------------------------------------------------------------------------- /include/rotor/plugin/locality.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "plugin_base.h" 10 | 11 | namespace rotor::plugin { 12 | 13 | /** \struct locality_plugin_t 14 | * 15 | * \brief detects and assigns locality leader to the supervisor 16 | * 17 | * For the supervisors hierarchy it detects top-level supervisor which uses 18 | * the same locality and assigns it's queue to each of the supervisors 19 | * in the tree. 20 | * 21 | */ 22 | struct ROTOR_API locality_plugin_t : public plugin_base_t { 23 | using plugin_base_t::plugin_base_t; 24 | 25 | /** The plugin unique identity to allow further static_cast'ing*/ 26 | static const std::type_index class_identity; 27 | 28 | const std::type_index &identity() const noexcept override; 29 | 30 | void activate(actor_base_t *actor) noexcept override; 31 | }; 32 | 33 | } // namespace rotor::plugin 34 | -------------------------------------------------------------------------------- /include/rotor/plugin/resources.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "plugin_base.h" 10 | #include 11 | 12 | #if defined(_MSC_VER) 13 | #pragma warning(push) 14 | #pragma warning(disable : 4251) 15 | #endif 16 | 17 | namespace rotor::plugin { 18 | 19 | using resource_id_t = std::size_t; 20 | 21 | /** \struct resources_plugin_t 22 | * 23 | * \brief "lock" for external resources 24 | * 25 | * The main purpose of the plugin is to let actor know, that some 26 | * external resources are acquired, and the actor should will be 27 | * suspended until they will be released. 28 | * 29 | * The suspension will happen during init and shutdown phases, e.g.: 30 | * - actor can wait, until connection will be established 31 | * - actor can wait, until it receives handshake from remote system 32 | * - etc... 33 | * 34 | * The used `resource_id` can be anything, which has meaning for 35 | * an actor. Internally `resource_id` is just an index in vector, 36 | * so don't create it too big. 37 | * 38 | */ 39 | struct ROTOR_API resources_plugin_t : public plugin_base_t { 40 | using plugin_base_t::plugin_base_t; 41 | 42 | /** The plugin unique identity to allow further static_cast'ing*/ 43 | static const std::type_index class_identity; 44 | 45 | const std::type_index &identity() const noexcept override; 46 | 47 | void activate(actor_base_t *actor) noexcept override; 48 | bool handle_init(message::init_request_t *message) noexcept override; 49 | bool handle_shutdown(message::shutdown_request_t *message) noexcept override; 50 | 51 | /** \brief increments the resource counter (aka locks) 52 | * 53 | * It will block init/shutdown until the resource be released 54 | * 55 | */ 56 | virtual void acquire(resource_id_t = 0) noexcept; 57 | 58 | /** \brief decrements the resource counter (aka unlocks) 59 | * 60 | * If there is no any resource free, the init/shutdown 61 | * procedure (if it was started) will be continued 62 | * 63 | */ 64 | virtual bool release(resource_id_t = 0) noexcept; 65 | 66 | /** \brief returns counter value for `resource_id` 67 | * 68 | * if the resource was freed, zero is returned 69 | * 70 | */ 71 | virtual std::uint32_t has(resource_id_t = 0) noexcept; 72 | 73 | /** \brief returns true if there is any resource is locked */ 74 | virtual bool has_any() noexcept; 75 | 76 | /** \brief generic non-public fields accessor */ 77 | template auto &access() noexcept; 78 | 79 | private: 80 | using Resources = std::vector; 81 | Resources resources; 82 | bool configured = false; 83 | }; 84 | 85 | } // namespace rotor::plugin 86 | 87 | #if defined(_MSC_VER) 88 | #pragma warning(pop) 89 | #endif 90 | -------------------------------------------------------------------------------- /include/rotor/plugin/starter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "plugin_base.h" 10 | 11 | namespace rotor::plugin { 12 | 13 | /** \struct starter_plugin_t 14 | * 15 | * \brief allows custom (actor) subscriptions and it is responsible 16 | * for starting actor when it receives {@link message::start_trigger_t}. 17 | * 18 | */ 19 | struct ROTOR_API starter_plugin_t : public plugin_base_t { 20 | using plugin_base_t::plugin_base_t; 21 | 22 | /** The plugin unique identity to allow further static_cast'ing*/ 23 | static const std::type_index class_identity; 24 | 25 | const std::type_index &identity() const noexcept override; 26 | 27 | void activate(actor_base_t *actor) noexcept override; 28 | void deactivate() noexcept override; 29 | 30 | /** \brief subscribes *actor* handler on main actor address */ 31 | template subscription_info_ptr_t subscribe_actor(Handler &&handler) noexcept; 32 | 33 | /** \brief subscribes *actor* handler on arbitrary address */ 34 | template 35 | subscription_info_ptr_t subscribe_actor(Handler &&handler, const address_ptr_t &addr) noexcept; 36 | 37 | bool handle_init(message::init_request_t *) noexcept override; 38 | void handle_start(message::start_trigger_t *message) noexcept override; 39 | bool handle_subscription(message::subscription_t &message) noexcept override; 40 | 41 | /** \brief start message reaction */ 42 | void on_start(message::start_trigger_t &message) noexcept; 43 | 44 | private: 45 | subscription_container_t tracked; 46 | bool configured = false; 47 | }; 48 | 49 | } // namespace rotor::plugin 50 | -------------------------------------------------------------------------------- /include/rotor/plugins.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | // actors plugins 10 | #include "plugin/address_maker.h" 11 | #include "plugin/init_shutdown.h" 12 | #include "plugin/lifetime.h" 13 | #include "plugin/link_client.h" 14 | #include "plugin/link_server.h" 15 | #include "plugin/registry.h" 16 | #include "plugin/resources.h" 17 | #include "plugin/starter.h" 18 | 19 | // supervisors plugins 20 | #include "plugin/child_manager.h" 21 | #include "plugin/delivery.h" 22 | #include "plugin/locality.h" 23 | #include "plugin/foreigners_support.h" 24 | -------------------------------------------------------------------------------- /include/rotor/policy.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | namespace rotor { 10 | 11 | /** \brief how to behave on child actor initialization failures */ 12 | enum class supervisor_policy_t { 13 | /** \brief shutdown supervisor (and all its actors) if a child-actor 14 | * fails during supervisor initialization phase 15 | */ 16 | shutdown_self = 1, 17 | 18 | /** \brief shutdown a failed child and continue initialization */ 19 | shutdown_failed, 20 | }; 21 | 22 | /** \brief spawner's actor restart policy */ 23 | enum class restart_policy_t { 24 | /** \brief always restart actor */ 25 | always, 26 | /** \brief never restart actor */ 27 | never, 28 | 29 | /** \brief ask terminated actor whether a new instance should be spawned 30 | * `should_restart()` method is used 31 | */ 32 | ask_actor, 33 | 34 | /** \brief restart actor only when it terminated normally (without error) */ 35 | normal_only, 36 | 37 | /** \brief restart actor only when it terminated abnormally (with error) */ 38 | fail_only, 39 | }; 40 | 41 | } // namespace rotor 42 | -------------------------------------------------------------------------------- /include/rotor/spawner.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "forward.hpp" 10 | #include "policy.h" 11 | 12 | #if defined(_MSC_VER) 13 | #pragma warning(push) 14 | #pragma warning(disable : 4251) 15 | #endif 16 | 17 | namespace rotor { 18 | 19 | namespace details { 20 | enum class request_state_t { NONE, SENT, CONFIRMED }; 21 | } 22 | 23 | /** \struct spawner_t 24 | * \brief allows automatically restart actors 25 | * 26 | * Spawner will NOT create new actor instances when 27 | * supervisor is SHUTTING_DOWN or SHUT_DOWN. 28 | * 29 | */ 30 | struct ROTOR_API spawner_t { 31 | ~spawner_t(); 32 | 33 | /** \brief minimum amount of time before respawning new actor 34 | * 35 | * Default value is 15 seconds. 36 | * 37 | */ 38 | spawner_t &&restart_period(const pt::time_duration &) noexcept; 39 | 40 | /** \brief determines whether new actor instance should be spawned */ 41 | spawner_t &&restart_policy(restart_policy_t) noexcept; 42 | 43 | /** \brief maximum amount of spawned actor instances 44 | * 45 | * The default value is `0`, which means try new restart 46 | * attempt if that is allowed by the policy. 47 | * 48 | */ 49 | spawner_t &&max_attempts(size_t) noexcept; 50 | 51 | /** \brief shutdown actor when the spawned actor crashed 52 | * 53 | * The actor "crash" means actor terminated with non-zero 54 | * error code. 55 | * 56 | */ 57 | spawner_t &&escalate_failure(bool = true) noexcept; 58 | 59 | /** \brief sends a message to supervisor to spawn 60 | * the new actor instance 61 | */ 62 | void spawn() noexcept; 63 | 64 | private: 65 | spawner_t(factory_t factory, supervisor_t &supervisor) noexcept; 66 | 67 | factory_t factory; 68 | supervisor_t &supervisor; 69 | pt::time_duration period = pt::seconds{15}; 70 | restart_policy_t policy = restart_policy_t::normal_only; 71 | size_t attempts = 0; 72 | bool done = false; 73 | bool escalate = false; 74 | 75 | friend struct supervisor_t; 76 | }; 77 | 78 | } // namespace rotor 79 | 80 | #if defined(_MSC_VER) 81 | #pragma warning(pop) 82 | #endif 83 | -------------------------------------------------------------------------------- /include/rotor/state.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | namespace rotor { 10 | 11 | /** \brief state of actor in `rotor` */ 12 | enum class state_t { 13 | UNKNOWN, 14 | NEW, 15 | INITIALIZING, 16 | INITIALIZED, 17 | OPERATIONAL, 18 | SHUTTING_DOWN, 19 | SHUT_DOWN, 20 | }; 21 | 22 | } // namespace rotor 23 | -------------------------------------------------------------------------------- /include/rotor/subscription.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/forward.hpp" 10 | #include "rotor/address.hpp" 11 | #include "rotor/subscription_point.h" 12 | #include "rotor/message.h" 13 | #include 14 | #include 15 | 16 | #if defined(_MSC_VER) 17 | #pragma warning(push) 18 | #pragma warning(disable : 4251) 19 | #endif 20 | 21 | namespace rotor { 22 | 23 | /** \struct subscription_t 24 | * \brief Holds and classifies message handlers on behalf of supervisor 25 | * 26 | * The handlers are classified by message type and by the source supervisor, i.e. 27 | * whether the handler's supervisor is external or not. 28 | * 29 | */ 30 | struct ROTOR_API subscription_t { 31 | /** \brief alias for message type (i.e. stringized typeid) */ 32 | using message_type_t = const void *; 33 | 34 | /** \brief vector of handler pointers */ 35 | using handlers_t = std::vector; 36 | 37 | /** \struct joint_handlers_t 38 | * \brief pair internal and external {@link handler_t} 39 | */ 40 | struct joint_handlers_t { 41 | /** \brief internal handlers, i.e. those which belong to actors of the supervisor */ 42 | handlers_t internal; 43 | /** \brief external handlers, i.e. those which belong to actors of other supervisor */ 44 | handlers_t external; 45 | }; 46 | 47 | subscription_t() noexcept; 48 | 49 | /** \brief upgrades subscription_point_t into subscription_info smart pointer 50 | * 51 | * Performs the classification of the point, i.e. whether handler and address 52 | * are internal or external, records the state subscription state and records 53 | * the handler among address handlers. 54 | * 55 | */ 56 | subscription_info_ptr_t materialize(const subscription_point_t &point) noexcept; 57 | 58 | /** \brief sets new handler for the subscription point */ 59 | void update(subscription_point_t &point, handler_ptr_t &new_handler) noexcept; 60 | 61 | /** \brief remove subscription_info from `internal_infos` and `mine_handlers` */ 62 | void forget(const subscription_info_ptr_t &info) noexcept; 63 | 64 | /** \brief returns list of all handlers for the message (internal and external) */ 65 | const joint_handlers_t *get_recipients(const message_base_t &message) const noexcept; 66 | 67 | /** \brief generic non-public fields accessor */ 68 | template auto &access() noexcept; 69 | 70 | private: 71 | struct subscription_key_t { 72 | address_t *address; 73 | message_type_t message_type; 74 | inline bool operator==(const subscription_key_t &other) const noexcept { 75 | return address == other.address && message_type == other.message_type; 76 | } 77 | }; 78 | 79 | struct subscription_key_hash_t { 80 | inline std::size_t operator()(const subscription_key_t &k) const noexcept { 81 | return std::size_t(k.address) + 0x9e3779b9 + (size_t(k.message_type) << 6) + (size_t(k.message_type) >> 2); 82 | } 83 | }; 84 | 85 | using addressed_handlers_t = boost::unordered_map; 86 | 87 | using info_container_t = boost::unordered_map>; 88 | address_t *main_address; 89 | info_container_t internal_infos; 90 | addressed_handlers_t mine_handlers; 91 | }; 92 | 93 | } // namespace rotor 94 | 95 | #if defined(_MSC_VER) 96 | #pragma warning(pop) 97 | #endif 98 | -------------------------------------------------------------------------------- /include/rotor/system_context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "address.hpp" 10 | #include "supervisor_config.h" 11 | #include "extended_error.h" 12 | #include "message_stringifier.h" 13 | 14 | #include 15 | 16 | #if defined(_MSC_VER) 17 | #pragma warning(push) 18 | #pragma warning(disable : 4251) 19 | #endif 20 | 21 | namespace rotor { 22 | 23 | struct supervisor_t; 24 | struct actor_base_t; 25 | using supervisor_ptr_t = intrusive_ptr_t; 26 | 27 | /** \struct system_context_t 28 | * \brief The system context holds root {@link supervisor_t} 29 | * (intrusive pointer) and may be loop-related details in derived classes 30 | * 31 | */ 32 | struct ROTOR_API system_context_t : arc_base_t { 33 | public: 34 | /** \brief returns builder for root supervisor */ 35 | template auto create_supervisor(); 36 | 37 | /** \brief returns root supervisor */ 38 | inline supervisor_ptr_t get_supervisor() noexcept { return supervisor; } 39 | 40 | system_context_t() = default; 41 | 42 | system_context_t(const system_context_t &) = delete; 43 | system_context_t(system_context_t &&) = delete; 44 | virtual ~system_context_t(); 45 | 46 | /** \brief fatal error handler 47 | * 48 | * The error is fatal, is further `rotor` behavior is undefined. The method should 49 | * be overriden in derived classes for error propagation/notification. The default 50 | * implementation is to output the error to `std::err` and invoke `std::abort()`. 51 | * 52 | */ 53 | virtual void on_error(actor_base_t *actor, const extended_error_ptr_t &ec) noexcept; 54 | 55 | /** \brief identifies the context. 56 | * 57 | * By default, is is just human-readable address of the system context 58 | * 59 | */ 60 | virtual std::string identity() noexcept; 61 | 62 | /** \brief generic non-public fields accessor */ 63 | template auto &access() noexcept; 64 | 65 | /** \brief returns the default stringifier 66 | * 67 | * The stringifier is lazily constructed on demand via `make_stringifier` method. 68 | * 69 | */ 70 | const message_stringifier_t &get_stringifier(); 71 | 72 | protected: 73 | /** \brief constructs message stringifier */ 74 | virtual message_stringifier_ptr_t make_stringifier() const noexcept; 75 | 76 | private: 77 | friend struct supervisor_t; 78 | supervisor_ptr_t supervisor; 79 | message_stringifier_ptr_t stringifier; 80 | }; 81 | 82 | /** \brief intrusive pointer for system context */ 83 | using system_context_ptr_t = intrusive_ptr_t; 84 | 85 | } // namespace rotor 86 | 87 | #if defined(_MSC_VER) 88 | #pragma warning(pop) 89 | #endif 90 | -------------------------------------------------------------------------------- /include/rotor/thread.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | /** \file thread.hpp 10 | * A convenience header to include rotor support for pure thread backends 11 | */ 12 | 13 | #include "rotor/thread/supervisor_thread.h" 14 | #include "rotor/thread/system_context_thread.h" 15 | 16 | namespace rotor { 17 | 18 | /// namespace for pure thread backend (supervisor) for `rotor` 19 | namespace thread {} 20 | 21 | } // namespace rotor 22 | -------------------------------------------------------------------------------- /include/rotor/thread/supervisor_thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/supervisor.h" 10 | #include "rotor/thread/export.h" 11 | #include "system_context_thread.h" 12 | 13 | namespace rotor { 14 | namespace thread { 15 | 16 | /** \struct supervisor_thread_t 17 | * \brief pure thread supervisor dedicated for blocking I/O or computing operations 18 | * 19 | * To let it work, the potentially long blocking I/O operations should 20 | * be split into smaller chunks, it should be wrapped into message, and actually 21 | * in the handler the operation should be performed. Lastly, the handler should 22 | * be tagged I/O. 23 | */ 24 | struct ROTOR_THREAD_API supervisor_thread_t : public supervisor_t { 25 | /** \brief constructs new thread supervisor */ 26 | inline supervisor_thread_t(supervisor_config_t &cfg) : supervisor_t{cfg} {} 27 | 28 | void start() noexcept override; 29 | void shutdown() noexcept override; 30 | void enqueue(message_ptr_t message) noexcept override; 31 | void intercept(message_ptr_t &message, const void *tag, const continuation_t &continuation) noexcept override; 32 | 33 | /** \brief updates timer and fires timer handlers, which have been expired */ 34 | void update_time() noexcept; 35 | 36 | void do_start_timer(const pt::time_duration &interval, timer_handler_base_t &handler) noexcept override; 37 | void do_cancel_timer(request_id_t timer_id) noexcept override; 38 | }; 39 | 40 | } // namespace thread 41 | } // namespace rotor 42 | -------------------------------------------------------------------------------- /include/rotor/thread/system_context_thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/arc.hpp" 10 | #include "rotor/system_context.h" 11 | #include "rotor/timer_handler.hpp" 12 | #include "rotor/thread/export.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #if defined(_MSC_VER) 20 | #pragma warning(push) 21 | #pragma warning(disable : 4251) 22 | #endif 23 | 24 | namespace rotor { 25 | namespace thread { 26 | 27 | struct supervisor_thread_t; 28 | 29 | /** \brief intrusive pointer for thread supervisor */ 30 | using supervisor_ptr_t = intrusive_ptr_t; 31 | 32 | /** \struct system_context_thread_t 33 | * \brief The thread system context, for blocking operations 34 | * 35 | */ 36 | struct ROTOR_THREAD_API system_context_thread_t : public system_context_t { 37 | /** \brief constructs thread system context */ 38 | system_context_thread_t() noexcept; 39 | 40 | /** \brief invokes blocking execution of the supervisor 41 | * 42 | * It blocks until root supervisor shuts down. 43 | * 44 | */ 45 | virtual void run() noexcept; 46 | 47 | /** \brief checks for messages from external threads and fires expired timers */ 48 | void check() noexcept; 49 | 50 | protected: 51 | /** \brief an alias for monotonic clock */ 52 | using clock_t = std::chrono::steady_clock; 53 | 54 | /** \struct deadline_info_t 55 | * \brief struct to keep timer handlers 56 | */ 57 | struct deadline_info_t { 58 | /** \brief non-owning pointer to timer handler */ 59 | timer_handler_base_t *handler; 60 | 61 | /** \brief time point, after which the timer is considered expired */ 62 | clock_t::time_point deadline; 63 | }; 64 | 65 | /** \brief ordered list of deadline infos (type) */ 66 | using list_t = std::list; 67 | 68 | /** \brief fires handlers for expired timers */ 69 | void update_time() noexcept; 70 | 71 | /** \brief start timer implementation */ 72 | void start_timer(const pt::time_duration &interval, timer_handler_base_t &handler) noexcept; 73 | 74 | /** \brief cancel timer implementation */ 75 | 76 | void cancel_timer(request_id_t timer_id) noexcept; 77 | 78 | /** \brief mutex for inbound queue */ 79 | std::mutex mutex; 80 | 81 | /** \brief cv for notifying about pushing messages into inbound queue */ 82 | std::condition_variable cv; 83 | 84 | /** \brief current time */ 85 | clock_t::time_point now; 86 | 87 | /** \brief ordered list of deadline infos */ 88 | list_t timer_nodes; 89 | 90 | /** \brief whether the context is intercepting blocking (I/O) handler */ 91 | bool intercepting = false; 92 | 93 | friend struct supervisor_thread_t; 94 | }; 95 | 96 | /** \brief intrusive pointer type for system context thread context */ 97 | using system_context_ptr_t = rotor::intrusive_ptr_t; 98 | 99 | } // namespace thread 100 | } // namespace rotor 101 | 102 | #if defined(_MSC_VER) 103 | #pragma warning(pop) 104 | #endif 105 | -------------------------------------------------------------------------------- /include/rotor/timer_handler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "forward.hpp" 10 | #include 11 | 12 | namespace rotor { 13 | 14 | /** \struct timer_handler_base_t 15 | * \brief Base class for timer handler 16 | */ 17 | struct timer_handler_base_t { 18 | 19 | /** \brief actor, which "owns" timer, i.e. where "start_timer" was invoked" */ 20 | actor_base_t *owner; 21 | 22 | /** \brief timer identity (aka timer request id) */ 23 | request_id_t request_id; 24 | 25 | /** \brief constructs timer handler from non-owning pointer to timer and timer request id */ 26 | timer_handler_base_t(actor_base_t *owner_, request_id_t request_id_) noexcept 27 | : owner{owner_}, request_id{request_id_} {} 28 | 29 | /** \brief an action when timer was triggered or cancelled */ 30 | virtual void trigger(bool cancelled) noexcept = 0; 31 | 32 | virtual ~timer_handler_base_t() = default; 33 | }; 34 | 35 | /** \brief alias for timer smart pointer */ 36 | using timer_handler_ptr_t = std::unique_ptr; 37 | 38 | /** \struct timer_handler_t 39 | * \brief templated implementation of timer handler 40 | * 41 | */ 42 | template struct timer_handler_t : timer_handler_base_t { 43 | /** \brief delegate object, i.e. that one on which the callback (method) will be invoked*/ 44 | Object *object; 45 | 46 | /** \brief timer callback */ 47 | Method method; 48 | 49 | /** \brief constructs timer handler from non-owning pointer to timer, timer request id, delegate and callback 50 | * (object method) */ 51 | timer_handler_t(actor_base_t *owner_, request_id_t request_id_, Object *object_, Method method_) noexcept 52 | : timer_handler_base_t{owner_, request_id_}, object{object_}, method{std::forward(method_)} {} 53 | 54 | void trigger(bool cancelled) noexcept override { std::invoke(method, object, request_id, cancelled); } 55 | }; 56 | 57 | } // namespace rotor 58 | -------------------------------------------------------------------------------- /include/rotor/wx.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | /** \file wx.hpp 10 | * A convenience header to include rotor support for wxWidgets 11 | */ 12 | 13 | #include "rotor/wx/supervisor_config_wx.h" 14 | #include "rotor/wx/supervisor_wx.h" 15 | #include "rotor/wx/system_context_wx.h" 16 | 17 | namespace rotor { 18 | 19 | /// namespace for wxWidgets adapters for `rotor` 20 | namespace wx {} 21 | 22 | } // namespace rotor 23 | -------------------------------------------------------------------------------- /include/rotor/wx/supervisor_config_wx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/supervisor_config.h" 10 | #include 11 | 12 | namespace rotor { 13 | namespace wx { 14 | 15 | /** \struct supervisor_config_wx_t 16 | * \brief wx supervisor config, which holds a pointer to the "context window". 17 | */ 18 | struct supervisor_config_wx_t : public supervisor_config_t { 19 | /** \brief the wx context, responsible for messages delivery 20 | * 21 | * Actual rotor-message delivery for actors running on the 22 | * top of wx-loop is performed via wx-events, i.e. rotor-messages 23 | * are **wrapped** into wx-events. 24 | * 25 | * The wx-supervisor subscribes to the `wx-events` and unwraps 26 | * the rotor-messages from the events. 27 | * 28 | * The wx event handler is used as a transport medium. 29 | * 30 | */ 31 | wxEvtHandler *handler; 32 | 33 | using supervisor_config_t::supervisor_config_t; 34 | }; 35 | 36 | /** \brief config builder for wx supervisor */ 37 | template struct supervisor_config_wx_builder_t : supervisor_config_builder_t { 38 | /** \brief final builder class */ 39 | using builder_t = typename Supervisor::template config_builder_t; 40 | 41 | /** \brief parent config builder */ 42 | using parent_t = supervisor_config_builder_t; 43 | using parent_t::parent_t; 44 | 45 | /** \brief bit mask for event handler validation */ 46 | constexpr static const std::uint32_t EVT_LOOP = 1 << 2; 47 | 48 | /** \brief bit mask for all required fields */ 49 | constexpr static const std::uint32_t requirements_mask = parent_t::requirements_mask | EVT_LOOP; 50 | 51 | /** \brief sets event handler */ 52 | builder_t &&handler(wxEvtHandler *handler_) && { 53 | parent_t::config.handler = handler_; 54 | parent_t::mask = (parent_t::mask & ~EVT_LOOP); 55 | return std::move(*static_cast(this)); 56 | } 57 | }; 58 | 59 | } // namespace wx 60 | } // namespace rotor 61 | -------------------------------------------------------------------------------- /include/rotor/wx/supervisor_wx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/wx/export.h" 10 | #include "rotor/supervisor.h" 11 | #include "rotor/wx/supervisor_config_wx.h" 12 | #include "rotor/wx/system_context_wx.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace rotor { 19 | namespace wx { 20 | 21 | /** \struct supervisor_wx_t 22 | * \brief delivers rotor-messages on top of wx event 23 | * 24 | * Basically wx event handler is used as transport for wx-rotor-events, 25 | * which wrap rotor-messages. 26 | * 27 | * Since wx-event loop is mainly GUI-loop, i.e. singleton, there are no 28 | * different execution contexts, i.e. only one (main) executing thread; 29 | * hence, there is no sense of having multiple supervisors. 30 | * 31 | * The wx-supervisor (and it's actors) role in `rotor` messaging is 32 | * to **abstract** destination endpoints from other non-wx (I/O) event 33 | * loops, i.e. show some information in corresponding field/window/frame/... 34 | * Hence, the receiving rotor-messages actors should be aware of your 35 | * wx-application system, i.e. hold refences to appropriate fields/windows 36 | * etc. 37 | * 38 | * The commands translator (i.e. from click on the button to the `rotor` 39 | * -message send) should be also performed on wx-specific actors. 40 | * 41 | */ 42 | struct ROTOR_WX_API supervisor_wx_t : public supervisor_t { 43 | 44 | /** \brief injects an alias for supervisor_config_wx_t */ 45 | using config_t = supervisor_config_wx_t; 46 | 47 | /** \brief injects templated supervisor_config_wx_builder_t */ 48 | template using config_builder_t = supervisor_config_wx_builder_t; 49 | 50 | /** \brief constructs new supervisor from supervisor config */ 51 | supervisor_wx_t(supervisor_config_wx_t &config); 52 | 53 | void start() noexcept override; 54 | void shutdown() noexcept override; 55 | void enqueue(message_ptr_t message) noexcept override; 56 | // void on_timer_trigger(request_id_t timer_id) noexcept override; 57 | 58 | /** \brief returns pointer to the wx system context */ 59 | inline system_context_wx_t *get_context() noexcept { return static_cast(context); } 60 | 61 | protected: 62 | /** \struct timer_t 63 | * \brief timer structure, adoped for wx-supervisor needs. 64 | */ 65 | struct ROTOR_WX_API timer_t : public wxTimer { 66 | /** \brief alias for intrusive pointer for the supervisor */ 67 | using supervisor_ptr_t = intrusive_ptr_t; 68 | 69 | /** \brief non-owning pointer to timer handler */ 70 | timer_handler_base_t *handler; 71 | 72 | /** \brief intrusive pointer to the supervisor */ 73 | supervisor_ptr_t sup; 74 | 75 | /** \brief constructs timer from wx supervisor */ 76 | timer_t(timer_handler_base_t *handler, supervisor_ptr_t &&sup_); 77 | 78 | /** \brief invokes `shutdown_timer_trigger` method if shutdown timer triggers*/ 79 | virtual void Notify() noexcept override; 80 | }; 81 | 82 | friend struct timer_t; 83 | 84 | void do_start_timer(const pt::time_duration &interval, timer_handler_base_t &handler) noexcept override; 85 | void do_cancel_timer(request_id_t timer_id) noexcept override; 86 | 87 | /** \brief unique pointer to timer */ 88 | using timer_ptr_t = std::unique_ptr; 89 | 90 | /** \brief timer id to timer pointer mapping type */ 91 | using timers_map_t = std::unordered_map; 92 | 93 | /** \brief non-owning pointer to the wx application (copied from config) */ 94 | wxEvtHandler *handler; 95 | 96 | /** \brief timer id to timer pointer mapping */ 97 | timers_map_t timers_map; 98 | }; 99 | 100 | } // namespace wx 101 | } // namespace rotor 102 | -------------------------------------------------------------------------------- /include/rotor/wx/system_context_wx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/arc.hpp" 10 | #include "rotor/wx/export.h" 11 | #include "rotor/wx/supervisor_config_wx.h" 12 | #include "rotor/system_context.h" 13 | #include 14 | 15 | namespace rotor { 16 | namespace wx { 17 | 18 | struct supervisor_wx_t; 19 | 20 | /** \brief intrusive pointer for wx supervisor */ 21 | using supervisor_ptr_t = intrusive_ptr_t; 22 | 23 | /** \struct system_context_wx_t 24 | * \brief The wxWidgets system context, which holds a pointer to wxApp and 25 | * root wx-supervisor 26 | * 27 | */ 28 | struct ROTOR_WX_API system_context_wx_t : public system_context_t { 29 | /** \brief intrusive pointer type for wx system context */ 30 | using ptr_t = rotor::intrusive_ptr_t; 31 | 32 | /** \brief construct the context from wx application 33 | * 34 | * If `app` is null, then the wx application is taken via `wxTheApp` macro 35 | * 36 | */ 37 | system_context_wx_t(wxAppConsole *app = nullptr); 38 | 39 | /** \brief returns wx application */ 40 | inline wxAppConsole *get_app() noexcept { return app; } 41 | 42 | protected: 43 | friend struct supervisor_wx_t; 44 | 45 | /** \brief non-owning pointer to the wx application */ 46 | wxAppConsole *app; 47 | }; 48 | 49 | /** \brief intrusive pointer type for wx system context */ 50 | using system_context_ptr_t = typename system_context_wx_t::ptr_t; 51 | 52 | } // namespace wx 53 | } // namespace rotor 54 | -------------------------------------------------------------------------------- /src/rotor/address_mapping.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/address_mapping.h" 8 | #include "rotor/handler.h" 9 | #include 10 | 11 | using namespace rotor; 12 | 13 | void address_mapping_t::set(actor_base_t &actor, const subscription_info_ptr_t &info) noexcept { 14 | auto &point_map = actor_map[static_cast(&actor)]; 15 | auto message_type = info->handler->message_type(); 16 | point_map.try_emplace(message_type, info); 17 | } 18 | 19 | address_ptr_t address_mapping_t::get_mapped_address(actor_base_t &actor, const void *message) noexcept { 20 | auto a_it = actor_map.find(static_cast(&actor)); 21 | if (a_it != actor_map.end()) { 22 | auto &point_map = a_it->second; 23 | auto p_it = point_map.find(message); 24 | if (p_it != point_map.end()) { 25 | return p_it->second->address; 26 | } 27 | } 28 | return {}; 29 | } 30 | 31 | void address_mapping_t::remove(const subscription_point_t &point) noexcept { 32 | auto &subs = actor_map.at(point.owner_ptr); 33 | for (auto it = subs.begin(); it != subs.end();) { 34 | auto &info = *it->second; 35 | if (info.handler.get() == point.handler.get() && info.address == point.address) { 36 | subs.erase(it); 37 | break; 38 | } else { 39 | ++it; 40 | } 41 | } 42 | if (subs.empty()) { 43 | actor_map.erase(point.owner_ptr); 44 | } 45 | } 46 | 47 | /* 48 | void address_mapping_t::clear(supervisor_t&) noexcept { 49 | assert(actor_map.size() == 1); 50 | actor_map.clear(); 51 | } 52 | */ 53 | 54 | bool address_mapping_t::has_subscriptions(const actor_base_t &actor) const noexcept { 55 | auto it = actor_map.find(static_cast(&actor)); 56 | return it != actor_map.end(); 57 | } 58 | -------------------------------------------------------------------------------- /src/rotor/detail/child_info.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/detail/child_info.h" 8 | #include "rotor/actor_base.h" 9 | 10 | using namespace rotor::detail; 11 | 12 | void child_info_t::spawn_attempt() noexcept { 13 | initialized = started = false; 14 | ++attempts; 15 | timer_id = 0; 16 | shutdown = shutdown_state_t::none; 17 | last_instantiation = clock_t::local_time(); 18 | if (max_attempts && attempts >= max_attempts) { 19 | active = false; 20 | } 21 | if (policy == restart_policy_t::never) { 22 | active = false; 23 | } 24 | } 25 | 26 | spawn_demand_t child_info_t::next_spawn(bool abnormal_shutdown) noexcept { 27 | if (timer_id) { 28 | return demand::no{}; 29 | } 30 | 31 | auto check_failure_escalation = [&]() -> bool { 32 | if (escalate_failure) { 33 | if (max_attempts && attempts >= max_attempts) { 34 | return true; 35 | } 36 | if (abnormal_shutdown) { 37 | if (policy == restart_policy_t::always && !active) { 38 | active = false; 39 | return true; 40 | } 41 | if (policy == restart_policy_t::normal_only) { 42 | active = false; 43 | return true; 44 | } 45 | } 46 | } 47 | return false; 48 | }; 49 | 50 | if (check_failure_escalation()) { 51 | return demand::escalate_failure{}; 52 | } 53 | 54 | if (!active) { 55 | return demand::no{}; 56 | } 57 | 58 | if (policy == restart_policy_t::fail_only && !abnormal_shutdown) { 59 | active = false; 60 | return demand::no{}; 61 | } 62 | 63 | if (policy == restart_policy_t::normal_only && abnormal_shutdown) { 64 | active = false; 65 | return demand::no{}; 66 | } 67 | 68 | if (policy == restart_policy_t::ask_actor) { 69 | auto need_restart = actor->should_restart(); 70 | if (!need_restart) { 71 | active = false; 72 | return demand::no{}; 73 | } 74 | } 75 | 76 | auto now = clock_t::local_time(); 77 | auto after = now - last_instantiation; 78 | if (after >= restart_period) { 79 | return demand::now{}; 80 | } 81 | return after; 82 | } 83 | -------------------------------------------------------------------------------- /src/rotor/error_code.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/error_code.h" 8 | 9 | namespace rotor::details { 10 | 11 | const char *error_code_category::name() const noexcept { return "rotor_error"; } 12 | const char *shutdown_code_category::name() const noexcept { return "rotor_shutdown"; } 13 | 14 | std::string error_code_category::message(int c) const { 15 | switch (static_cast(c)) { 16 | case error_code_t::success: 17 | return "success"; 18 | case error_code_t::cancelled: 19 | return "request has been cancelled"; 20 | case error_code_t::request_timeout: 21 | return "request timeout"; 22 | case error_code_t::supervisor_defined: 23 | return "supervisor is already defined"; 24 | case error_code_t::already_registered: 25 | return "service name is already registered"; 26 | case error_code_t::actor_misconfigured: 27 | return "actor is misconfigured"; 28 | case error_code_t::actor_not_linkable: 29 | return "actor is not linkeable"; 30 | case error_code_t::already_linked: 31 | return "already linked"; 32 | case error_code_t::failure_escalation: 33 | return "failure escalation"; 34 | case error_code_t::unknown_service: 35 | return "the requested service name is not registered"; 36 | case error_code_t::discovery_failed: 37 | return "discovery has been failed"; 38 | case error_code_t::registration_failed: 39 | return "registration has been failed"; 40 | } 41 | return "unknown"; 42 | } 43 | 44 | std::string shutdown_code_category::message(int c) const { 45 | switch (static_cast(c)) { 46 | case shutdown_code_t::normal: 47 | return "normal shutdown"; 48 | case shutdown_code_t::supervisor_shutdown: 49 | return "actor shutdown has been requested by supervisor"; 50 | case shutdown_code_t::child_init_failed: 51 | return "supervisor shutdown due to child init failure"; 52 | case shutdown_code_t::child_down: 53 | return "supervisor shutdown due to child shutdown policy"; 54 | case shutdown_code_t::init_failed: 55 | return "actor shutdown due to init failure"; 56 | case shutdown_code_t::unlink_requested: 57 | return "actor shutdown due to unlink request"; 58 | } 59 | return "unknown shutdown reason"; 60 | } 61 | 62 | } // namespace rotor::details 63 | 64 | namespace rotor { 65 | 66 | const static details::error_code_category error_category; 67 | const static details::shutdown_code_category shutdown_category; 68 | const details::error_code_category &error_code_category() { return error_category; } 69 | const details::shutdown_code_category &shutdown_code_category() { return shutdown_category; } 70 | 71 | } // namespace rotor 72 | -------------------------------------------------------------------------------- /src/rotor/ev/system_context_ev.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/ev/system_context_ev.h" 8 | #include "rotor/ev/supervisor_ev.h" 9 | 10 | using namespace rotor::ev; 11 | -------------------------------------------------------------------------------- /src/rotor/extended_error.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/extended_error.h" 8 | #include "rotor/message_stringifier.h" 9 | #include 10 | 11 | namespace rotor { 12 | 13 | std::string extended_error_t::message(const message_stringifier_t *stringifier) const noexcept { 14 | std::stringstream out; 15 | out << context << " " << ec.message(); 16 | if (request && stringifier) { 17 | out << " ["; 18 | stringifier->stringify_to(out, *request); 19 | out << "]"; 20 | } 21 | if (next) { 22 | out << " <- " << next->message(); 23 | } 24 | return out.str(); 25 | } 26 | 27 | extended_error_ptr_t extended_error_t::root() const noexcept { 28 | if (!next) { 29 | auto self = const_cast(this); 30 | return extended_error_ptr_t(self); 31 | } 32 | auto n = next; 33 | while (n->next) { 34 | n = n->next; 35 | } 36 | return n; 37 | } 38 | 39 | extended_error_ptr_t make_error(const std::string &context_, const std::error_code &ec_, 40 | const extended_error_ptr_t &next_, const message_ptr_t &request_) noexcept { 41 | return new extended_error_t(context_, ec_, next_, request_); 42 | } 43 | 44 | } // namespace rotor 45 | -------------------------------------------------------------------------------- /src/rotor/fltk/supervisor_fltk.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/fltk/supervisor_fltk.h" 8 | #include "rotor/fltk/system_context_fltk.h" 9 | 10 | #include 11 | 12 | using namespace rotor; 13 | using namespace rotor::fltk; 14 | 15 | namespace { 16 | namespace to { 17 | struct on_timer_trigger {}; 18 | struct timers_map {}; 19 | } // namespace to 20 | } // namespace 21 | 22 | namespace rotor { 23 | template <> 24 | inline auto rotor::actor_base_t::access(request_id_t request_id, 25 | bool cancelled) noexcept { 26 | on_timer_trigger(request_id, cancelled); 27 | } 28 | 29 | template <> inline auto &supervisor_fltk_t::access() noexcept { return timers_map; } 30 | } // namespace rotor 31 | 32 | static void on_timeout(void *data) noexcept { 33 | auto timer = reinterpret_cast(data); 34 | auto *sup = timer->sup.get(); 35 | 36 | auto timer_id = timer->handler->request_id; 37 | auto &timers_map = sup->access(); 38 | 39 | try { 40 | auto actor_ptr = timers_map.at(timer_id)->handler->owner; 41 | actor_ptr->access(timer_id, false); 42 | timers_map.erase(timer_id); 43 | Fl::awake(); 44 | // sup->do_process(); 45 | } catch (std::out_of_range &) { 46 | // no-op 47 | } 48 | intrusive_ptr_release(sup); 49 | } 50 | 51 | supervisor_fltk_t::supervisor_fltk_t(supervisor_config_fltk_t &config) : supervisor_t(config) {} 52 | 53 | supervisor_fltk_t::timer_t::timer_t(supervisor_ptr_t supervisor_, timer_handler_base_t *handler_) 54 | : sup(std::move(supervisor_)), handler{handler_} {} 55 | 56 | void supervisor_fltk_t::do_start_timer(const pt::time_duration &interval, timer_handler_base_t &handler) noexcept { 57 | auto self = timer_t::supervisor_ptr_t(this); 58 | auto timer = std::make_unique(std::move(self), &handler); 59 | auto seconds = interval.total_nanoseconds() / 1000000000.; 60 | Fl::add_timeout(seconds, on_timeout, timer.get()); 61 | timers_map.emplace(handler.request_id, std::move(timer)); 62 | intrusive_ptr_add_ref(this); 63 | } 64 | 65 | void supervisor_fltk_t::do_cancel_timer(request_id_t timer_id) noexcept { 66 | try { 67 | auto &timer = timers_map.at(timer_id); 68 | Fl::remove_timeout(on_timeout, timer.get()); 69 | auto actor_ptr = timer->handler->owner; 70 | actor_ptr->access(timer_id, true); 71 | timers_map.erase(timer_id); 72 | intrusive_ptr_release(this); 73 | } catch (std::out_of_range &) { 74 | // no-op 75 | } 76 | } 77 | 78 | void supervisor_fltk_t::enqueue(message_ptr_t message) noexcept { 79 | static_cast(context)->enqueue(std::move(message)); 80 | } 81 | 82 | void supervisor_fltk_t::start() noexcept { 83 | intrusive_ptr_add_ref(this); 84 | Fl::awake( 85 | [](void *data) { 86 | auto supersisor = reinterpret_cast(data); 87 | supersisor->do_process(); 88 | intrusive_ptr_release(supersisor); 89 | }, 90 | this); 91 | } 92 | 93 | void supervisor_fltk_t::shutdown() noexcept { 94 | intrusive_ptr_add_ref(this); 95 | Fl::awake( 96 | [](void *data) { 97 | auto supersisor = reinterpret_cast(data); 98 | supersisor->do_shutdown(); 99 | supersisor->do_process(); 100 | intrusive_ptr_release(supersisor); 101 | }, 102 | this); 103 | } 104 | -------------------------------------------------------------------------------- /src/rotor/fltk/system_context_fltk.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2025 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/fltk/system_context_fltk.h" 8 | #include "rotor/supervisor.h" 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | using namespace rotor; 15 | using namespace rotor::fltk; 16 | 17 | namespace { 18 | namespace to { 19 | struct inbound_queue {}; 20 | struct queue {}; 21 | } // namespace to 22 | } // namespace 23 | 24 | template <> inline auto &rotor::supervisor_t::access() noexcept { return inbound_queue; } 25 | template <> inline auto &rotor::supervisor_t::access() noexcept { return queue; } 26 | 27 | static std::atomic_int32_t queue_counter{0}; 28 | 29 | static void _callback(void *data) { 30 | auto sup_raw = static_cast(data); 31 | auto &inbound = sup_raw->access(); 32 | auto &queue = sup_raw->access(); 33 | message_base_t *ptr; 34 | bool try_fetch = true; 35 | while (try_fetch) { 36 | std::int32_t fetched = 0; 37 | try_fetch = false; 38 | while (inbound.pop(ptr)) { 39 | queue.emplace_back(ptr, false); 40 | ++fetched; 41 | } 42 | if (fetched) { 43 | sup_raw->do_process(); 44 | try_fetch = queue_counter.fetch_sub(fetched) != fetched; 45 | } 46 | } 47 | } 48 | 49 | void system_context_fltk_t::enqueue(message_ptr_t message) noexcept { 50 | auto sup = get_supervisor().get(); 51 | if (sup) { 52 | auto &inbound = sup->access(); 53 | inbound.push(message.detach()); 54 | if (queue_counter.fetch_add(1) == 0) { 55 | Fl::awake(_callback, sup); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/rotor/handler.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/supervisor.h" 8 | 9 | using namespace rotor; 10 | 11 | namespace { 12 | namespace to { 13 | struct intercept {}; 14 | } // namespace to 15 | } // namespace 16 | 17 | template <> 18 | inline auto rotor::supervisor_t::access( 19 | message_ptr_t &msg, const void *tag, const continuation_t &continuation) noexcept { 20 | return intercept(msg, tag, continuation); 21 | } 22 | 23 | struct continuation_impl_t final : continuation_t { 24 | continuation_impl_t(handler_intercepted_t &handler_, message_ptr_t &message_) noexcept 25 | : handler{handler_}, message{message_} {} 26 | 27 | void operator()() const noexcept override { handler.call_no_check(message); } 28 | 29 | private: 30 | handler_intercepted_t &handler; 31 | message_ptr_t &message; 32 | }; 33 | 34 | handler_base_t::handler_base_t(actor_base_t &actor, const void *handler_type_) noexcept 35 | : handler_type{handler_type_}, actor_ptr{&actor} { 36 | auto h1 = reinterpret_cast(handler_type); 37 | auto h2 = reinterpret_cast(&actor); 38 | precalc_hash = h1 ^ (h2 << 1); 39 | intrusive_ptr_add_ref(actor_ptr); 40 | } 41 | 42 | handler_base_t::~handler_base_t() { intrusive_ptr_release(actor_ptr); } 43 | 44 | handler_ptr_t handler_base_t::upgrade(const void *tag) noexcept { 45 | handler_ptr_t self(this); 46 | return handler_ptr_t(new handler_intercepted_t(self, tag)); 47 | } 48 | 49 | handler_intercepted_t::handler_intercepted_t(handler_ptr_t backend_, const void *tag_) noexcept 50 | : handler_base_t(*backend_->actor_ptr, backend_->handler_type), backend{std::move(backend_)}, tag{tag_} {} 51 | 52 | void handler_intercepted_t::call(message_ptr_t &message) noexcept { 53 | if (select(message)) { 54 | auto &sup = actor_ptr->get_supervisor(); 55 | continuation_impl_t continuation(*this, message); 56 | sup.access(message, tag, continuation); 57 | } 58 | } 59 | 60 | bool handler_intercepted_t::select(message_ptr_t &message) noexcept { return backend->select(message); } 61 | 62 | void handler_intercepted_t::call_no_check(message_ptr_t &message) noexcept { return backend->call_no_check(message); } 63 | 64 | handler_ptr_t handler_intercepted_t::upgrade(const void *tag_) noexcept { 65 | if (tag_ == tag) { 66 | return handler_ptr_t(this); 67 | } 68 | return handler_base_t::upgrade(tag_); 69 | } 70 | 71 | const void *handler_intercepted_t::message_type() const noexcept { return backend->message_type(); } 72 | -------------------------------------------------------------------------------- /src/rotor/message.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/message.h" 8 | #include 9 | 10 | using type_map_t = std::unordered_map; 11 | 12 | namespace rotor::message_support { 13 | 14 | const void *register_type(const std::type_index &type_index) noexcept { 15 | static type_map_t type_map = {}; 16 | 17 | auto name = std::string_view(type_index.name()); 18 | auto it = type_map.find(name); 19 | if (it != type_map.end()) { 20 | return it->second; 21 | } 22 | auto ptr = static_cast(type_index.name()); 23 | type_map[name] = ptr; 24 | return ptr; 25 | } 26 | 27 | } // namespace rotor::message_support 28 | -------------------------------------------------------------------------------- /src/rotor/message_stringifier.cpp: -------------------------------------------------------------------------------- 1 | #include "rotor/message_stringifier.h" 2 | 3 | #include 4 | 5 | using namespace rotor; 6 | 7 | std::string message_stringifier_t::stringify(const message_base_t &message) const { 8 | std::stringstream stream; 9 | stringify_to(stream, message); 10 | return std::move(stream).str(); 11 | } 12 | -------------------------------------------------------------------------------- /src/rotor/messages.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/messages.hpp" 8 | #include "rotor/handler.h" 9 | #include "rotor/supervisor.h" 10 | 11 | namespace rotor::payload { 12 | 13 | handler_call_t::~handler_call_t() { 14 | auto ptr = orig_message.get(); 15 | if (ptr && ptr->next_route && ptr->use_count() == 1) { 16 | ptr->address = std::move(ptr->next_route); 17 | auto &sup = handler->actor_ptr->get_supervisor(); 18 | sup.enqueue(std::move(orig_message)); 19 | } 20 | } 21 | 22 | } // namespace rotor::payload 23 | -------------------------------------------------------------------------------- /src/rotor/plugin/address_maker.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/plugin/address_maker.h" 8 | #include "rotor/actor_base.h" 9 | #include "rotor/supervisor.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace rotor; 18 | using namespace rotor::plugin; 19 | 20 | namespace { 21 | namespace to { 22 | struct address_maker {}; 23 | struct address {}; 24 | struct identity {}; 25 | } // namespace to 26 | } // namespace 27 | 28 | template <> auto &actor_base_t::access() noexcept { return address_maker; } 29 | template <> auto &actor_base_t::access() noexcept { return address; } 30 | template <> auto &actor_base_t::access() noexcept { return identity; } 31 | 32 | const std::type_index address_maker_plugin_t::class_identity = typeid(address_maker_plugin_t); 33 | 34 | const std::type_index &address_maker_plugin_t::identity() const noexcept { return class_identity; } 35 | 36 | void address_maker_plugin_t::activate(actor_base_t *actor_) noexcept { 37 | actor = actor_; 38 | 39 | if (!actor->get_address()) { 40 | actor->access() = create_address(); 41 | } 42 | actor->access() = this; 43 | plugin_base_t::activate(actor_); 44 | actor_->configure(*this); 45 | if (actor->access().empty()) { 46 | generate_identity(); 47 | } 48 | actor->init_start(); 49 | } 50 | 51 | void address_maker_plugin_t::deactivate() noexcept { 52 | actor->access() = nullptr; 53 | return plugin_base_t::deactivate(); 54 | } 55 | 56 | address_ptr_t address_maker_plugin_t::create_address() noexcept { return actor->get_supervisor().make_address(); } 57 | 58 | void address_maker_plugin_t::set_identity(std::string_view name, bool append_addr) noexcept { 59 | if (!append_addr) { 60 | actor->access() = name; 61 | return; 62 | } 63 | std::stringstream out; 64 | auto &addr = actor->access(); 65 | out << name << " "; 66 | out << std::hex << (const void *)addr.get(); 67 | actor->access() = out.str(); 68 | } 69 | 70 | void address_maker_plugin_t::generate_identity() noexcept { 71 | set_identity(dynamic_cast(actor) ? "supervisor" : "actor"); 72 | } 73 | -------------------------------------------------------------------------------- /src/rotor/plugin/foreigners_support.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/plugin/foreigners_support.h" 8 | #include "rotor/supervisor.h" 9 | 10 | using namespace rotor; 11 | using namespace rotor::plugin; 12 | 13 | namespace { 14 | namespace to { 15 | struct lifetime {}; 16 | struct points {}; 17 | struct state {}; 18 | struct alive_actors {}; 19 | } // namespace to 20 | } // namespace 21 | 22 | template <> auto &supervisor_t::access() noexcept { return alive_actors; } 23 | template <> auto &actor_base_t::access() noexcept { return lifetime; } 24 | template <> auto &lifetime_plugin_t::access() noexcept { return points; } 25 | template <> auto &actor_base_t::access() noexcept { return state; } 26 | 27 | const std::type_index foreigners_support_plugin_t::class_identity = typeid(foreigners_support_plugin_t); 28 | 29 | const std::type_index &foreigners_support_plugin_t::identity() const noexcept { return class_identity; } 30 | 31 | void foreigners_support_plugin_t::activate(actor_base_t *actor_) noexcept { 32 | actor = actor_; 33 | 34 | subscribe(&foreigners_support_plugin_t::on_call); 35 | subscribe(&foreigners_support_plugin_t::on_unsubscription); 36 | subscribe(&foreigners_support_plugin_t::on_subscription_external); 37 | 38 | return plugin_base_t::activate(actor_); 39 | } 40 | 41 | void foreigners_support_plugin_t::deactivate() noexcept { 42 | auto lifetime = actor->access(); 43 | for (auto &info : foreign_points) { 44 | lifetime->unsubscribe(info); 45 | } 46 | return plugin_base_t::deactivate(); 47 | } 48 | 49 | void foreigners_support_plugin_t::on_call(message::handler_call_t &message) noexcept { 50 | auto &handler = message.payload.handler; 51 | auto child_actor = handler->actor_ptr; 52 | // need to check, that 53 | // 1. children exists 54 | // 2. check it's state 55 | // 2. it is still subscribed to the message 56 | 57 | auto &sup = static_cast(*actor); 58 | if (sup.access().count(child_actor)) { 59 | if (child_actor->access() < state_t::SHUT_DOWN) { 60 | auto &orig_message = message.payload.orig_message; 61 | auto point = subscription_point_t(handler, orig_message->address); 62 | auto lifetime = child_actor->access(); 63 | if (lifetime) { 64 | auto &points = lifetime->access(); 65 | if (points.find(point) != points.end()) { 66 | handler->call(orig_message); 67 | } 68 | } 69 | } 70 | } 71 | } 72 | 73 | void foreigners_support_plugin_t::on_subscription_external(message::external_subscription_t &message) noexcept { 74 | auto &sup = static_cast(*actor); 75 | auto &point = message.payload.point; 76 | assert(&point.address->supervisor == &sup); 77 | auto info = sup.subscribe(point.handler, point.address, point.owner_ptr, owner_tag_t::FOREIGN); 78 | foreign_points.emplace_back(info); 79 | } 80 | 81 | void foreigners_support_plugin_t::on_unsubscription(message::commit_unsubscription_t &message) noexcept { 82 | auto &sup = static_cast(*actor); 83 | auto &point = message.payload.point; 84 | 85 | auto it = foreign_points.find(point); 86 | auto &info = *it; 87 | 88 | sup.commit_unsubscription(info); 89 | foreign_points.erase(it); 90 | 91 | if (foreign_points.empty() && actor->access() == state_t::SHUTTING_DOWN) { 92 | plugin_base_t::deactivate(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/rotor/plugin/init_shutdown.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/plugin/init_shutdown.h" 8 | #include "rotor/supervisor.h" 9 | 10 | using namespace rotor; 11 | using namespace rotor::plugin; 12 | 13 | namespace { 14 | namespace to { 15 | struct state {}; 16 | struct init_request {}; 17 | struct shutdown_request {}; 18 | struct assign_shutdown_reason {}; 19 | } // namespace to 20 | } // namespace 21 | 22 | template <> auto &actor_base_t::access() noexcept { return state; } 23 | template <> auto &actor_base_t::access() noexcept { return init_request; } 24 | template <> auto &actor_base_t::access() noexcept { return shutdown_request; } 25 | 26 | template <> 27 | auto actor_base_t::access( 28 | const extended_error_ptr_t &reason) noexcept { 29 | return assign_shutdown_reason(reason); 30 | } 31 | 32 | const std::type_index init_shutdown_plugin_t::class_identity = typeid(init_shutdown_plugin_t); 33 | 34 | const std::type_index &init_shutdown_plugin_t::identity() const noexcept { return class_identity; } 35 | 36 | void init_shutdown_plugin_t::activate(actor_base_t *actor_) noexcept { 37 | actor = actor_; 38 | subscribe(&init_shutdown_plugin_t::on_init); 39 | subscribe(&init_shutdown_plugin_t::on_shutdown); 40 | 41 | reaction_on(reaction_t::INIT); 42 | reaction_on(reaction_t::SHUTDOWN); 43 | } 44 | 45 | void init_shutdown_plugin_t::on_init(message::init_request_t &msg) noexcept { 46 | /* it could be shutdown, then ignore it */ 47 | actor->access().reset(&msg); 48 | if (actor->access() == state_t::INITIALIZING) { 49 | plugin_base_t::activate(actor); 50 | actor->init_continue(); 51 | } 52 | } 53 | 54 | void init_shutdown_plugin_t::on_shutdown(message::shutdown_request_t &msg) noexcept { 55 | assert((actor->access() != state_t::SHUTTING_DOWN) || 56 | (actor->access() != state_t::SHUT_DOWN)); 57 | // std::cout << "received shutdown_request for " << actor->address.get() << " from " << msg.payload.reply_to.get() 58 | // << "\n"; 59 | auto &reason = msg.payload.request_payload.reason; 60 | actor->access().reset(&msg); 61 | actor->access(reason); 62 | actor->shutdown_start(); 63 | actor->shutdown_continue(); 64 | } 65 | 66 | bool init_shutdown_plugin_t::handle_init(message::init_request_t *) noexcept { 67 | return bool(actor->access()); 68 | } 69 | 70 | bool init_shutdown_plugin_t::handle_shutdown(message::shutdown_request_t *req) noexcept { 71 | actor->deactivate_plugins(); 72 | return plugin_base_t::handle_shutdown(req); 73 | } 74 | -------------------------------------------------------------------------------- /src/rotor/plugin/locality.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/plugin/locality.h" 8 | #include "rotor/supervisor.h" 9 | 10 | using namespace rotor; 11 | using namespace rotor::plugin; 12 | 13 | namespace { 14 | namespace to { 15 | struct parent {}; 16 | struct locality_leader {}; 17 | struct inbound_queue {}; 18 | struct inbound_queue_size {}; 19 | } // namespace to 20 | } // namespace 21 | 22 | template <> auto &supervisor_t::access() noexcept { return parent; } 23 | template <> auto &supervisor_t::access() noexcept { return locality_leader; } 24 | template <> auto &supervisor_t::access() noexcept { return inbound_queue; } 25 | template <> auto &supervisor_t::access() noexcept { return inbound_queue_size; } 26 | 27 | const std::type_index locality_plugin_t::class_identity = typeid(locality_plugin_t); 28 | 29 | const std::type_index &locality_plugin_t::identity() const noexcept { return class_identity; } 30 | 31 | void locality_plugin_t::activate(actor_base_t *actor_) noexcept { 32 | auto &address = actor_->get_address(); 33 | auto &sup = static_cast(*actor_); 34 | auto &parent = sup.access(); 35 | bool use_other = parent && static_cast(parent)->get_address()->same_locality(*address); 36 | auto locality_leader = use_other ? parent->access() : ⊃ 37 | sup.access() = locality_leader; 38 | if (!use_other) { 39 | auto sz = sup.access(); 40 | sup.access().reserve_unsafe(sz); 41 | } 42 | return plugin_base_t::activate(actor_); 43 | } 44 | -------------------------------------------------------------------------------- /src/rotor/plugin/resources.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/plugin/resources.h" 8 | #include "rotor/supervisor.h" 9 | #include 10 | 11 | using namespace rotor; 12 | using namespace rotor::plugin; 13 | 14 | namespace { 15 | namespace to { 16 | struct state {}; 17 | struct resources {}; 18 | struct continuation_mask {}; 19 | } // namespace to 20 | } // namespace 21 | 22 | template <> auto &actor_base_t::access() noexcept { return state; } 23 | template <> auto &actor_base_t::access() noexcept { return resources; } 24 | template <> auto &actor_base_t::access() noexcept { return continuation_mask; } 25 | 26 | const std::type_index resources_plugin_t::class_identity = typeid(resources_plugin_t); 27 | 28 | const std::type_index &resources_plugin_t::identity() const noexcept { return class_identity; } 29 | 30 | void resources_plugin_t::activate(actor_base_t *actor_) noexcept { 31 | actor = actor_; 32 | 33 | reaction_on(reaction_t::INIT); 34 | reaction_on(reaction_t::SHUTDOWN); 35 | 36 | actor->access() = this; 37 | actor->configure(*this); 38 | 39 | return plugin_base_t::activate(actor_); 40 | } 41 | 42 | bool resources_plugin_t::handle_init(message::init_request_t *) noexcept { 43 | if (!configured) { 44 | configured = true; 45 | actor->configure(*this); 46 | } 47 | return !has_any(); 48 | } 49 | 50 | bool resources_plugin_t::handle_shutdown(message::shutdown_request_t *req) noexcept { 51 | return !has_any() && plugin_base_t::handle_shutdown(req); 52 | } 53 | 54 | void resources_plugin_t::acquire(resource_id_t id) noexcept { 55 | if (id >= resources.size()) { 56 | resources.resize(id + 1); 57 | } 58 | ++resources[id]; 59 | auto state = actor->access(); 60 | if (state == state_t::INITIALIZING) { 61 | reaction_on(reaction_t::INIT); 62 | } else if (state >= state_t::OPERATIONAL) { 63 | reaction_on(reaction_t::SHUTDOWN); 64 | } 65 | } 66 | 67 | bool resources_plugin_t::release(resource_id_t id) noexcept { 68 | assert(id < resources.size() && "out of bound resource release"); 69 | auto &value = resources[id]; 70 | assert(value > 0 && "release should be previously acquired before releasing"); 71 | --value; 72 | auto state = actor->access(); 73 | bool has_acquired = has_any(); 74 | if (state == state_t::INITIALIZING) { 75 | if (!has_acquired && !(actor->access() & actor_base_t::PROGRESS_INIT)) { 76 | actor->init_continue(); 77 | } 78 | } else if (state == state_t::SHUTTING_DOWN) { 79 | if (!has_acquired && !(actor->access() & actor_base_t::PROGRESS_SHUTDOWN)) { 80 | actor->shutdown_continue(); 81 | } 82 | } 83 | return has_acquired; 84 | } 85 | 86 | std::uint32_t resources_plugin_t::has(resource_id_t id) noexcept { 87 | if (id >= resources.size()) 88 | return 0; 89 | return resources[id]; 90 | } 91 | 92 | bool resources_plugin_t::has_any() noexcept { 93 | auto sum = std::accumulate(resources.begin(), resources.end(), 0); 94 | return sum > 0; 95 | } 96 | -------------------------------------------------------------------------------- /src/rotor/plugin/starter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2023 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/plugin/starter.h" 8 | #include "rotor/supervisor.h" 9 | 10 | using namespace rotor; 11 | using namespace rotor::plugin; 12 | 13 | namespace { 14 | namespace to { 15 | struct state {}; 16 | struct plugins {}; 17 | } // namespace to 18 | } // namespace 19 | 20 | template <> auto &actor_base_t::access() noexcept { return state; } 21 | template <> auto &actor_base_t::access() noexcept { return plugins; } 22 | 23 | const std::type_index starter_plugin_t::class_identity = typeid(starter_plugin_t); 24 | 25 | const std::type_index &starter_plugin_t::identity() const noexcept { return class_identity; } 26 | 27 | void starter_plugin_t::activate(actor_base_t *actor_) noexcept { 28 | actor = actor_; 29 | actor_->configure(*this); 30 | plugin_base_t::activate(actor_); 31 | reaction_on(reaction_t::INIT); 32 | reaction_on(reaction_t::SUBSCRIPTION); 33 | reaction_on(reaction_t::START); 34 | subscribe(&starter_plugin_t::on_start); 35 | } 36 | 37 | void starter_plugin_t::deactivate() noexcept { 38 | tracked.clear(); 39 | return plugin_base_t::deactivate(); 40 | } 41 | 42 | bool starter_plugin_t::handle_subscription(message::subscription_t &message) noexcept { 43 | auto &point = message.payload.point; 44 | auto it = std::find_if(tracked.begin(), tracked.end(), [&](auto info) { return *info == point; }); 45 | if (it != tracked.end()) { 46 | tracked.erase(it); 47 | } 48 | if (configured && tracked.empty() && actor->access() == state_t::INITIALIZING) { 49 | actor->init_continue(); 50 | return true; 51 | } 52 | return plugin_base_t::handle_subscription(message); 53 | } 54 | 55 | bool starter_plugin_t::handle_init(message::init_request_t *message) noexcept { 56 | if (!configured) { 57 | configured = true; 58 | actor->configure(*this); 59 | if (tracked.empty()) { 60 | reaction_off(reaction_t::INIT); 61 | reaction_off(reaction_t::SUBSCRIPTION); 62 | } 63 | } 64 | return tracked.empty() && message; 65 | } 66 | 67 | void starter_plugin_t::handle_start(message::start_trigger_t *trigger) noexcept { 68 | actor->on_start(); 69 | return plugin_base_t::handle_start(trigger); 70 | } 71 | 72 | void starter_plugin_t::on_start(message::start_trigger_t &message) noexcept { 73 | // ignore, e.g. if we are shutting down 74 | if (actor->access() != state_t::INITIALIZED) { 75 | return; 76 | } 77 | 78 | auto &plugins = actor->access(); 79 | for (auto rit = plugins.rbegin(); rit != plugins.rend(); ++rit) { 80 | auto plugin = *rit; 81 | if (plugin->get_reaction() & plugin_base_t::START) { 82 | plugin->handle_start(&message); 83 | plugin->reaction_off(plugin_base_t::START); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/rotor/spawner.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/spawner.h" 8 | #include "rotor/supervisor.h" 9 | #include 10 | 11 | using namespace rotor; 12 | 13 | namespace { 14 | namespace to { 15 | struct manager {}; 16 | } // namespace to 17 | } // namespace 18 | 19 | template <> auto &supervisor_t::access() noexcept { return manager; } 20 | 21 | spawner_t::spawner_t(factory_t factory_, supervisor_t &supervisor_) noexcept 22 | : factory{std::move(factory_)}, supervisor{supervisor_} {} 23 | 24 | spawner_t::~spawner_t() { spawn(); } 25 | 26 | spawner_t &&spawner_t::restart_period(const pt::time_duration &period_) noexcept { 27 | period = period_; 28 | return std::move(*this); 29 | } 30 | 31 | spawner_t &&spawner_t::restart_policy(restart_policy_t policy_) noexcept { 32 | policy = policy_; 33 | return std::move(*this); 34 | } 35 | 36 | spawner_t &&spawner_t::max_attempts(size_t attempts_) noexcept { 37 | attempts = attempts_; 38 | return std::move(*this); 39 | } 40 | 41 | spawner_t &&spawner_t::escalate_failure(bool value) noexcept { 42 | escalate = value; 43 | return std::move(*this); 44 | } 45 | 46 | void spawner_t::spawn() noexcept { 47 | if (!done) { 48 | done = true; 49 | auto manager = supervisor.access(); 50 | manager->spawn(std::move(factory), period, policy, attempts, escalate); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/rotor/subscription_point.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/subscription.h" 8 | #include "rotor/supervisor.h" 9 | 10 | namespace rotor { 11 | 12 | namespace to { 13 | struct subscription_map {}; 14 | } // namespace to 15 | 16 | template <> auto &supervisor_t::access() noexcept { return subscription_map; } 17 | 18 | namespace tags { 19 | 20 | const void *io = &io; 21 | 22 | } 23 | 24 | bool subscription_point_t::operator==(const subscription_point_t &other) const { 25 | return address == other.address && (*handler == *other.handler); 26 | } 27 | 28 | subscription_container_t::iterator subscription_container_t::find(const subscription_point_t &point) noexcept { 29 | auto predicate = [&point](auto &info) { 30 | return *info->handler == *point.handler && info->address == point.address; 31 | }; 32 | auto rit = std::find_if(rbegin(), rend(), predicate); 33 | if (rit == rend()) { 34 | return end(); 35 | } 36 | return --rit.base(); 37 | } 38 | 39 | void subscription_info_t::tag(const void *t) noexcept { 40 | auto new_handler = handler->upgrade(t); 41 | auto &sup = handler->actor_ptr->get_supervisor(); 42 | auto &sm = sup.access(); 43 | sm.update(*this, new_handler); 44 | } 45 | 46 | } // namespace rotor 47 | -------------------------------------------------------------------------------- /src/rotor/system_context.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "rotor/supervisor.h" 12 | #include "rotor/system_context.h" 13 | #include "rotor/misc/default_stringifier.h" 14 | 15 | namespace rotor { 16 | 17 | namespace { 18 | namespace to { 19 | struct inbound_queue {}; 20 | } // namespace to 21 | } // namespace 22 | 23 | template <> auto &supervisor_t::access() noexcept { return inbound_queue; } 24 | } // namespace rotor 25 | 26 | using namespace rotor; 27 | 28 | void system_context_t::on_error(actor_base_t *actor, const extended_error_ptr_t &ec) noexcept { 29 | std::cerr << "fatal error "; 30 | if (actor) { 31 | std::cerr << actor->get_identity(); 32 | } 33 | std::cerr << ": " << ec->message() << "\n"; 34 | std::terminate(); 35 | } 36 | 37 | std::string system_context_t::identity() noexcept { 38 | std::stringstream out; 39 | out << std::hex << (const void *)this; 40 | return out.str(); 41 | } 42 | 43 | system_context_t::~system_context_t() { 44 | if (supervisor) { 45 | message_base_t *ptr; 46 | while (supervisor->access().pop(ptr)) { 47 | intrusive_ptr_release(ptr); 48 | } 49 | } 50 | } 51 | 52 | auto system_context_t::get_stringifier() -> const message_stringifier_t & { 53 | if (!stringifier) { 54 | stringifier = make_stringifier(); 55 | } 56 | return *stringifier; 57 | } 58 | 59 | auto system_context_t::make_stringifier() const noexcept -> message_stringifier_ptr_t { 60 | return message_stringifier_ptr_t(new misc::default_stringifier_t()); 61 | } 62 | -------------------------------------------------------------------------------- /src/rotor/thread/supervisor_thread.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/thread/supervisor_thread.h" 8 | #include "rotor/thread/system_context_thread.h" 9 | 10 | using namespace rotor; 11 | using namespace rotor::thread; 12 | 13 | void supervisor_thread_t::start() noexcept { 14 | // no-op 15 | } 16 | 17 | void supervisor_thread_t::shutdown() noexcept { 18 | auto &sup_addr = supervisor->get_address(); 19 | auto ec = make_error_code(shutdown_code_t::normal); 20 | auto reason = make_error(ec); 21 | auto msg = make_message(sup_addr, address, reason); 22 | supervisor->enqueue(msg); 23 | } 24 | 25 | void supervisor_thread_t::enqueue(message_ptr_t message) noexcept { 26 | auto ctx = static_cast(context); 27 | inbound_queue.push(message.detach()); 28 | std::lock_guard lock(ctx->mutex); 29 | ctx->cv.notify_one(); 30 | } 31 | 32 | void supervisor_thread_t::intercept(message_ptr_t &message, const void *tag, 33 | const continuation_t &continuation) noexcept { 34 | auto ctx = static_cast(context); 35 | if (tag == rotor::tags::io) { 36 | ctx->intercepting = true; 37 | ctx->check(); 38 | supervisor_t::intercept(message, tag, continuation); 39 | ctx->intercepting = false; 40 | ctx->check(); 41 | } else { 42 | supervisor_t::intercept(message, tag, continuation); 43 | } 44 | } 45 | 46 | void supervisor_thread_t::do_start_timer(const pt::time_duration &interval, timer_handler_base_t &handler) noexcept { 47 | auto ctx = static_cast(context); 48 | ctx->start_timer(interval, handler); 49 | } 50 | 51 | void supervisor_thread_t::do_cancel_timer(request_id_t timer_id) noexcept { 52 | auto ctx = static_cast(context); 53 | ctx->cancel_timer(timer_id); 54 | } 55 | 56 | void supervisor_thread_t::update_time() noexcept { 57 | auto ctx = static_cast(context); 58 | ctx->update_time(); 59 | } 60 | -------------------------------------------------------------------------------- /src/rotor/wx/supervisor_wx.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/wx/supervisor_wx.h" 8 | #include 9 | 10 | using namespace rotor::wx; 11 | using namespace rotor; 12 | 13 | namespace rotor::wx { 14 | namespace { 15 | namespace to { 16 | struct on_timer_trigger {}; 17 | } // namespace to 18 | } // namespace 19 | } // namespace rotor::wx 20 | 21 | namespace rotor { 22 | template <> 23 | inline auto rotor::actor_base_t::access(request_id_t request_id, 24 | bool cancelled) noexcept { 25 | on_timer_trigger(request_id, cancelled); 26 | } 27 | } // namespace rotor 28 | 29 | supervisor_wx_t::timer_t::timer_t(timer_handler_base_t *handler_, supervisor_ptr_t &&sup_) 30 | : handler{handler_}, sup{std::move(sup_)} {} 31 | 32 | void supervisor_wx_t::timer_t::Notify() noexcept { 33 | auto timer_id = handler->request_id; 34 | auto *supervisor = sup.get(); 35 | auto &timers_map = sup->timers_map; 36 | 37 | try { 38 | auto actor_ptr = timers_map.at(timer_id)->handler->owner; 39 | actor_ptr->access(timer_id, false); 40 | timers_map.erase(timer_id); 41 | supervisor->do_process(); 42 | } catch (std::out_of_range &ex) { 43 | // no-op 44 | } 45 | } 46 | 47 | supervisor_wx_t::supervisor_wx_t(supervisor_config_wx_t &config_) : supervisor_t{config_}, handler{config_.handler} {} 48 | 49 | void supervisor_wx_t::start() noexcept { 50 | supervisor_ptr_t self{this}; 51 | handler->CallAfter([self = std::move(self)]() { 52 | auto &sup = *self; 53 | sup.do_process(); 54 | }); 55 | } 56 | 57 | void supervisor_wx_t::shutdown() noexcept { 58 | supervisor_ptr_t self{this}; 59 | auto ec = make_error_code(shutdown_code_t::normal); 60 | auto reason = make_error(ec); 61 | handler->CallAfter([self = std::move(self), reason = std::move(reason)]() { 62 | auto &sup = *self; 63 | sup.do_shutdown(reason); 64 | sup.do_process(); 65 | }); 66 | } 67 | 68 | void supervisor_wx_t::enqueue(message_ptr_t message) noexcept { 69 | supervisor_ptr_t self{this}; 70 | handler->CallAfter([self = std::move(self), message = std::move(message)]() { 71 | auto &sup = *self; 72 | sup.put(message); 73 | sup.do_process(); 74 | }); 75 | } 76 | 77 | void supervisor_wx_t::do_start_timer(const pt::time_duration &interval, timer_handler_base_t &handler) noexcept { 78 | auto self = timer_t::supervisor_ptr_t(this); 79 | auto timer = std::make_unique(&handler, std::move(self)); 80 | auto timeout_ms = static_cast(interval.total_milliseconds()); 81 | timer->StartOnce(timeout_ms); 82 | timers_map.emplace(handler.request_id, std::move(timer)); 83 | } 84 | 85 | void supervisor_wx_t::do_cancel_timer(request_id_t timer_id) noexcept { 86 | try { 87 | auto &timer = timers_map.at(timer_id); 88 | timer->Stop(); 89 | auto actor_ptr = timer->handler->owner; 90 | actor_ptr->access(timer_id, true); 91 | timers_map.erase(timer_id); 92 | } catch (std::out_of_range &ex) { 93 | // no-op 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/rotor/wx/system_context_wx.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor/wx/system_context_wx.h" 8 | #include "rotor/wx/supervisor_wx.h" 9 | 10 | using namespace rotor::wx; 11 | 12 | system_context_wx_t::system_context_wx_t(wxAppConsole *app_) { app = app_ ? app_ : wxTheApp; } 13 | -------------------------------------------------------------------------------- /test_package/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.23) 2 | project(test_package CXX) 3 | 4 | find_package("rotor" COMPONENTS asio thread REQUIRED) 5 | 6 | if (WIN32) 7 | add_compile_definitions( 8 | _SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING 9 | _WIN32_WINNT=0x600 10 | ) 11 | endif() 12 | 13 | add_executable(${PROJECT_NAME} test_package.cpp) 14 | target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) 15 | target_link_libraries(${PROJECT_NAME} rotor::asio rotor::thread) 16 | -------------------------------------------------------------------------------- /test_package/conanfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from conan import ConanFile 4 | from conan.tools.cmake import CMake, cmake_layout 5 | from conan.tools.build import can_run 6 | 7 | 8 | class helloTestConan(ConanFile): 9 | settings = "os", "compiler", "build_type", "arch" 10 | generators = "CMakeDeps", "CMakeToolchain" 11 | 12 | def requirements(self): 13 | self.requires(self.tested_reference_str) 14 | 15 | def build(self): 16 | cmake = CMake(self) 17 | cmake.configure() 18 | cmake.build() 19 | 20 | def layout(self): 21 | cmake_layout(self) 22 | 23 | def test(self): 24 | if can_run(self): 25 | cmd = os.path.join(self.cpp.build.bindir, "test_package") 26 | self.run(cmd, env="conanrun") 27 | -------------------------------------------------------------------------------- /test_package/test_package.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace asio = boost::asio; 5 | namespace pt = boost::posix_time; 6 | 7 | struct server_actor : public rotor::actor_base_t { 8 | using rotor::actor_base_t::actor_base_t; 9 | void on_start() noexcept override { 10 | rotor::actor_base_t::on_start(); 11 | std::cout << "hello world\n"; 12 | do_shutdown(); 13 | } 14 | }; 15 | 16 | int main() { 17 | asio::io_context io_context; 18 | auto system_context = rotor::asio::system_context_asio_t::ptr_t{new rotor::asio::system_context_asio_t(io_context)}; 19 | auto strand = std::make_shared(io_context); 20 | auto timeout = boost::posix_time::milliseconds{500}; 21 | auto sup = 22 | system_context->create_supervisor().strand(strand).timeout(timeout).finish(); 23 | 24 | sup->create_actor().timeout(timeout).autoshutdown_supervisor().finish(); 25 | 26 | sup->start(); 27 | io_context.run(); 28 | 29 | rotor::thread::system_context_thread_t thread_ctx; 30 | 31 | return 0; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /tests/008-config-builder.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include "supervisor_test.h" 9 | #include "actor_test.h" 10 | #include "system_context_test.h" 11 | #include "access.h" 12 | 13 | namespace r = rotor; 14 | namespace rt = rotor::test; 15 | 16 | template struct sample_config_builder_t : public r::actor_config_builder_t { 17 | using parent_t = r::actor_config_builder_t; 18 | using parent_t::parent_t; 19 | 20 | bool validate() noexcept override { return false; } 21 | }; 22 | 23 | struct sample_actor_t : public rt::actor_test_t { 24 | using rt::actor_test_t::actor_test_t; 25 | template using config_builder_t = sample_config_builder_t; 26 | }; 27 | 28 | TEST_CASE("direct builder configuration", "[config_builder]") { 29 | r::system_context_t system_context; 30 | auto sup = system_context.create_supervisor() 31 | .timeout(rt::default_timeout) 32 | .locality(&system_context) 33 | .finish(); 34 | REQUIRE(sup); 35 | REQUIRE(sup->locality == &system_context); 36 | sup->do_process(); 37 | sup->do_shutdown(); 38 | sup->do_process(); 39 | sup->do_process(); 40 | CHECK(rt::empty(sup->get_subscription())); 41 | } 42 | 43 | TEST_CASE("indirect builder configuration", "[config_builder]") { 44 | r::system_context_t system_context; 45 | auto sup = system_context.create_supervisor() 46 | .locality(&system_context) 47 | .timeout(rt::default_timeout) 48 | .finish(); 49 | REQUIRE(sup); 50 | REQUIRE(sup->locality == &system_context); 51 | sup->do_process(); 52 | sup->do_shutdown(); 53 | sup->do_process(); 54 | } 55 | 56 | TEST_CASE("validation", "[config_builder]") { 57 | rt::system_context_test_t system_context; 58 | auto sup = system_context.create_supervisor() 59 | .locality(&system_context) 60 | .timeout(rt::default_timeout) 61 | .finish(); 62 | REQUIRE(sup); 63 | 64 | SECTION("custom validator failed") { 65 | auto act = sup->create_actor().timeout(rt::default_timeout).finish(); 66 | REQUIRE(!act); 67 | CHECK(system_context.reason->ec == r::error_code_t::actor_misconfigured); 68 | } 69 | 70 | sup->do_process(); 71 | sup->do_shutdown(); 72 | sup->do_process(); 73 | } 74 | -------------------------------------------------------------------------------- /tests/009-system-context.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include "supervisor_test.h" 9 | #include "system_context_test.h" 10 | #include 11 | #include 12 | 13 | namespace r = rotor; 14 | namespace rt = rotor::test; 15 | 16 | TEST_CASE("misconfigured root supervisor", "[system_context]") { 17 | rt::system_context_test_t system_context; 18 | auto sup = system_context.create_supervisor().finish(); 19 | REQUIRE(!sup); 20 | REQUIRE(system_context.reason->ec.value() == static_cast(r::error_code_t::actor_misconfigured)); 21 | REQUIRE(system_context.reason == system_context.reason->root()); 22 | CHECK_THAT(system_context.reason->message(), Catch::Matchers::EndsWith("actor is misconfigured")); 23 | REQUIRE(!system_context.get_supervisor()); 24 | } 25 | 26 | TEST_CASE("properly configured root supervisor", "[system_context]") { 27 | rt::system_context_test_t system_context; 28 | auto sup = system_context.create_supervisor().timeout(rt::default_timeout).finish(); 29 | REQUIRE(sup); 30 | CHECK(!system_context.reason); 31 | CHECK(system_context.get_supervisor() == sup); 32 | 33 | sup->do_process(); 34 | sup->do_shutdown(); 35 | sup->do_process(); 36 | sup.reset(); 37 | } 38 | 39 | TEST_CASE("root supervisor cannot be created twice", "[system_context]") { 40 | rt::system_context_test_t system_context; 41 | auto sup1 = system_context.create_supervisor().timeout(rt::default_timeout).finish(); 42 | REQUIRE(sup1); 43 | REQUIRE(system_context.get_supervisor() == sup1); 44 | 45 | auto sup2 = system_context.create_supervisor().timeout(rt::default_timeout).finish(); 46 | REQUIRE(!sup2); 47 | REQUIRE(system_context.get_supervisor() == sup1); 48 | REQUIRE(system_context.reason->ec.value() == static_cast(r::error_code_t::supervisor_defined)); 49 | CHECK_THAT(system_context.reason->message(), Catch::Matchers::EndsWith("supervisor is already defined")); 50 | 51 | sup1->do_process(); 52 | sup1->do_shutdown(); 53 | sup1->do_process(); 54 | } 55 | -------------------------------------------------------------------------------- /tests/011-ping_pong.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include "access.h" 9 | #include "supervisor_test.h" 10 | 11 | namespace r = rotor; 12 | namespace rt = r::test; 13 | 14 | struct ping_t {}; 15 | struct pong_t {}; 16 | 17 | static std::uint32_t destroyed = 0; 18 | 19 | struct pinger_t : public r::actor_base_t { 20 | std::uint32_t ping_sent = 0; 21 | std::uint32_t pong_received = 0; 22 | 23 | using r::actor_base_t::actor_base_t; 24 | ~pinger_t() override { destroyed += 1; } 25 | 26 | void set_ponger_addr(const r::address_ptr_t &addr) { ponger_addr = addr; } 27 | 28 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 29 | plugin.with_casted([](auto &p) { p.subscribe_actor(&pinger_t::on_pong); }); 30 | } 31 | 32 | void on_start() noexcept override { 33 | r::actor_base_t::on_start(); 34 | ++ping_sent; 35 | send(ponger_addr); 36 | } 37 | 38 | void on_pong(r::message_t &) noexcept { ++pong_received; } 39 | 40 | r::address_ptr_t ponger_addr; 41 | }; 42 | 43 | struct ponger_t : public r::actor_base_t { 44 | std::uint32_t ping_received = 0; 45 | std::uint32_t pong_sent = 0; 46 | 47 | using r::actor_base_t::actor_base_t; 48 | ~ponger_t() override { destroyed += 3; } 49 | 50 | void set_pinger_addr(const r::address_ptr_t &addr) { pinger_addr = addr; } 51 | 52 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 53 | plugin.with_casted([](auto &p) { p.subscribe_actor(&ponger_t::on_ping); }); 54 | } 55 | 56 | void on_ping(r::message_t &) noexcept { 57 | ++ping_received; 58 | send(pinger_addr); 59 | ++pong_sent; 60 | } 61 | 62 | private: 63 | r::address_ptr_t pinger_addr; 64 | }; 65 | 66 | TEST_CASE("ping-pong", "[supervisor]") { 67 | r::system_context_t system_context; 68 | 69 | auto sup = system_context.create_supervisor().timeout(rt::default_timeout).finish(); 70 | auto pinger = sup->create_actor().timeout(rt::default_timeout).finish(); 71 | auto ponger = sup->create_actor().timeout(rt::default_timeout).finish(); 72 | 73 | pinger->set_ponger_addr(ponger->get_address()); 74 | ponger->set_pinger_addr(pinger->get_address()); 75 | 76 | sup->do_process(); 77 | REQUIRE(pinger->ping_sent == 1); 78 | REQUIRE(pinger->pong_received == 1); 79 | REQUIRE(ponger->pong_sent == 1); 80 | REQUIRE(ponger->ping_received == 1); 81 | 82 | sup->do_shutdown(); 83 | sup->do_process(); 84 | REQUIRE(sup->get_state() == r::state_t::SHUT_DOWN); 85 | REQUIRE(sup->get_leader_queue().size() == 0); 86 | REQUIRE(sup->get_points().size() == 0); 87 | CHECK(rt::empty(sup->get_subscription())); 88 | 89 | pinger.reset(); 90 | ponger.reset(); 91 | REQUIRE(destroyed == 4); 92 | } 93 | -------------------------------------------------------------------------------- /tests/013-observer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include "supervisor_test.h" 9 | #include "access.h" 10 | 11 | namespace r = rotor; 12 | namespace rt = r::test; 13 | 14 | struct foo_t {}; 15 | 16 | struct simpleton_actor_t : public r::actor_base_t { 17 | using r::actor_base_t::actor_base_t; 18 | 19 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 20 | plugin.with_casted([](auto &p) { p.subscribe_actor(&simpleton_actor_t::on_foo); }); 21 | } 22 | 23 | void on_start() noexcept override { 24 | r::actor_base_t::on_start(); 25 | INFO("simpleton_actor_t::on_start()"); 26 | send(address); 27 | } 28 | 29 | void on_foo(r::message_t &) noexcept { 30 | INFO("simpleton_actor_t::on_foo()"); 31 | ++foo_count; 32 | } 33 | 34 | std::uint32_t foo_count = 0; 35 | }; 36 | 37 | struct observer_config_t : r::actor_config_t { 38 | r::address_ptr_t observable; 39 | using r::actor_config_t::actor_config_t; 40 | }; 41 | 42 | template struct observer_config_builder_t : r::actor_config_builder_t { 43 | using builder_t = typename Actor::template config_builder_t; 44 | using parent_t = r::actor_config_builder_t; 45 | using parent_t::parent_t; 46 | 47 | builder_t &&observable(const r::address_ptr_t &addr) { 48 | parent_t::config.observable = addr; 49 | return std::move(*static_cast(this)); 50 | } 51 | 52 | bool validate() noexcept override { return parent_t::config.observable && parent_t::validate(); } 53 | }; 54 | 55 | struct foo_observer_t : public r::actor_base_t { 56 | using config_t = observer_config_t; 57 | template using config_builder_t = observer_config_builder_t; 58 | 59 | explicit foo_observer_t(config_t &cfg) : r::actor_base_t(cfg), simpleton_addr{cfg.observable} {} 60 | 61 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 62 | plugin.with_casted( 63 | [this](auto &p) { p.subscribe_actor(&foo_observer_t::on_foo, simpleton_addr); }); 64 | } 65 | 66 | void on_foo(r::message_t &) noexcept { 67 | ++foo_count; 68 | INFO("foo_observer_t::on_foo"); 69 | } 70 | 71 | r::address_ptr_t simpleton_addr; 72 | std::uint32_t foo_count = 0; 73 | }; 74 | 75 | TEST_CASE("observer", "[actor]") { 76 | r::system_context_t system_context; 77 | 78 | auto sup = system_context.create_supervisor().timeout(rt::default_timeout).finish(); 79 | auto simpleton = sup->create_actor().timeout(rt::default_timeout).finish(); 80 | auto &simpleton_addr = simpleton->get_address(); 81 | auto observer = 82 | sup->create_actor().observable(simpleton_addr).timeout(rt::default_timeout).finish(); 83 | sup->do_process(); 84 | 85 | REQUIRE(simpleton->foo_count == 1); 86 | REQUIRE(observer->foo_count == 1); 87 | 88 | sup->do_shutdown(); 89 | sup->do_process(); 90 | REQUIRE(sup->get_state() == r::state_t::SHUT_DOWN); 91 | REQUIRE(sup->get_leader_queue().size() == 0); 92 | REQUIRE(sup->get_points().size() == 0); 93 | REQUIRE(rt::empty(sup->get_subscription())); 94 | } 95 | -------------------------------------------------------------------------------- /tests/015-resources.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "access.h" 8 | #include "rotor.hpp" 9 | #include "supervisor_test.h" 10 | #include "actor_test.h" 11 | 12 | namespace r = rotor; 13 | namespace rt = r::test; 14 | 15 | TEST_CASE("release/acquire resources, when other actor is in progress of configuration", "[plugin]") { 16 | r::system_context_t system_context; 17 | auto sup = system_context.create_supervisor() 18 | .timeout(rt::default_timeout) 19 | .create_registry(true) 20 | .finish(); 21 | auto act = sup->create_actor().timeout(rt::default_timeout).finish(); 22 | act->configurer = [&](auto &, r::plugin::plugin_base_t &plugin) { 23 | plugin.with_casted([&](auto &) { 24 | auto res = act->access(); 25 | res->acquire(0); 26 | res->release(0); 27 | }); 28 | }; 29 | 30 | sup->do_process(); 31 | CHECK(sup->get_state() == r::state_t::OPERATIONAL); 32 | CHECK(act->get_state() == r::state_t::OPERATIONAL); 33 | 34 | sup->do_shutdown(); 35 | sup->do_process(); 36 | CHECK(sup->get_state() == r::state_t::SHUT_DOWN); 37 | CHECK(act->get_state() == r::state_t::SHUT_DOWN); 38 | } 39 | -------------------------------------------------------------------------------- /tests/016-pub_sub.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include "supervisor_test.h" 9 | #include "access.h" 10 | 11 | namespace r = rotor; 12 | namespace rt = r::test; 13 | 14 | struct payload_t {}; 15 | 16 | struct pub_config_t : r::actor_config_t { 17 | r::address_ptr_t pub_addr; 18 | using r::actor_config_t::actor_config_t; 19 | }; 20 | 21 | template struct pub_config_builder_t : r::actor_config_builder_t { 22 | using builder_t = typename Actor::template config_builder_t; 23 | using parent_t = r::actor_config_builder_t; 24 | using parent_t::parent_t; 25 | 26 | builder_t &&pub_addr(const r::address_ptr_t &addr) { 27 | parent_t::config.pub_addr = addr; 28 | return std::move(*static_cast(this)); 29 | } 30 | 31 | bool validate() noexcept override { return parent_t::config.pub_addr && parent_t::validate(); } 32 | }; 33 | 34 | struct pub_t : public r::actor_base_t { 35 | using config_t = pub_config_t; 36 | template using config_builder_t = pub_config_builder_t; 37 | 38 | explicit pub_t(config_t &cfg) : r::actor_base_t(cfg), pub_addr{cfg.pub_addr} {} 39 | 40 | void on_start() noexcept override { 41 | r::actor_base_t::on_start(); 42 | send(pub_addr); 43 | } 44 | 45 | r::address_ptr_t pub_addr; 46 | }; 47 | 48 | struct sub_t : public r::actor_base_t { 49 | using config_t = pub_config_t; 50 | template using config_builder_t = pub_config_builder_t; 51 | 52 | explicit sub_t(config_t &cfg) : r::actor_base_t(cfg), pub_addr{cfg.pub_addr} {} 53 | 54 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 55 | plugin.with_casted( 56 | [this](auto &p) { p.subscribe_actor(&sub_t::on_payload, pub_addr); }); 57 | } 58 | 59 | void on_payload(r::message_t &) noexcept { ++received; } 60 | 61 | std::uint16_t received = 0; 62 | r::address_ptr_t pub_addr; 63 | }; 64 | 65 | TEST_CASE("ping-pong-pub-sub", "[supervisor]") { 66 | r::system_context_t system_context; 67 | 68 | auto sup = system_context.create_supervisor().timeout(rt::default_timeout).finish(); 69 | auto pub_addr = sup->create_address(); 70 | sup->create_actor().pub_addr(pub_addr).timeout(rt::default_timeout).finish(); 71 | auto sub1 = sup->create_actor().pub_addr(pub_addr).timeout(rt::default_timeout).finish(); 72 | auto sub2 = sup->create_actor().pub_addr(pub_addr).timeout(rt::default_timeout).finish(); 73 | 74 | sup->do_process(); 75 | 76 | REQUIRE(sub1->received == 1); 77 | REQUIRE(sub2->received == 1); 78 | 79 | sup->do_shutdown(); 80 | sup->do_process(); 81 | REQUIRE(sup->get_state() == r::state_t::SHUT_DOWN); 82 | REQUIRE(sup->get_leader_queue().size() == 0); 83 | REQUIRE(sup->get_points().size() == 0); 84 | CHECK(rt::empty(sup->get_subscription())); 85 | } 86 | -------------------------------------------------------------------------------- /tests/018-lambda-handlers.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include "supervisor_test.h" 9 | #include "access.h" 10 | 11 | namespace r = rotor; 12 | namespace rt = r::test; 13 | 14 | static const void *my_tag = &my_tag; 15 | 16 | struct payload { 17 | const void *data; 18 | }; 19 | using message_t = r::message_t; 20 | 21 | struct sample_actor_t : public r::actor_base_t { 22 | using r::actor_base_t::actor_base_t; 23 | bool received = false; 24 | 25 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 26 | plugin.with_casted([this](auto &p) { 27 | r::subscription_info_ptr_t info = 28 | p.subscribe_actor(r::lambda([this](message_t &) noexcept { received = true; })); 29 | info->access(my_tag); 30 | }); 31 | } 32 | 33 | void on_start() noexcept override { 34 | r::actor_base_t::on_start(); 35 | send(address, my_tag); 36 | } 37 | }; 38 | 39 | TEST_CASE("lambda handler + interceptor", "[actor]") { 40 | r::system_context_t system_context; 41 | 42 | bool intercepted = false; 43 | rt::interceptor_t interceptor = [&](auto &msg, const void *tag, const auto &cont) { 44 | if (tag == my_tag) { 45 | auto my_msg = static_cast(msg.get()); 46 | if (my_msg->payload.data == my_tag) { 47 | intercepted = true; 48 | } 49 | } 50 | cont(); 51 | }; 52 | auto sup = system_context.create_supervisor() 53 | .interceptor(std::move(interceptor)) 54 | .timeout(rt::default_timeout) 55 | .finish(); 56 | auto actor = sup->create_actor().timeout(rt::default_timeout).finish(); 57 | sup->do_process(); 58 | 59 | REQUIRE(sup->active_timers.size() == 0); 60 | REQUIRE(actor->received == true); 61 | 62 | sup->do_shutdown(); 63 | sup->do_process(); 64 | 65 | REQUIRE(sup->get_state() == r::state_t::SHUT_DOWN); 66 | REQUIRE(sup->get_leader_queue().size() == 0); 67 | REQUIRE(sup->get_points().size() == 0); 68 | REQUIRE(rt::empty(sup->get_subscription())); 69 | CHECK(intercepted); 70 | } 71 | -------------------------------------------------------------------------------- /tests/098-message-stringifier.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include "rotor/extended_error.h" 9 | #include "supervisor_test.h" 10 | 11 | #include 12 | #include 13 | 14 | using namespace Catch::Matchers; 15 | 16 | namespace r = rotor; 17 | namespace rt = rotor::test; 18 | 19 | namespace payload { 20 | 21 | struct ping_t {}; 22 | 23 | } // namespace payload 24 | 25 | namespace message { 26 | 27 | using ping_t = r::message_t; 28 | 29 | } 30 | 31 | TEST_CASE("default message stringifier", "[misc]") { 32 | r::system_context_ptr_t system_context = new r::system_context_t(); 33 | auto sup = system_context->create_supervisor().timeout(rt::default_timeout).finish(); 34 | auto &stringifer = system_context->get_stringifier(); 35 | 36 | SECTION("rotor message") { 37 | auto start_message = r::make_message(sup->get_address()); 38 | auto message = stringifer.stringify(*start_message); 39 | CHECK_THAT(message, StartsWith("r::start_trigger @")); 40 | } 41 | 42 | SECTION("unknown / non-rotor message") { 43 | auto ping = r::make_message(sup->get_address()); 44 | auto message = stringifer.stringify(*ping); 45 | CHECK_THAT(message, ContainsSubstring("[?]")); 46 | CHECK_THAT(message, ContainsSubstring("rotor::message_t")); 47 | CHECK_THAT(message, ContainsSubstring("payload::ping_t")); 48 | CHECK_THAT(message, ContainsSubstring(" =>")); 49 | } 50 | 51 | SECTION("extended error") { 52 | auto ping = r::make_message(sup->get_address()); 53 | auto context = std::string("some-actor"); 54 | auto ee = r::make_error(context, {}, {}, ping); 55 | auto string = ee->message(&stringifer); 56 | CHECK_THAT(string, ContainsSubstring("[r::start_trigger @")); 57 | CHECK_THAT(string, ContainsSubstring("some-actor")); 58 | } 59 | 60 | sup->do_process(); 61 | sup->do_shutdown(); 62 | sup->do_process(); 63 | } 64 | -------------------------------------------------------------------------------- /tests/099-misc.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include 9 | 10 | namespace r = rotor; 11 | 12 | TEST_CASE("error code messages", "[misc]") { 13 | CHECK(r::error_code_category().name() == std::string("rotor_error")); 14 | CHECK(r::error_code_category().message(-1) == "unknown"); 15 | 16 | CHECK(r::shutdown_code_category().name() == std::string("rotor_shutdown")); 17 | CHECK(r::shutdown_code_category().message(-1) == "unknown shutdown reason"); 18 | } 19 | -------------------------------------------------------------------------------- /tests/101-asio_ping-pong-1-strand.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include "rotor/asio.hpp" 9 | #include "supervisor_asio_test.h" 10 | 11 | namespace r = rotor; 12 | namespace ra = rotor::asio; 13 | namespace rt = r::test; 14 | namespace asio = boost::asio; 15 | namespace pt = boost::posix_time; 16 | 17 | struct ping_t {}; 18 | struct pong_t {}; 19 | 20 | static std::uint32_t destroyed = 0; 21 | 22 | struct pinger_t : public r::actor_base_t { 23 | std::uint32_t ping_sent = 0; 24 | std::uint32_t pong_received = 0; 25 | rotor::address_ptr_t ponger_addr; 26 | 27 | using r::actor_base_t::actor_base_t; 28 | ~pinger_t() { destroyed += 1; } 29 | 30 | void set_ponger_addr(const rotor::address_ptr_t &addr) { ponger_addr = addr; } 31 | 32 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 33 | r::actor_base_t::configure(plugin); 34 | plugin.with_casted([](auto &p) { p.subscribe_actor(&pinger_t::on_pong); }); 35 | } 36 | 37 | void on_start() noexcept override { 38 | r::actor_base_t::on_start(); 39 | send(ponger_addr); 40 | ++ping_sent; 41 | } 42 | 43 | void on_pong(rotor::message_t &) noexcept { 44 | ++pong_received; 45 | do_shutdown(); 46 | } 47 | }; 48 | 49 | struct ponger_t : public r::actor_base_t { 50 | std::uint32_t pong_sent = 0; 51 | std::uint32_t ping_received = 0; 52 | rotor::address_ptr_t pinger_addr; 53 | 54 | using r::actor_base_t::actor_base_t; 55 | ~ponger_t() { destroyed += 3; } 56 | 57 | void set_pinger_addr(const rotor::address_ptr_t &addr) { pinger_addr = addr; } 58 | 59 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 60 | plugin.with_casted([](auto &p) { p.subscribe_actor(&ponger_t::on_ping); }); 61 | } 62 | 63 | void on_ping(rotor::message_t &) noexcept { 64 | ++ping_received; 65 | send(pinger_addr); 66 | ++pong_sent; 67 | } 68 | }; 69 | 70 | TEST_CASE("ping/pong ", "[supervisor][asio]") { 71 | asio::io_context io_context{1}; 72 | auto system_context = ra::system_context_asio_t::ptr_t{new ra::system_context_asio_t(io_context)}; 73 | auto strand = std::make_shared(io_context); 74 | auto timeout = r::pt::milliseconds{10}; 75 | auto sup = system_context->create_supervisor().timeout(timeout).strand(strand).finish(); 76 | 77 | auto pinger = sup->create_actor().timeout(timeout).autoshutdown_supervisor().finish(); 78 | auto ponger = sup->create_actor().timeout(timeout).finish(); 79 | pinger->set_ponger_addr(static_cast(ponger.get())->get_address()); 80 | ponger->set_pinger_addr(static_cast(pinger.get())->get_address()); 81 | 82 | sup->start(); 83 | io_context.run(); 84 | 85 | REQUIRE(pinger->ping_sent == 1); 86 | REQUIRE(pinger->pong_received == 1); 87 | REQUIRE(ponger->pong_sent == 1); 88 | REQUIRE(ponger->ping_received == 1); 89 | 90 | REQUIRE(static_cast(sup.get())->access() == r::state_t::SHUT_DOWN); 91 | REQUIRE(sup->get_leader_queue().size() == 0); 92 | CHECK(rt::empty(sup->get_subscription())); 93 | 94 | pinger.reset(); 95 | ponger.reset(); 96 | 97 | io_context.run(); 98 | CHECK(sup->get_timers_map().size() == 0); 99 | CHECK(destroyed == 4); 100 | } 101 | -------------------------------------------------------------------------------- /tests/104-asio_timer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "rotor.hpp" 8 | #include "rotor/asio.hpp" 9 | #include "supervisor_asio_test.h" 10 | #include "access.h" 11 | 12 | namespace r = rotor; 13 | namespace ra = rotor::asio; 14 | namespace rt = r::test; 15 | namespace asio = boost::asio; 16 | namespace pt = boost::posix_time; 17 | 18 | struct sample_res_t {}; 19 | struct sample_req_t { 20 | using response_t = sample_res_t; 21 | }; 22 | 23 | using traits_t = r::request_traits_t; 24 | 25 | struct bad_actor_t : public r::actor_base_t { 26 | using r::actor_base_t::actor_base_t; 27 | r::extended_error_ptr_t ee; 28 | 29 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 30 | r::actor_base_t::configure(plugin); 31 | plugin.with_casted([](auto &p) { p.subscribe_actor(&bad_actor_t::on_response); }); 32 | } 33 | 34 | void on_start() noexcept override { 35 | r::actor_base_t::on_start(); 36 | start_timer(r::pt::milliseconds(1), *this, &bad_actor_t::delayed_start); 37 | start_timer(r::pt::minutes(1), *this, &bad_actor_t::delayed_start); // to be cancelled 38 | } 39 | 40 | void delayed_start(r::request_id_t, bool) noexcept { 41 | request(address).send(r::pt::milliseconds(1)); 42 | } 43 | 44 | void on_response(traits_t::response::message_t &msg) noexcept { 45 | ee = msg.payload.ee; 46 | supervisor->do_shutdown(); 47 | } 48 | }; 49 | 50 | TEST_CASE("timer", "[supervisor][asio]") { 51 | asio::io_context io_context{1}; 52 | auto timeout = r::pt::milliseconds{10}; 53 | auto system_context = ra::system_context_asio_t::ptr_t{new ra::system_context_asio_t(io_context)}; 54 | auto strand = std::make_shared(io_context); 55 | 56 | auto sup = system_context->create_supervisor().strand(strand).timeout(timeout).finish(); 57 | auto actor = sup->create_actor().timeout(timeout).finish(); 58 | 59 | sup->start(); 60 | io_context.run(); 61 | 62 | REQUIRE(actor->ee->ec == r::error_code_t::request_timeout); 63 | 64 | REQUIRE(static_cast(sup.get())->access() == r::state_t::SHUT_DOWN); 65 | CHECK(rt::empty(sup->get_subscription())); 66 | 67 | REQUIRE(sup->get_state() == r::state_t::SHUT_DOWN); 68 | REQUIRE(sup->get_leader_queue().size() == 0); 69 | CHECK(rt::empty(sup->get_subscription())); 70 | } 71 | -------------------------------------------------------------------------------- /tests/121-wx_ping_ping.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include 8 | #include "rotor.hpp" 9 | #include "rotor/wx.hpp" 10 | #include "supervisor_wx_test.h" 11 | #include 12 | #include 13 | #include "access.h" 14 | 15 | IMPLEMENT_APP_NO_MAIN(rotor::test::RotorApp) 16 | 17 | namespace r = rotor; 18 | namespace rx = rotor::wx; 19 | namespace rt = rotor::test; 20 | 21 | struct ping_t {}; 22 | struct pong_t {}; 23 | 24 | static std::uint32_t destroyed = 0; 25 | 26 | struct pinger_t : public r::actor_base_t { 27 | std::uint32_t ping_sent = 0; 28 | std::uint32_t pong_received = 0; 29 | rotor::address_ptr_t ponger_addr; 30 | 31 | using r::actor_base_t::actor_base_t; 32 | ~pinger_t() { destroyed += 1; } 33 | 34 | void set_ponger_addr(const rotor::address_ptr_t &addr) { ponger_addr = addr; } 35 | 36 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 37 | r::actor_base_t::configure(plugin); 38 | plugin.with_casted([](auto &p) { p.subscribe_actor(&pinger_t::on_pong); }); 39 | } 40 | 41 | void on_start() noexcept override { 42 | r::actor_base_t::on_start(); 43 | send(ponger_addr); 44 | ++ping_sent; 45 | } 46 | 47 | void on_pong(rotor::message_t &) noexcept { 48 | ++pong_received; 49 | supervisor->shutdown(); 50 | auto loop = wxEventLoopBase::GetActive(); 51 | loop->ScheduleExit(); 52 | } 53 | }; 54 | 55 | struct ponger_t : public r::actor_base_t { 56 | std::uint32_t pong_sent = 0; 57 | std::uint32_t ping_received = 0; 58 | rotor::address_ptr_t pinger_addr; 59 | 60 | using r::actor_base_t::actor_base_t; 61 | 62 | ~ponger_t() { destroyed += 3; } 63 | 64 | void set_pinger_addr(const rotor::address_ptr_t &addr) { pinger_addr = addr; } 65 | 66 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 67 | plugin.with_casted([](auto &p) { p.subscribe_actor(&ponger_t::on_ping); }); 68 | } 69 | 70 | void on_ping(rotor::message_t &) noexcept { 71 | ++ping_received; 72 | send(pinger_addr); 73 | ++pong_sent; 74 | } 75 | }; 76 | 77 | TEST_CASE("ping/pong ", "[supervisor][wx]") { 78 | using app_t = rotor::test::RotorApp; 79 | auto app = new app_t(); 80 | 81 | auto timeout = r::pt::milliseconds{10}; 82 | app->CallOnInit(); 83 | wxEventLoopBase *loop = app->GetTraits()->CreateEventLoop(); 84 | wxEventLoopBase::SetActive(loop); 85 | rx::system_context_ptr_t system_context{new rx::system_context_wx_t(app)}; 86 | wxEvtHandler handler; 87 | auto sup = 88 | system_context->create_supervisor().handler(&handler).timeout(timeout).finish(); 89 | sup->start(); 90 | 91 | auto pinger = sup->create_actor().timeout(timeout).finish(); 92 | auto ponger = sup->create_actor().timeout(timeout).finish(); 93 | pinger->set_ponger_addr(static_cast(ponger.get())->get_address()); 94 | ponger->set_pinger_addr(static_cast(pinger.get())->get_address()); 95 | 96 | sup->start(); 97 | loop->Run(); 98 | 99 | REQUIRE(pinger->ping_sent == 1); 100 | REQUIRE(pinger->pong_received == 1); 101 | REQUIRE(ponger->pong_sent == 1); 102 | REQUIRE(ponger->ping_received == 1); 103 | 104 | pinger.reset(); 105 | ponger.reset(); 106 | REQUIRE(destroyed == 4); 107 | 108 | REQUIRE(static_cast(sup.get())->access() == r::state_t::SHUT_DOWN); 109 | REQUIRE(sup->get_leader_queue().size() == 0); 110 | CHECK(rt::empty(sup->get_subscription())); 111 | 112 | delete app; 113 | } 114 | -------------------------------------------------------------------------------- /tests/122-wx_timer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2024 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include 8 | #include "rotor.hpp" 9 | #include "rotor/wx.hpp" 10 | #include "supervisor_wx_test.h" 11 | #include 12 | #include 13 | #include "access.h" 14 | 15 | IMPLEMENT_APP_NO_MAIN(rotor::test::RotorApp) 16 | 17 | namespace r = rotor; 18 | namespace rx = rotor::wx; 19 | namespace rt = r::test; 20 | namespace pt = boost::posix_time; 21 | 22 | struct sample_res_t {}; 23 | struct sample_req_t { 24 | using response_t = sample_res_t; 25 | }; 26 | 27 | using traits_t = r::request_traits_t; 28 | 29 | struct bad_actor_t : public r::actor_base_t { 30 | using r::actor_base_t::actor_base_t; 31 | r::extended_error_ptr_t ee; 32 | 33 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 34 | r::actor_base_t::configure(plugin); 35 | plugin.with_casted([](auto &p) { p.subscribe_actor(&bad_actor_t::on_response); }); 36 | } 37 | 38 | void on_start() noexcept override { 39 | r::actor_base_t::on_start(); 40 | start_timer(r::pt::milliseconds(1), *this, &bad_actor_t::delayed_start); 41 | start_timer(r::pt::minutes(1), *this, &bad_actor_t::delayed_start); // to be cancelled 42 | } 43 | 44 | void delayed_start(r::request_id_t, bool) noexcept { 45 | request(address).send(r::pt::milliseconds(1)); 46 | } 47 | 48 | void on_response(traits_t::response::message_t &msg) noexcept { 49 | ee = msg.payload.ee; 50 | // alternative for supervisor.do_shutdown() for better coverage 51 | auto sup_addr = static_cast(supervisor)->get_address(); 52 | auto shutdown_trigger = r::make_message(sup_addr, sup_addr, ee); 53 | supervisor->enqueue(shutdown_trigger); 54 | auto loop = wxEventLoopBase::GetActive(); 55 | loop->ScheduleExit(); 56 | } 57 | }; 58 | 59 | TEST_CASE("ping/pong ", "[supervisor][wx]") { 60 | using app_t = rotor::test::RotorApp; 61 | auto app = new app_t(); 62 | 63 | auto timeout = r::pt::milliseconds{10}; 64 | app->CallOnInit(); 65 | wxEventLoopBase *loop = app->GetTraits()->CreateEventLoop(); 66 | wxEventLoopBase::SetActive(loop); 67 | rx::system_context_ptr_t system_context{new rx::system_context_wx_t(app)}; 68 | wxEvtHandler handler; 69 | auto sup = 70 | system_context->create_supervisor().handler(&handler).timeout(timeout).finish(); 71 | sup->start(); 72 | 73 | auto actor = sup->create_actor().timeout(timeout).finish(); 74 | 75 | sup->start(); 76 | loop->Run(); 77 | 78 | REQUIRE(actor->ee->ec == r::error_code_t::request_timeout); 79 | 80 | REQUIRE(sup->get_state() == r::state_t::SHUT_DOWN); 81 | REQUIRE(sup->get_leader_queue().size() == 0); 82 | CHECK(rt::empty(sup->get_subscription())); 83 | 84 | actor.reset(); 85 | sup.reset(); 86 | 87 | delete app; 88 | } 89 | -------------------------------------------------------------------------------- /tests/132-ev_timer.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include 8 | #include "rotor.hpp" 9 | #include "rotor/ev.hpp" 10 | #include "access.h" 11 | #include 12 | #include // for calling WSAStartup on Windows 13 | 14 | namespace r = rotor; 15 | namespace re = rotor::ev; 16 | namespace pt = boost::posix_time; 17 | namespace rt = r::test; 18 | 19 | struct sample_res_t {}; 20 | struct sample_req_t { 21 | using response_t = sample_res_t; 22 | }; 23 | 24 | using traits_t = r::request_traits_t; 25 | 26 | struct bad_actor_t : public r::actor_base_t { 27 | using r::actor_base_t::actor_base_t; 28 | r::extended_error_ptr_t ee; 29 | 30 | void configure(r::plugin::plugin_base_t &plugin) noexcept override { 31 | r::actor_base_t::configure(plugin); 32 | plugin.with_casted([](auto &p) { p.subscribe_actor(&bad_actor_t::on_response); }); 33 | } 34 | 35 | void on_start() noexcept override { 36 | r::actor_base_t::on_start(); 37 | start_timer(r::pt::milliseconds(1), *this, &bad_actor_t::delayed_start); 38 | start_timer(r::pt::minutes(1), *this, &bad_actor_t::delayed_start); // to be cancelled 39 | } 40 | 41 | void delayed_start(r::request_id_t, bool) noexcept { 42 | request(address).send(r::pt::milliseconds(1)); 43 | } 44 | 45 | void on_response(traits_t::response::message_t &msg) noexcept { 46 | ee = msg.payload.ee; 47 | supervisor->do_shutdown(); 48 | } 49 | }; 50 | 51 | TEST_CASE("timer", "[supervisor][ev]") { 52 | auto *loop = ev_loop_new(0); 53 | auto system_context = r::intrusive_ptr_t{new re::system_context_ev_t()}; 54 | auto timeout = r::pt::milliseconds{10}; 55 | auto sup = system_context->create_supervisor() 56 | .loop(loop) 57 | .timeout(timeout) 58 | .loop_ownership(true) 59 | .finish(); 60 | auto actor = sup->create_actor().timeout(timeout).finish(); 61 | 62 | sup->start(); 63 | ev_run(loop); 64 | 65 | REQUIRE(actor->ee->ec == r::error_code_t::request_timeout); 66 | REQUIRE(static_cast(sup.get())->access() == r::state_t::SHUT_DOWN); 67 | } 68 | -------------------------------------------------------------------------------- /tests/143-thread-shutdown_flag.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #ifdef __unix__ 8 | #include "rotor.hpp" 9 | #include "rotor/thread.hpp" 10 | #include "actor_test.h" 11 | #include "supervisor_test.h" 12 | #include "system_context_test.h" 13 | #include "access.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | namespace r = rotor; 20 | namespace rt = r::test; 21 | namespace rth = rotor::thread; 22 | 23 | namespace payload { 24 | struct sample_payload_t {}; 25 | } // namespace payload 26 | 27 | namespace message { 28 | using sample_payload_t = r::message_t; 29 | } 30 | 31 | std::atomic_bool my_flag{false}; 32 | 33 | TEST_CASE("throw in factory", "[spawner]") { 34 | auto system_context = rth::system_context_thread_t(); 35 | auto timeout = r::pt::milliseconds{100}; 36 | auto sup = system_context.create_supervisor() 37 | .timeout(timeout) 38 | .shutdown_flag(my_flag, r::pt::millisec{1}) 39 | .finish(); 40 | 41 | auto r = signal(SIGALRM, [](int) { my_flag = true; }); 42 | REQUIRE(r != SIG_ERR); 43 | 44 | alarm(1); 45 | 46 | sup->start(); 47 | system_context.run(); 48 | 49 | CHECK(my_flag == true); 50 | REQUIRE(static_cast(sup.get())->access() == r::state_t::SHUT_DOWN); 51 | } 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /tests/access.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "access.h" 8 | 9 | namespace rotor::test { 10 | 11 | bool empty(rotor::subscription_t &subs) noexcept { 12 | return subs.access().empty() && subs.access().empty(); 13 | } 14 | 15 | } // namespace rotor::test 16 | -------------------------------------------------------------------------------- /tests/actor_test.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #include "actor_test.h" 8 | #include "access.h" 9 | 10 | using namespace rotor::test; 11 | using namespace rotor; 12 | 13 | actor_test_t::~actor_test_t() { printf("~actor_test_t, %p(%p)\n", (void *)this, (void *)address.get()); } 14 | 15 | void actor_test_t::configure(plugin::plugin_base_t &plugin) noexcept { 16 | actor_base_t::configure(plugin); 17 | if (configurer) 18 | configurer(*this, plugin); 19 | } 20 | 21 | void actor_test_t::force_cleanup() noexcept { 22 | for (auto plugin : plugins) { 23 | plugin->access().clear(); 24 | } 25 | lifetime->access().clear(); 26 | } 27 | 28 | void actor_test_t::shutdown_finish() noexcept { 29 | actor_base_t::shutdown_finish(); 30 | if (shutdowner) 31 | shutdowner(*this); 32 | } 33 | -------------------------------------------------------------------------------- /tests/actor_test.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 3 | // 4 | // Distributed under the MIT Software License 5 | // 6 | 7 | #pragma once 8 | 9 | #include "rotor/actor_base.h" 10 | #include 11 | #include 12 | 13 | namespace rotor { 14 | namespace test { 15 | 16 | namespace payload { 17 | 18 | struct sample_t { 19 | int value; 20 | }; 21 | 22 | } // namespace payload 23 | 24 | namespace message { 25 | using sample_t = rotor::message_t; 26 | } 27 | 28 | struct actor_test_t; 29 | using plugin_configurer_t = std::function; 30 | using shutdown_fn_t = std::function; 31 | 32 | struct actor_test_t : public actor_base_t { 33 | using actor_base_t::actor_base_t; 34 | 35 | ~actor_test_t(); 36 | plugin_configurer_t configurer; 37 | shutdown_fn_t shutdowner; 38 | 39 | void configure(plugin::plugin_base_t &plugin) noexcept override; 40 | auto &get_plugins() const noexcept { return plugins; } 41 | auto get_activating_plugins() noexcept { return this->activating_plugins; } 42 | auto get_deactivating_plugins() noexcept { return this->deactivating_plugins; } 43 | 44 | auto &get_state() noexcept { return state; } 45 | 46 | void shutdown_finish() noexcept override; 47 | void force_cleanup() noexcept; 48 | }; 49 | 50 | } // namespace test 51 | } // namespace rotor 52 | -------------------------------------------------------------------------------- /tests/supervisor_asio_test.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | #include 9 | #include "rotor/asio/supervisor_asio.h" 10 | #include "access.h" 11 | 12 | namespace rotor { 13 | namespace test { 14 | 15 | struct supervisor_asio_test_t : public rotor::asio::supervisor_asio_t { 16 | using rotor::asio::supervisor_asio_t::supervisor_asio_t; 17 | 18 | timers_map_t &get_timers_map() noexcept { return timers_map; } 19 | state_t &get_state() noexcept { return state; } 20 | auto &get_leader_queue() { return access()->access(); } 21 | 22 | subscription_t &get_subscription() noexcept { return subscription_map; } 23 | }; 24 | 25 | } // namespace test 26 | } // namespace rotor 27 | -------------------------------------------------------------------------------- /tests/supervisor_wx_test.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2020 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/wx.hpp" 10 | #include "access.h" 11 | 12 | namespace rotor { 13 | namespace test { 14 | 15 | class RotorApp : public wxAppConsole { 16 | public: 17 | virtual ~RotorApp() override {} 18 | }; 19 | 20 | struct supervisor_wx_test_t : public rotor::wx::supervisor_wx_t { 21 | using rotor::wx::supervisor_wx_t::supervisor_wx_t; 22 | 23 | state_t &get_state() noexcept { return state; } 24 | auto &get_leader_queue() { return access()->access(); } 25 | subscription_t &get_subscription() noexcept { return subscription_map; } 26 | }; 27 | 28 | } // namespace test 29 | } // namespace rotor 30 | -------------------------------------------------------------------------------- /tests/system_context_test.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // 4 | // Copyright (c) 2019-2021 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com) 5 | // 6 | // Distributed under the MIT Software License 7 | // 8 | 9 | #include "rotor/system_context.h" 10 | 11 | namespace rotor { 12 | namespace test { 13 | 14 | struct system_context_test_t : public rotor::system_context_t { 15 | extended_error_ptr_t reason; 16 | 17 | system_context_test_t() {} 18 | 19 | inline virtual void on_error(actor_base_t *, const extended_error_ptr_t &ec_) noexcept override { reason = ec_; } 20 | }; 21 | 22 | } // namespace test 23 | } // namespace rotor 24 | --------------------------------------------------------------------------------