30 Commits ade4ce9198 ... 3529ddabd8

Author SHA1 Message Date
  Ivan Baidakou 3529ddabd8 update docs 2 years ago
  Ivan Baidakou daa9083223 remove #include <cstdio> 2 years ago
  Ivan Baidakou ec0b42f506 Mention DHT11 example 2 years ago
  Ivan Baidakou aee11de043 update docs 2 years ago
  Ivan Baidakou 86b41856f2 moar docs 2 years ago
  Ivan Baidakou 871c817934 Merge branch 'introduce-bsp' 2 years ago
  Ivan Baidakou fad061b55b update docs 2 years ago
  Ivan Baidakou 7e33393d8a update readme 2 years ago
  Ivan Baidakou dcfbe0ca15 minor fixes 2 years ago
  Ivan Baidakou e5507dd462 moar docs 2 years ago
  Ivan Baidakou bf03b28b6e moar docs 2 years ago
  Ivan Baidakou 409a5dcd3d moar docs 2 years ago
  Ivan Baidakou 7884a2ad76 moar docs 2 years ago
  Ivan Baidakou a6394bfd70 moar docs 2 years ago
  Ivan Baidakou a52e9e68b8 moar docs 2 years ago
  Ivan Baidakou 90c20a6ab0 moar docs 2 years ago
  Ivan Baidakou 1d55407253 moar docs 2 years ago
  Ivan Baidakou 7444698577 moar docs 2 years ago
  Ivan Baidakou 023aa46e90 Introduce ROTOR_LIGHT_FW_QUEUE 2 years ago
  Ivan Baidakou c0cf8e6970 Add css to docs 2 years ago
  Ivan Baidakou aa05704894 update docs 2 years ago
  Ivan Baidakou fb7e666039 introduce on_queue_full weak function (minor fixes) 2 years ago
  Ivan Baidakou 546280fc51 introduce on_queue_full weak function 2 years ago
  Ivan Baidakou 8829e2afd6 update docs 2 years ago
  Ivan Baidakou 029974718c add stm32f407g board example 2 years ago
  Ivan Baidakou dabfe72470 yet another minor speedup 2 years ago
  Ivan Baidakou fc1d3cd5de improve performance 2 years ago
  Ivan Baidakou b80091ddc4 add thread-safety for arms 2 years ago
  Ivan Baidakou 80d9b4c629 moar interrupt safety 2 years ago
  Ivan Baidakou 0387a8157d introduce bsp 2 years ago
10 changed files with 296 additions and 150 deletions
  1. 33 6
      CMakeLists.txt
  2. 48 12
      README.md
  3. 5 0
      docs/Changelog.md
  4. 35 13
      docs/Conception.md
  5. 2 1
      docs/Doxyfile.in
  6. 67 10
      docs/Introduction.md
  7. 105 107
      docs/Tutorial.md
  8. 1 1
      docs/actor-lifetime.drawio
  9. BIN
      docs/actor-lifetime.png
  10. 0 0
      docs/extra.css

+ 33 - 6
CMakeLists.txt

@@ -16,23 +16,45 @@ endif ()
 set(ROTOR_LIGHT_DOC OFF CACHE BOOL "generate docs using doxygen")
 set(ROTOR_LIGHT_ACTOR "uint64_t" CACHE STRING "ActorId type [default: uint64_t]")
 set(ROTOR_LIGHT_TIMEPOINT "int64_t" CACHE STRING "TimePoint type [default: int64_t]")
-set(ROTOR_LIGHT_DURATION "int32_t" CACHE STRING "Duration type [default: int32_t]")
+set(ROTOR_LIGHT_DURATION "std::int_fast32_t" CACHE STRING "Duration type [default: std::int_fast32_t]")
+set(ROTOR_LIGHT_MESSAGE "std::uint_fast16_t" CACHE STRING "Message id type [default: std::uint_fast16_t]")
+set(ROTOR_LIGHT_EVENT "std::uint16_t" CACHE STRING "Event id type [default: std::uint16_t]")
+set(ROTOR_LIGHT_INDEX "std::uint_fast8_t" CACHE STRING "Queue indexing type [default: std::uint_fast8_t]")
+set(ROTOR_LIGHT_FW_QUEUE "0" CACHE STRING "Default queue/priority for rotor light framework messages [default: 0]")
 
 include(CMakePrintHelpers)
 include(GenerateExportHeader)
 
+
+if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AVR")
+    set(ROTOR_LIGHT_BSP_DIR "avr")
+elseif ("${TOOLCHAIN_PREFIX}" MATCHES "^arm-.*")
+    set(ROTOR_LIGHT_BSP_DIR "arm")
+elseif ("${TOOLCHAIN_PREFIX}" STREQUAL "")
+    set(ROTOR_LIGHT_BSP_DIR "host")
+endif()
+
+
+set(ROTOR_LIGHT_BSP_PATH "include/rotor-light/bsp/${ROTOR_LIGHT_BSP_DIR}")
+add_library(rotor_light_bsp INTERFACE)
+target_include_directories(rotor_light_bsp INTERFACE
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/${ROTOR_LIGHT_BSP_PATH}>
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/${ROTOR_LIGHT_BSP_PATH}>
+    $<INSTALL_INTERFACE:include/${ROTOR_LIGHT_BSP_PATH}>
+)
+
 add_library(rotor_light
     src/rotor-light/actor.cpp
+    src/rotor-light/misc.cpp
     src/rotor-light/queue.cpp
     src/rotor-light/planner.cpp
     src/rotor-light/supervisor.cpp
 )
 
