fix(back, back11): event deferral in orthogonal regions

This commit is contained in:
Christian Granzin
2026-06-21 13:15:03 +02:00
committed by Christian Granzin
parent ebd17cc9f9
commit cac5d8d657
6 changed files with 106 additions and 3 deletions
@@ -20,6 +20,7 @@
** The deprecated APIs `process_queued_events()` and `process_single_queued_event()` are removed, use `process_event_pool(...)` instead
** The default active state switch policy is set to "after source exit" in compliance to the UML specification. The other options are not UML-compliant and will be removed (https://github.com/boostorg/msm/issues/222[#222])
** The back-end uses its own `process_result` type with new enum values instead of aliasing the enum of the `back` namespace. The old enum values are deprecated (https://github.com/boostorg/msm/issues/242[#242]) and will be removed
* fix(back, back11): Infinite recursion with event deferral in orthogonal regions (regression from 1.78) (https://github.com/boostorg/msm/issues/184[#184])
== Boost 1.91
+1 -1
View File
@@ -1983,7 +1983,7 @@ public:
deferred_fct next = pair.first;
m_events_queue.m_deferred_events_queue.pop_front();
boost::msm::back::execute_return res = next();
if (res != ::boost::msm::back::HANDLED_FALSE && res != ::boost::msm::back::HANDLED_DEFERRED)
if (res != ::boost::msm::back::HANDLED_FALSE && !(res & ::boost::msm::back::HANDLED_DEFERRED))
{
not_only_deferred = true;
}
+1 -1
View File
@@ -2010,7 +2010,7 @@ protected: // interface for the derived class
deferred_fct next = pair.first;
m_events_queue.m_deferred_events_queue.pop_front();
boost::msm::back::execute_return res = next();
if (res != ::boost::msm::back::HANDLED_FALSE && res != ::boost::msm::back::HANDLED_DEFERRED)
if (res != ::boost::msm::back::HANDLED_FALSE && !(res & ::boost::msm::back::HANDLED_DEFERRED))
{
not_only_deferred = true;
}
+2 -1
View File
@@ -34,9 +34,10 @@ add_executable(boost_msm_tests
History.cpp
KleeneDeferred.cpp
ManyDeferTransitions.cpp
OrthogonalDeferred.cpp
OrthogonalDeferred2.cpp
OrthogonalDeferred3.cpp
OrthogonalDeferred.cpp
OrthogonalDeferred4.cpp
Serialize.cpp
SerializeWithHistory.cpp
SetStates.cpp
+1
View File
@@ -49,6 +49,7 @@ test-suite msm-unit-tests
[ run OrthogonalDeferred.cpp ]
[ run OrthogonalDeferred2.cpp ]
[ run OrthogonalDeferred3.cpp ]
[ run OrthogonalDeferred4.cpp ]
# MSVC 32-bit runs out of memory when compiling this test
[ run OrthogonalDeferredEuml.cpp : : : <toolset>msvc-14.3,<address-model>32:<build>no ]
# GCC has a bug in std::type_info::before() that leads to a segfault.
+100
View File
@@ -0,0 +1,100 @@
// 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)
#ifndef BOOST_MSM_NONSTANDALONE_TEST
#define BOOST_TEST_MODULE orthogonal_deferred4_test
#endif
#include <boost/test/unit_test.hpp>
// back-end
#include "BackCommon.hpp"
//front-end
#include "FrontCommon.hpp"
namespace mpl = boost::mpl;
using namespace boost::msm::front;
namespace
{
// Events
struct Ping {};
// Actions
struct Action
{
template <typename Event, typename Fsm, typename Source, typename Target>
void operator()(const Event&, Fsm& fsm, Source&, Target&)
{
fsm.action_counter++;
}
};
// Guards
struct Conditional
{
template <typename Event, typename Fsm, typename Source, typename Target>
bool operator()(const Event&, Fsm& fsm, Source&, Target&)
{
return fsm.conditional;
}
};
struct Machine_ : test::StateMachineBase_<Machine_>
{
typedef int activate_deferred_events;
struct Deferring : state<> {};
struct Acting : state<> {};
// Two orthogonal regions
typedef mpl::vector<Deferring, Acting> initial_state;
using transition_table = mpl::vector<
// Source Event Target Action Guard
Row< Deferring, Ping, none, Defer , none>, // region 1: defer
Row< Acting , Ping, none, Action, Conditional> // region 2: handle conditionally
>;
size_t action_counter{};
bool conditional{};
};
using TestMachines = get_test_machines<Machine_>;
BOOST_AUTO_TEST_CASE_TEMPLATE(guard_reject_test, test_machine, TestMachines)
{
test_machine sm;
sm.start();
sm.process_event(Ping{});
BOOST_REQUIRE(sm.action_counter == 0);
}
BOOST_AUTO_TEST_CASE_TEMPLATE(handle_discard_test, TestMachine, TestMachines)
{
TestMachine sm;
sm.conditional = true;
sm.start();
sm.process_event(Ping{});
// Action called one times too many in old-backends
// (though behavior exists since 1.78).
size_t expected_action_counter = 2;
#ifndef BOOST_MSM_TEST_SKIP_BACKMP11
if (boost::msm::backmp11::is_backmp11_state_machine<TestMachine>::value)
{
expected_action_counter = 1;
}
#endif
BOOST_REQUIRE(sm.action_counter == expected_action_counter);
}
} // nmespace