├── .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 |
35 | $projectname
36 | $projectnumber
37 |
38 | $projectbrief
39 | |
40 |
41 |
42 |
43 |
44 | $projectbrief
45 | |
46 |
47 |
48 |
49 |
50 | $searchbox |
51 |
52 |
53 |
54 |
55 |
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