-target_include_directories(rotor_light
-    PUBLIC
-        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
-        $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
-        $<INSTALL_INTERFACE:include>
+target_include_directories(rotor_light PUBLIC
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
+    $<INSTALL_INTERFACE:include>
 )
 
 target_compile_features(rotor_light PUBLIC cxx_std_17)
@@ -45,7 +67,12 @@ target_compile_definitions(rotor_light PUBLIC
     "ROTOR_LIGHT_ACTOR=${ROTOR_LIGHT_ACTOR}"
     "ROTOR_LIGHT_TIMEPOINT=${ROTOR_LIGHT_TIMEPOINT}"
     "ROTOR_LIGHT_DURATION=${ROTOR_LIGHT_DURATION}"
+    "ROTOR_LIGHT_MESSAGE=${ROTOR_LIGHT_MESSAGE}"
+    "ROTOR_LIGHT_EVENT=${ROTOR_LIGHT_EVENT}"
+    "ROTOR_LIGHT_INDEX=${ROTOR_LIGHT_INDEX}"
+    "ROTOR_LIGHT_FW_QUEUE=${ROTOR_LIGHT_FW_QUEUE}"
 )
+target_link_libraries(rotor_light rotor_light_bsp)
 
 if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
     add_subdirectory("examples")

+ 48 - 12
README.md

@@ -1,13 +1,19 @@
-`rotor-light` is real-time platform-neutral, C++ actor micro-framework for
-embedded systems with supervising capabilities.
+[rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) is real-time
+platform-neutral, C++ actor micro-framework for embedded systems with
+supervising capabilities.
 
-`rotor-light` is built with the concurrency in the mind, i.e. independent
-entities (actors) interact each other via messages.
+[rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) is built with
+the concurrency in the mind, i.e. independent entities (actors) interact
+each other via messages.
 
-`rotor-light` is adoption of [rotor](https://github.com/basiliscos/cpp-rotor)
-framework for embedded systems. The framework has no platform-dependent
-code, so, it can, however, be used as is on host system, if you cautiously
-care about thread-safety.
+[rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) is adoption of
+[rotor](https://github.com/basiliscos/cpp-rotor) framework for embedded systems.
+The framework has no platform-dependent code, so, it can, however, be used as
+is on host system, if you cautiously care about thread-safety.
+
+## docs
+
+Tutorial, concepts and API reference can be found [here](https://basiliscos.neocities.org/rotor-light)
 
 ## features
 
@@ -32,9 +38,10 @@ care about thread-safety.
 
 |                       | messages/second | binary size
 |:---------------------:|:---------------:|:--------------:
-| host (1)              | ~55.3M          | 13143 bytes
-| STM32-H743ZI          | ~2.3M           | 14330 bytes
-| Arduino Uno R3 (2)    | ~15.8K          | 6546 bytes
+| host (1)              | ~64.4M          | 10379 bytes
+| STM32-H743ZI          | ~2.8M           | 14330 bytes
+| stm32f407g-discovery  | ~0.52M          | 4336 bytes
+| Arduino Uno R3 (2)    | ~29.5K          | 5714 bytes
 | xilinx microblaze (3) | ~58.8K          | 42868 byes
 
 All examples can be measured with `examples/ping-pong-throughput.cpp`,
@@ -44,8 +51,37 @@ compiled with `CMAKE_BUILD_TYPE=MinSizeRel` and the stripped
 
 (2) Build with `-DROTOR_LIGHT_ACTOR=uint8_t`
 
-(2) Zynq xc7z020clg400-1, QMTECH development board. Microblaze standard config, hw mul/div instr. enabled, clocking is 50Mhz.
+(3) Zynq xc7z020clg400-1, QMTECH development board. Microblaze standard
+config, hw mul/div instr. enabled, clocking is 50Mhz.
+
+The performance numbers are approximate and they are not constant. Actually
+message delivery depends on many factrors like number of queues,
+number of actors, number of supervisors, number of subscriptions.
+
+# examples
+
+There are `ping-pong` and `blink-led` examples for different platforms.
+If the examples are too trivial you can look at my [example]
+(https://notabug.org/basiliscos/rotor-light-playground-stm32) of using
+one-wire DHT11 sensor and UART for logging, everything is async &
+interrupt-based `stm32f407g-discovery` board.
+
+For `arduino` there is an [example]
+(https://notabug.org/basiliscos/rotor-light-playground-arduino) too.
 
 ## license
 
 MIT
+
+## telegram
+
+ru/en community for discussing
+[rotor](https://github.com/basiliscos/cpp-rotor)
+and [rotor-light](https://notabug.org/basiliscos/cpp-rotor-light)
+is available at [cpp_rotor](https://t.me/cpp_rotor) group.
+
+## Changelog
+
+### 0.01 (07-Sep-2022)
+ - initial version
+

+ 5 - 0
docs/Changelog.md

@@ -0,0 +1,5 @@
+# Changelog
+
+### 0.01 (07-Sep-2022)
+ - initial version
+

+ 35 - 13
docs/Conception.md

@@ -20,18 +20,22 @@ method to allow the framework do that.
 Actor does not react on the messages, when it is in the `off` state. 
 The `ActorBase::advance_stop()` method invocation does that. Actor can override
 the `advance_stop()` method and postpone the `ActorBase::advance_stop()` invokation
-when it will be ready, i.e. until the underlying hardware will be off. 
+when it is ready, i.e. until the underlying hardware is off.
 
 Likewise the `advance_init()` method can be overrided and the `ActorBase::advance_init()`
-can be postponed, until underlying hardware will be ready. 
+can be postponed, until underlying hardware is ready.
 
 The `advance_start()` method plays a bit different role: it is singalled from its 
-**supervisor** (see below), that everything is ready to start, i.e. all other actor's
-siblings, belonging to the same supervisor, and it is safe to send message to other
+**supervisor** (see below), that everything is ready to start (i.e. all other actor's
+siblings, belonging to the same supervisor) and it is safe to send a message to other
 actors as they are `operational`. In other words, it is the *cross-actor synchronization
 point*. When overriding, the `ActorBase::advance_start()` must be invoked to change
 the state.
 
+All `advance_*` methods are invoked automatically upon receiving
+[rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) messages, it is incorrect to
+invoke them manually.
+
 `actor` is designed to be recycleable, i.e. when it is shut down (`off` state), it can
 be started again. If an actor has defaults values, which are changed during its lifetime, 
 the overriden `initialize()` method is the proper place to reset the defaults.
@@ -40,9 +44,9 @@ There might be the case, then an actor cannot be initialized, i.e. due to hardwa
 failure. In that case, the `ActorBase::initialize()` **must not be invoked**, and
 the failure should be reported to its supervisor via the code like:
 
-```
-send<message::ChangeStateAck>(0, supervisor->get_id(), id, State::initialized, false);
-```
+~~~{.cpp}
+send<ctx::thread, message::ChangeStateAck>(0, supervisor->get_id(), id, State::initialized, false);
+~~~
 
 What to do with the failure, is the job of the *supervisor*.
 
@@ -61,7 +65,7 @@ in the `operational` state.
 
 Supervisor is a special kind of actor, which manages (or  "orchestrates", if you like)
 other actors (including supervisors), which it owns. Supervisor is responsible for
-**initializing**, **shutting down**, **synchronization** of actors and also handles
+**initializing**, **shutting down**, **synchronization** of actors and it also handles
 **actors failures**.
 
 Supervisor initialization (shut down) is simple: it becomes `initialized` (`off`)
@@ -80,7 +84,7 @@ and the fail policy, defined in the actor.
 
 The full list of fail policies is:
 
-```
+~~~{.cpp}
 enum class FailPolicy {
     restart        = 0b00000001,
     force_restart  = 0b00000011,
@@ -88,7 +92,7 @@ enum class FailPolicy {
     force_escalate = 0b00001100,
     ignore         = 0b00010000,
 };
-```
+~~~
 
 If actor stops in `operational` state this is the normal case, and supervisor does
 nothing, unless the actor policy is `force_restart` or `force_escalate`, which 
@@ -98,10 +102,10 @@ The actor **restart procedure** is simple: send it stop message (if the actor wa
 and then `initialize()` it and wait initialization confirmation (it's done via 
 `ActorBase::advance_init()`). 
 
-The actor **escalate procedure** is simple: supervisor stops all actors, and then shuts
+The actor **escalate procedure** is simple too: supervisor stops all actors, and then shuts
 self down too. If it was the root supervisor, it exits from `::process()` messages
-method too, as there are no more messages(). Then, usually, that leads to exiting
-from `main()` methon of the firmware which causes hardware restart..
+method too, as there are no more messages. Then, usually, that leads to exiting
+from `main()` method of the firmware which causes hardware restart.
 
 If actor fails in `initializing` state, and it's policy is `restart`, then supervisor
 performs the restart actor procedure. 
@@ -143,3 +147,21 @@ upstream, i.e. to root supervisor.
 If the root supervisor fails too... ok, it seems, that we have tried the best we can,
 and the final radical remedy left is just to *restart the whole board* (i.e. exit from
 `main()` entry point).
+
+
+## message priorities
+
+In `rotor_light` it is possitble to have multiple queues. Queues are indexed from 0,
+as usually, howerver queues are processed in the *reverse order*, so a queue with
+higher index is processed before any queue with lower index. This means that
+queue index is actually **queue priority**.
+
+~~~{.cpp}
+using Queue = rl::Queue<Storage, 15, 5>;
+~~~
+
+In the example above master queue consists of two sub-queues: first (index `0`) with
+storage for 15 messages, and the second (index `1`) with storage for 5 messages.
+Until there is any message in the second queue, it is processed; then it starts
+processing messages in the first queue.
+

+ 2 - 1
docs/Doxyfile.in

@@ -804,7 +804,8 @@ ALPHABETICAL_INDEX = no
 INPUT                  =  @CMAKE_CURRENT_SOURCE_DIR@/include                \
                           @CMAKE_CURRENT_SOURCE_DIR@/docs/Introduction.md   \
                           @CMAKE_CURRENT_SOURCE_DIR@/docs/Tutorial.md       \
-                          @CMAKE_CURRENT_SOURCE_DIR@/docs/Conception.md
+                          @CMAKE_CURRENT_SOURCE_DIR@/docs/Conception.md     \
+                          @CMAKE_CURRENT_SOURCE_DIR@/docs/Changelog.md
 
 
 # This tag can be used to specify the character encoding of the source files

+ 67 - 10
docs/Introduction.md

@@ -9,6 +9,9 @@ more actors) and user-defined payload. Messaging is send with *fire-and-forget*
 approach, i.e. sender does not care about further delivery and processing of the
 message, like with UDP protocol.
 
+Like in UDP a message can be "lost", because the destination queue if full.
+Unlike UDP, this can be checked, howerver.
+
 The message delivery is *asynchronous*, as the message is put into the queue, and
 will be delivered some time later.
 
@@ -18,22 +21,23 @@ With all that properties it is possible to assemble [concurrent](https://en.wiki
 failure handling. *Synchronization* makes it sure that starting sending messages to
 actors is performed when actors are ready. *Failure handling* allows to use different
 restart strategies of a failing actor, or group of actors, or even escalate locally
-unresolvable problem into upper layers... upto restarting the whole board restart.
+unresolvable problem into upper layers... upto the whole board restart.
 
-`rotor-light` is platform-neutral framework, i.e. it can be build and run on any
-plaform, which conforms the requirements (C++17, basically).
+[rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) is platform-neutral
+framework, i.e. it can be build and run on any plaform, which conforms the
+requirements (C++17, basically).
 
 # rotor and rotor-light
 
 There is a successful C++ actor microframework [rotor](https://github.com/basiliscos/cpp-rotor),
 when a few colleagues of mine asked me to have something similar for embedded systems.
 
-The following table highlight the differences
+The following table highlights the differences
 
 |                              |      rotor light        |  rotor
 |:----------------------------:|:-----------------------:|:----------------------:
-| max. number of actors        | 64                      | unlimited
-| max. number of messages      | compile-time defined    | unlimited
+| max. number of actors        | 64                      | unlimited, runtime
+| max. number of messages      | compile-time defined    | unlimited, runtime
 | thread-safety                | no                      | yes
 | message priorities           | yes                     | no
 | request-response pattern     | no                      | yes
@@ -49,7 +53,7 @@ The following table highlight the differences
 | uses RTTI                    | no                      | yes
 
 
-(1) `rotor-light` needs only "now" function from timer, the time point and
+(1) [rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) needs only "now" function from timer, the time point and
 time interval are abstract notions, have to be defined by end-user of the library.
 
 (2) Non-intrusiveness means, that a framework does not owns execution thread: when
@@ -64,7 +68,8 @@ it is worth familiarize self with it to get some insights.
 
 # building
 
-`rotor-light` uses [cmake](https://cmake.org/) build system. It has no dependencies,
+[rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) uses
+[cmake](https://cmake.org/) build system. It has no dependencies,
 except C++17 core libraries  (e.g. `<tuple>`,`<type_traits>` etc.). Basically, the
 framework can be build via
 
@@ -78,11 +83,34 @@ make -j4
 
 There are few customization options:
 
+ - `ROTOR_LIGHT_FW_QUEUE` - the queue/priority to be used for internal rotor-light
+messaging, `0` by default. This might be useful, to have a dedicated queue for
+[rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) messages, to avoid
+messages loss due to queue overfill, or have lower or higher priorities for
+[rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) messages.
+
+ - `ROTOR_LIGHT_ACTOR` - defines the type to be used as actor id, the default is
+`uint64_t`, which means the limitation of 64 actors in total. If you know a priory,
+that there are less than 32/16/8 actors in total, you can save bits here.
+
  - `ROTOR_LIGHT_TIMEPOINT` defines the type to be used as timepoint. The default
-value is `int64_t`
+value is `int64_t`.
 
  - `ROTOR_LIGHT_DURATION` defines the type to be used as time interval. The default
-value is `int32_t`
+value is `std::int_fast32_t`.
+
+ - `ROTOR_LIGHT_MESSAGE` - defines the type to be used as message type id, the default is
+`std::uint_fast16_t`; you can tune that if you have more than 2^16 different message
+**types**.
+
+ - `ROTOR_LIGHT_EVENT` defines the type to be used as event id. The default
+value is `std::uint16_t`; tune if there is a chance of having more than 2^16
+**active events** at one time.
+
+ - `ROTOR_LIGHT_INDEX` defines the type to be used as queue index. The default
+value is `std::uint_fast8_t`.
+
+ - `ROTOR_LIGHT_DOC` - generate doxygen docs, false by default.
 
 Usage example:
 
@@ -90,3 +118,32 @@ Usage example:
 cmake -DROTOR_LIGHT_TIMEPOINT=int32_t ..
 ```
 
+# error handling
+
+If any **internal** [rotor-light](https://notabug.org/basiliscos/cpp-rotor-light)
+message is not delivered, this is a critical error. In the case the following function
+is invoked in the `rotor_ligth` namespace:
+
+~~~{.cpp}
+namespace rotor_light {
+
+__attribute__((weak)) void on_queue_full() {
+  for (;;) {
+    __asm__("nop");
+  }
+}
+~~~
+
+If there is need to turn on red LED or something like that, then you should define
+your own function `void on_queue_full()` (without weak specifier) in the namespace.
+
+Custom message delivery faulure does not causes `on_queue_full()` invokation, it
+should be done manually if needed.
+
+A few advices to avoid the critical error:
+
+ - inrease queue size
+
+ - use different queue/prirority for `rotor_light` messages, if it is OK to loose
+your custom messeages, as it is fatal to loose `rotor_ligth` messages. See
+`ROTOR_LIGHT_FW_QUEUE` build option.

+ 105 - 107
docs/Tutorial.md

@@ -4,57 +4,53 @@
 
 This is platform-neural tutorial, although hardware implementation refers to
 arduino. The full code can be found at `examples/atmega328p/blink-led.cpp`. In
-this example the blinking with LED is performed using `rotor-light`.
+this example the blinking with LED is performed using
+[rotor-light](https://notabug.org/basiliscos/cpp-rotor-light).
 
 Let's start from the inclusion of headers:
 
-```
+~~~{.cpp}
 #include <rotor-light.hpp>
-```
+~~~
 
 All further interactions with the framework is done via `rl` namespace
 
-```
+~~~{.cpp}
 namespace rl = rotor_light;
-```
+~~~
 
 In this example, there is only single message - `BlinkCommand`. It is good
 practice to define messages in it's own namespace, as it is much more
 convenient to deal with messages later (handle and send).
 
-```
+~~~{.cpp}
 namespace message {
 
 struct BlinkCommand : rl::Message {            // (1)
   static constexpr auto type_id = __LINE__;    // (2)
   using rl::Message::Message;                  // (3)
-  rl::MessageTypeId get_type_id() const override {  // (4)
-    return type_id; }
-  };
-
 } // namespace message
-```
+~~~
 
-The `BlinkCommand` message have to be derived from `rotor_ligth` `Message` (1),
+The `BlinkCommand` message have to be derived from `rotor_light` `Message` (1),
 and as the `BlinkCommand` has no payload, it just reuses its parent
 constructor (`using rl::Message::Message`, (3)).
 
-As the `rotor_ligth` does not use RTTI it should somehow identify
-message types at runtime. That's why overridden `get_type_id()` method (4)
-should be available on every message type. `rotor_light` does not care
-how you, as the user, define your message type, the only requirement is the
-uniqueness of them. The easiest way to achieve that is just have line (2)
-in every message type definition, and group all used messages in a single
-header file.
+As the `rotor_light` does not use RTTI it should somehow identify
+message types at runtime, the `type_id` (2) field helps with that. 
+`rotor_light` does not care how you, as the user, define your message 
+type, the only requirement is the uniqueness of them. The easiest way to
+achieve that is just have line (2) in every message type definition, and 
+group all used messages in a single header file.
 
 Let's move to the blinker actor code; as it is rather small, it is shown
 entirely:
 
-```
+~~~{.cpp}
 struct Blinker : rl::Actor<2> {  // (5)
-using Parent = Actor<2>;  // (6)
+using Parent = Actor<2>;         // (6)
 
-void initialize() override {  // (7)
+void initialize() override {              // (7)
   subscribe(&Blinker::on_blink_command);  // (8)
   Parent::initialize();                   // (9)
 }
@@ -66,10 +62,10 @@ void initialize() override {  // (7)
   }
 
   void blink() {
-    toggle_led();                                        // (13)
-    add_event(delay, [](void *data) {                    // (14)
-        auto self = static_cast<Blinker *>(data);        // (15)
-        self->send<message::BlinkCommand>(0, self->id);  // (16)
+    toggle_led();                                                         // (13)
+    add_event<rl::ctx::thread>(delay, [](void *data) {                    // (14)
+        auto self = static_cast<Blinker *>(data);                         // (15)
+        self->send<rl::ctx::thread, message::BlinkCommand>(0, self->id);  // (16)
     }, this);
   }
 
@@ -79,7 +75,7 @@ void on_blink_command(message::BlinkCommand &msg) {  // (17)
 
 rl::Duration delay;  // (18)
 };
-```
+~~~
 
 First, your own actor have to inherit from `rl::Actor` (5). The magic number `2` is
 used for preallocation of space for actor's message handlers: one handler from
@@ -103,12 +99,18 @@ using zero-priority queue. The `id` parameter is the destination actor address;
 which is the `Blinker` (self) address here. The destination address can also be
 multiple actors (i.e. it is mask).
 
+The `rl::ctx::thread` in (13) and (16) gives hints, wether interrupts should 
+(`ctx::thread`) or not (`ctx::interrupt`) be masked during methods invokations.
+The `ctx::thread` masks and then unmasks all interrupts, so, generally it
+should not be called from interrupt context. The last one (`ctx::interrupt`) 
+does not touches CPU interrupt enablence flag. 
+
 Each time the `BlinkCommand` is received (17), the `blink()` method is invoked, and
 the procedure above repeats again.
 
 Next, the application-wide compile-time configuration should be made:
 
-```
+~~~{.cpp}
 using Storage = rl::traits::MessageStorage<rl::message::ChangeState,    // (18)
                                            rl::message::ChangeStateAck,
                                            message::BlinkCommand>;
@@ -117,44 +119,48 @@ using Planner = rl::Planner<2>;      /* upto 2 time events */           // (20)
 using Supervisor = rl::Supervisor<                                      // (21)
                             rl::SupervisorBase::min_handlers_amount,
                             Blinker>;
-```
+~~~
 
-`rotor-light` uses queues to store and process messages. There are no dynamic
-allocations, so, all sizes have to be known at compile-time, including the sizes
-of all used messages. The `Storage` helper (18) is used to determine the maximum
-allowed space size per message: here **all messages** in the application
-should be enumerated, i.e. messages from the framework and user-defined.
+[rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) uses queues to
+store and process messages. There are no dynamic allocations, so, all sizes have
+to be known at compile-time, including the sizes of all used messages. The
+`Storage` helper (18) is used to determine the maximum allowed space size per
+message: here **all messages** in the application should be enumerated, i.e.
+messages from the framework and user-defined.
 
 Next, the application queue (or master queue) should be defined (19). It uses
 `Storage` and the magic number `5`, which pre-allocates space for queue with
-**zero-priority** for 5 messages. All `rotor-light` messages are put into that
-queue, but user-defined messages might use different queues. Queues with
-higher priorities are processed earlier than queues with lower priorities. So,
-to define master queue with two subqueues (priorities 0 and 1) with sizes
-enough to store 15 and 10 messages respectively, the following code should
-be used
-
-```
+**zero-priority** for 5 messages. All [rotor-light](https://notabug.org/basiliscos/cpp-rotor-light)
+messages are put into that queue by default, user-defined messages might use
+different queues. Queues with higher priorities are processed earlier than
+queues with lower priorities. So, to define master queue with two subqueues
+(priorities 0 and 1) with sizes enough to store 15 and 10 messages respectively,
+the following code should be used
+
+~~~{.cpp}
 using Queue = rl::Queue<Storage, 15, 10>;
-```
+~~~
 
 What is the recommended queue sizes? Well, it is completely application defined, but
-for `rotor-light` messages there should be enough space to store `N * 2` messages,
-where `N` is the total number of actors (including supervisors).
+for [rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) messages there
+should be enough space to store `N * 2` messages, where `N` is the total number of actors (including supervisors).
 
 What happens, if a queue is full? The next send message is simply discarded, you'll
 be notified about that as the `send()` method returns `false`. If the discarded
-message is `rotor-light` message, then *the framework behavior is undefined*. So,
-either do not overload default queue (i.e. with zero-priority), or use different
-queues for user-messages. NB: there is a spike of `rotor-light` messages only
-during (re)starting supervisor; when everything is *operational* there is almost no
-framework messages.
+message is [rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) message,
+then *the framework behavior is undefined* (more strictly speaking, overridable
+`void rotor_light::on_queue_full()` method is invoked). So, either do not overload
+default queue (i.e. with zero-priority), or use different  queues for user-messages.
+NB: there is a spike of [rotor-light](https://notabug.org/basiliscos/cpp-rotor-light)
+messages only during (re)starting supervisor; when everything is *operational*
+there is almost no framework messages.
 
 The Planner (20) is used to schedule future events, like it is shown at `add_event`
-(14). The number in angle braces defines planner capacity at compile-time. When event
-time arrives, it is removed from the planner. Please note, that `rotor-light` does
-not schedules any events into the planner, so its capacity and usage is completely
-controlled by the user code.
+(13). The number in angle braces (20) defines planner capacity at compile-time. When
+event time arrives, it is removed from the planner. Please note, that
+[rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) does not schedules
+any events into the planner, so its capacity and usage is completely controlled by
+the user code.
 
 How the framework interacts with the planner? If the root supervisor is used in
 *poll mode*, then, when there are no more messages left, the current time is
@@ -162,7 +168,7 @@ requested, and all timed out event handlers are executed. Then the special messa
 `refresh-time` is send to the supervisor and the procedure repeats endlessly.
 Indirectly, this is similar to the code like:
 
-```
+~~~{.cpp}
 while (true) {
   while (has_messages()) {
     auto& message = get_front_message();
@@ -177,7 +183,7 @@ while (true) {
     pop_event(event);
   }
     
-```
+~~~
 
 If the *await mode* is used, then user code should implement the whole `while` loop
 above and the event awaiting (22) code. This makes it possible to enter into
@@ -189,15 +195,15 @@ In the line (21) the used supervisor is defined: the amount of handlers is speci
 child actors (including other supervisors) are enumerated. The only `Blinker`
 child actor is specified in the current example.
 
-Let's move forward. In the following code `rotor-light` global variables are
-allocated:
+Let's move forward. In the following code [rotor-light](https://notabug.org/basiliscos/cpp-rotor-light)
+global variables are allocated:
 
-```
+~~~{.cpp}
 Queue queue;
 Planner planner;
 rl::Context context{&queue, &planner, &get_now};
 Supervisor sup;
-```
+~~~
 
 All of them can be allocated on the stack, but for the supervisor it is, probably,
 the bad idea, because most likely you'll need to access actors from some other
@@ -207,12 +213,12 @@ all its child actors, i.e. *supervisor contains and owns child actors*.
 
 The `get_now()` function pointer refers to a function with the following signature:
 
-```
+~~~{.cpp}
 using TimePoint = int64_t;
 using NowFunction = TimePoint (*)();
-```
+~~~
 
-It is the only link to the underlying hardware or system. The function should return
+It is a link to the underlying hardware or system. The function should return
 current time; the meaning of "current time" (`TimePoint`) is completely user
 specific (i.e. it can point to seconds, milliseconds, microseconds etc.). The only
 requirement to the function, that it should be **monotonic**, i.e. do not decrease
@@ -222,7 +228,7 @@ example it returns the number of microseconds passed since board boot.
 
 The final piece of the current example is:
 
-```
+~~~{.cpp}
 int main(int, char **) {
   app_hw_init();
 
@@ -237,8 +243,7 @@ int main(int, char **) {
   sup.process();                            // (27)
   return 0;
 }
-
-```
+~~~
 
 The context binding (23) let the supervisor know pre-allocated queues, planner and
 the `get_now()` function. During context binding all actors get their unique ids,
@@ -261,38 +266,35 @@ failure, there is nothing left to do than exit and the whole board restart.
 
 ## ping-pong messaging, poll timer
 
-In this example how to do messaging with `rotor-light` is demonstrated: `ping`
-message is sent from `pinger` actor to `ponger` actor. The `ponger` actor will
-reply back with `pong`  message, then after some delay `pinger` actor repeats
-the same procedure. The full code can be found at
+In this example how to do messaging with [rotor-light](https://notabug.org/basiliscos/cpp-rotor-light)
+is demonstrated: `ping` message is sent from `pinger` actor to `ponger` actor.
+The `ponger` actor will reply back with `pong`  message, then after some delay
+`pinger` actor repeats the same procedure. The full code can be found at
 `examples/atmega328p/ping-pong-poll.cpp`.
 
 First of all used messages should be defined:
 
-```
+~~~{.cpp}
 namespace message {
 struct Ping : rl::Message {
   using Message::Message;
   static constexpr auto type_id = __LINE__;
-  rl::MessageTypeId get_type_id() const override { return type_id; }
 };
 
 struct Pong : rl::Message {
   using Message::Message;
   static constexpr auto type_id = __LINE__;
-  rl::MessageTypeId get_type_id() const override { return type_id; }
 };
 } // namespace message
-
-```
+~~~
 
 The `ping` and `pong` messages are content-less, why there is need of them for all?
 Because there is need to demonstrate how to send and receive messages and
-distinguish them by type.
+distinguish them **by type**.
 
 The `Pinger` actor code is:
 
-```
+~~~{.cpp}
 struct Pinger : rl::Actor<2> {
   using Parent = Actor<2>;
 
@@ -306,26 +308,24 @@ struct Pinger : rl::Actor<2> {
     ping();                        // (31)
   }
 
-  void ping() {                         // (32)
-    /* toggle led */
-    PORTB ^= (1 << LED);
-    send<message::Ping>(0, ponger_id);  // (33)
+  void ping() {                                          // (32)
+    Board::toggle_led();
+    send<rl::ctx::thread, message::Ping>(0, ponger_id);  // (33)
   }
 
-  void on_pong(message::Pong &) {          // (34)
-    add_event(500000, [](void *data) {     // (35)
+  void on_pong(message::Pong &) {                           // (34)
+    add_event<rl::ctx::thread>(500000, [](void *data) {     // (35)
       static_cast<Pinger *>(data)->ping();
     }, this);
   }
 
   rl::ActorId ponger_id;   // (36)
 };
-
-```
+~~~
 
 As usually, the `initialize()` should be overridden to subscribe on `pong` messages
 (28). The `pinger` actor plays an *active role*, i.e. it sends initial `ping`
-message. This is performed in the overrisden `advance_start()` method (29), which is
+message. This is performed in the overridden `advance_start()` method (29), which is
 invoked as soon as the actor is ready: the default implementation machinery is
 invoked (30), and for convenience `ping()` (31) method is called. The `ping()` (32)
 method implementation is simple: after LED toggle, it sends the `ping` message (33).
@@ -340,7 +340,7 @@ is rescheduled after 500000 microseconds (i.e. 0.5 second) at (35).
 
 The `ponger` actor code is rather trivial:
 
-```
+~~~{.cpp}
 struct Ponger : rl::Actor<2> {
   using Parent = Actor<2>;
 
@@ -349,12 +349,11 @@ struct Ponger : rl::Actor<2> {
     Parent::initialize();
   }
   void on_ping(message::Ping &) {
-    send<message::Pong>(0, pinger_id);  // (37)
+    send<rl::ctx::thread, message::Pong>(0, pinger_id);  // (37)
   }
   rl::ActorId pinger_id;   // (38)
 };
-
-```
+~~~
 
 as soon `ping` message is received, it replies back (37) with `pong` message. Please
 note, while conceptually it "replies back", technically it just sends a new `pong`
@@ -363,7 +362,7 @@ request-response pattern, i.e. it "knows" whom to send back the "reply".
 
 Lets move forward.
 
-```
+~~~{.cpp}
 using Supervisor = rl::Supervisor<
     rl::SupervisorBase::min_handlers_amount,
     Pinger,
@@ -373,13 +372,13 @@ using Storage = rl::traits::MessageStorage<rl::message::ChangeState,
                                            rl::message::ChangeStateAck,
                                            message::Ping,
                                            message::Pong>;
-```
+~~~
 
 The standard supervisor is used; it owns `pinger` and `ponger` actors. The `Storage`
 allocates enough space for `Ping` and `Pong` messages.
 
 
-```
+~~~{.cpp}
 int main(int, char **) {
 
   app_hw_init();
@@ -397,19 +396,20 @@ int main(int, char **) {
   sup.process();   // (43)
   return 0;
 }
-```
+~~~
 
 The most interesting part is the setup-phase (39-40). `pinger` and `ponger` actors
 should know each others addresses, and the addresses are available only after
 context binding.
 
 When everything is ready, it enters into main loop (43). In the loop it either
-delivers `rotor-light` messages or waits, until the next event time occurs. It uses
-**busy waiting** by actively polling (querying) timer, whether the next event time
-happend. As usually for busy waiting, it consumes 100% CPU time, which is common
-strategy for embedded/real-time applications.
+delivers [rotor-light](https://notabug.org/basiliscos/cpp-rotor-light) messages
+or waits, until the next event time occurs. It uses **busy waiting** by actively
+polling (querying) timer, whether the next event time happend. As usually for
+busy waiting, it consumes 100% CPU time, which is common strategy for
+embedded/real-time applications.
 
-It is possible however to do something else, instead of endless timer polling, i.e.
+It is possible however to do something else, instead of endless timer polling, e.g.
 do CPU sleep (and consume less current and do less CO2 emission). See the
 next section.
 
@@ -423,12 +423,12 @@ delay the procedure repeats.
 However, the notable difference is in the delay: in the previous example it
 endlessly polls the timer whether the time arrives for the next event, burning
 CPU cycles, in the current example it does energy-efficient sleeping while
-there is noting to do (i.e. no messages) between events.
+there is nothing to do (i.e. no messages) between events.
 
 As the main logic is the same, the actor-related code is also the same; only
 `main()` function differs. 
 
-```
+~~~{.cpp}
 int main(int, char **) {
   app_hw_init();
 
@@ -452,13 +452,12 @@ int main(int, char **) {
     sup.process();                                 // (46)
     auto next_event_time = planner.next_event();   // (47)
     if (next_event_time) {                         // (48)
-      perform_sleep(next_event_time);              // (49)
+      Board::sleep(next_event_time);               // (49)
     }
   }
   return 0;
 }
-
-```
+~~~
 
 Firstly, the timer poll via recursively sending self `refresh-timer` message should
 be disabled (44). Then the infinite loop (45) should be started, as the supervisor
@@ -480,7 +479,7 @@ the powersave mode.
 Then second way is to have a custom supervisor with `on_refhesh_timer` method
 overridden, like that:
 
-```
+~~~{.cpp}
 struct MySupervisor : rl::Supervisor<3, MyActor1, MyActor2, ...> {
 using Parent = rl::Supervisor<3, MyActor1, MyActor2, ...>; 
 
@@ -490,5 +489,4 @@ void on_refhesh_timer(rl::message::RefreshTime &message) override {
 }
 
 };
-
-```
+~~~

File diff suppressed because it is too large
+ 1 - 1
docs/actor-lifetime.drawio


BIN
docs/actor-lifetime.png


+ 0 - 0
docs/extra.css


Some files were not shown because too many files changed in this diff