From 61c4b17cb2402b94a3a01a6ec7ce44c5151b9a16 Mon Sep 17 00:00:00 2001 From: Christian Granzin Date: Thu, 18 Jun 2026 17:03:33 +0200 Subject: [PATCH 1/2] feat(backmp11): terminate state, interrupt state, machine state --- .../ROOT/attachments/backmp11/BackAdapter.cpp | 6 + .../backmp11/InterruptibleStateMachine.cpp | 134 ++++++++++++ .../backmp11/serializer/BoostJson.cpp | 6 +- .../backmp11/serializer/NlohmannJson.cpp | 12 +- doc/modules/ROOT/pages/backmp11-back-end.adoc | 67 +++--- .../pages/backmp11-back-end/examples.adoc | 12 +- doc/modules/ROOT/pages/version-history.adoc | 1 + include/boost/msm/backmp11/common_types.hpp | 6 +- .../backmp11/detail/state_machine_base.hpp | 12 +- .../msm/backmp11/detail/transition_table.hpp | 2 +- .../msm/backmp11/serialization/boost_json.hpp | 17 +- .../backmp11/serialization/nlohmann_json.hpp | 12 -- include/boost/msm/backmp11/state_machine.hpp | 192 +++++++++--------- test/Backmp11.hpp | 72 +++++++ test/Backmp11MachineState.cpp | 163 +++++++++++++++ test/Backmp11Serialization.cpp | 12 +- test/CMakeLists.txt | 1 + test/Jamfile.v2 | 1 + test/Utils.hpp | 116 +++++++++++ 19 files changed, 657 insertions(+), 187 deletions(-) create mode 100644 doc/modules/ROOT/attachments/backmp11/InterruptibleStateMachine.cpp create mode 100644 test/Backmp11MachineState.cpp diff --git a/doc/modules/ROOT/attachments/backmp11/BackAdapter.cpp b/doc/modules/ROOT/attachments/backmp11/BackAdapter.cpp index d006d63a..5a785167 100644 --- a/doc/modules/ROOT/attachments/backmp11/BackAdapter.cpp +++ b/doc/modules/ROOT/attachments/backmp11/BackAdapter.cpp @@ -171,6 +171,12 @@ class back_adapter : public boost::msm::backmp11::state_machine< template boost::msm::back::HandledEnum process_event(const Event& event) { + if (this->template is_flag_active() && + !this->is_end_interrupt_event(event)) + { + return boost::msm::back::HANDLED_TRUE; + } + if (this->get_machine_state() == boost::msm::backmp11::machine_state::processing) { diff --git a/doc/modules/ROOT/attachments/backmp11/InterruptibleStateMachine.cpp b/doc/modules/ROOT/attachments/backmp11/InterruptibleStateMachine.cpp new file mode 100644 index 00000000..96aff353 --- /dev/null +++ b/doc/modules/ROOT/attachments/backmp11/InterruptibleStateMachine.cpp @@ -0,0 +1,134 @@ +// Copyright 2026 Christian Granzin +// Copyright 2010 Christophe Henry +// henry UNDERSCORE christophe AT hotmail DOT com +// This is an extended version of the state machine available in the boost::mpl library +// Distributed under the same license as the original. +// Copyright for the original version: +// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed +// under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include + +namespace back = boost::msm::backmp11; +namespace front = boost::msm::front; +using front::Row; +using front::Internal; +namespace mp11 = boost::mp11; + +namespace +{ + +// Events. +struct InterruptEvent {}; +struct SmInternalEvent {}; + +// Actions. +struct Interrupt +{ + template + void operator()(Fsm& fsm) + { + fsm.interrupt(); + } +}; + +struct PrintMessage +{ + template + void operator()(const SmInternalEvent&, Fsm&) + { + if (talk) + { + std::cout << "Processed SmInternalEvent" << std::endl; + } + } + + [[maybe_unused]] static inline bool talk{true}; +}; + +// States. +struct MyState : front::state<> {}; + +struct MyOtherState : front::state<> {}; + +// State machine. +struct InterruptibleStateMachine_ + : front::state_machine_def +{ + using initial_state = MyState; + using transition_table = mp11::mp_list< + Row + >; + using internal_transition_table = mp11::mp_list< + Internal + >; +}; + +template +class InterruptibleStateMachine + : public back::state_machine> +{ + using Base = back::state_machine>; + + public: + using Base::Base; + + void interrupt() + { + m_interrupted = true; + } + + void resume() + { + m_interrupted = false; + this->process_event_pool(); + } + + template + back::process_result process_event(const Event& event) + { + if (m_interrupted) + { + // Enqueue all events for later processing + // as long as the state machine is interrupted. + this->enqueue_event(event); + return back::process_result::deferred; + } + else + { + return Base::process_event(event); + } + } + + private: + bool m_interrupted{}; +}; + + +[[maybe_unused]] void interruptible_state_machine_example() +{ + InterruptibleStateMachine<> state_machine; + + state_machine.start(); + + // Interrupt the state machine. + state_machine.process_event(InterruptEvent{}); + + // The event is not yet processed. + state_machine.process_event(SmInternalEvent{}); + + // Resuming the state machine processes the event and prints: + // "Processed SmInternalEvent" + state_machine.resume(); +} + +} // namespace + diff --git a/doc/modules/ROOT/attachments/backmp11/serializer/BoostJson.cpp b/doc/modules/ROOT/attachments/backmp11/serializer/BoostJson.cpp index c5207fd9..10124a5a 100644 --- a/doc/modules/ROOT/attachments/backmp11/serializer/BoostJson.cpp +++ b/doc/modules/ROOT/attachments/backmp11/serializer/BoostJson.cpp @@ -50,14 +50,14 @@ std::string to_boost_json_string(const DimSwitch& dim_switch) // The initial state is Off (state id 0). dim_switch.start(); // Prints: - // {"front_end":{"brightness":0},"states":{"1":{"times_pressed":0}},"active_state_ids":[0],"stopped":false} + // {"front_end":{"brightness":0},"states":{"1":{"times_pressed":0}},"active_state_ids":[0],"machine_state":1} std::cout << to_boost_json_string(dim_switch) << std::endl; // Turn On (state id 1) and set brightness to 75. dim_switch.process_event(TurnOn{}); dim_switch.process_event(Dim{75}); // Prints: - // {"front_end":{"brightness":75},"states":{"1":{"times_pressed":1}},"active_state_ids":[1],"stopped":false} + // {"front_end":{"brightness":75},"states":{"1":{"times_pressed":1}},"active_state_ids":[1],"machine_state":1} std::cout << to_boost_json_string(dim_switch) << std::endl; // Deserialize the json into a new state machine. @@ -65,7 +65,7 @@ std::string to_boost_json_string(const DimSwitch& dim_switch) const auto dim_switch_2 = boost::json::value_to(json); // Prints: - // {"front_end":{"brightness":75},"states":{"1":{"times_pressed":1}},"active_state_ids":[1],"stopped":false} + // {"front_end":{"brightness":75},"states":{"1":{"times_pressed":1}},"active_state_ids":[1],"machine_state":1} std::cout << to_boost_json_string(dim_switch_2) << std::endl; } diff --git a/doc/modules/ROOT/attachments/backmp11/serializer/NlohmannJson.cpp b/doc/modules/ROOT/attachments/backmp11/serializer/NlohmannJson.cpp index e0ef722b..5061f6ce 100644 --- a/doc/modules/ROOT/attachments/backmp11/serializer/NlohmannJson.cpp +++ b/doc/modules/ROOT/attachments/backmp11/serializer/NlohmannJson.cpp @@ -59,12 +59,12 @@ std::string to_nlohmann_json_string(const DimSwitch& dim_switch) // "front_end": { // "brightness": 0 // }, + // "machine_state": 1, // "states": { // "1": { // "times_pressed": 0 // } - // }, - // "stopped": false + // } // } std::cout << to_nlohmann_json_string(dim_switch) << std::endl; @@ -79,12 +79,12 @@ std::string to_nlohmann_json_string(const DimSwitch& dim_switch) // "front_end": { // "brightness": 75 // }, + // "machine_state": 1, // "states": { // "1": { // "times_pressed": 1 // } - // }, - // "stopped": false + // } // } std::cout << to_nlohmann_json_string(dim_switch) << std::endl; // Deserialize the json into a new state machine. @@ -100,12 +100,12 @@ std::string to_nlohmann_json_string(const DimSwitch& dim_switch) // "front_end": { // "brightness": 75 // }, + // "machine_state": 1, // "states": { // "1": { // "times_pressed": 1 // } - // }, - // "stopped": false + // } // } std::cout << to_nlohmann_json_string(dim_switch_2) << std::endl; } diff --git a/doc/modules/ROOT/pages/backmp11-back-end.adoc b/doc/modules/ROOT/pages/backmp11-back-end.adoc index 018bf005..d452e7f6 100644 --- a/doc/modules/ROOT/pages/backmp11-back-end.adoc +++ b/doc/modules/ROOT/pages/backmp11-back-end.adoc @@ -15,8 +15,8 @@ It offers a significant improvement in runtime and memory usage, as can be seen | back | 10 | 844 | 73 | 0.7 | back_favor_compile_time | 12 | 821 | 241 | 1.0 | back11 | 26 | 2675 | 92 | 0.7 -| backmp11 | 2 | 236 | 18 | 0.2 -| backmp11_favor_compile_time | 2 | 226 | 44 | 2.2 +| backmp11 | 2 | 236 | 19 | 0.2 +| backmp11_favor_compile_time | 2 | 225 | 44 | 2.2 | sml | 4 | 254 | 64 | 0.1 |======================================================================================================= @@ -28,9 +28,9 @@ It offers a significant improvement in runtime and memory usage, as can be seen | | Compile time / sec | Compile RAM / MB | Binary size / kB | Runtime / sec | back | 32 | 2160 | 252 | 3.7 | back_favor_compile_time | 37 | 1747 | 974 | 263 -| backmp11 | 5 | 362 | 55 | 0.9 -| backmp11_favor_compile_time | 3 | 266 | 96 | 7.5 -| sml | 13 | 612 | 460 | 2.8 +| backmp11 | 5 | 360 | 57 | 0.9 +| backmp11_favor_compile_time | 3 | 265 | 96 | 7.6 +| sml | 13 | 612 | 431 | 2.8 |======================================================================================================= @@ -173,7 +173,7 @@ When a transition defined in SM1 causes SM2 and SM3 to exit: === Event pool -The setting `event_pool` configures the container and inline storage the back-end uses to hold work that cannot be processed immediately: enqueued events, deferred events, and completion transitions. +The setting `event_pool` configures the container and inline storage the back-end uses to hold work that cannot be processed immediately: enqueued events, deferred events, completion transitions, and state machine termination. ```cpp template