diff --git a/doc/modules/ROOT/pages/version-history.adoc b/doc/modules/ROOT/pages/version-history.adoc index ee1ac42a..da6ddb03 100644 --- a/doc/modules/ROOT/pages/version-history.adoc +++ b/doc/modules/ROOT/pages/version-history.adoc @@ -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 diff --git a/include/boost/msm/back/state_machine.hpp b/include/boost/msm/back/state_machine.hpp index 16776771..e43f365f 100644 --- a/include/boost/msm/back/state_machine.hpp +++ b/include/boost/msm/back/state_machine.hpp @@ -1983,7 +1983,7 @@ class state_machine : //public Derived 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; } diff --git a/include/boost/msm/back11/state_machine.hpp b/include/boost/msm/back11/state_machine.hpp index d398de9f..ad96a359 100644 --- a/include/boost/msm/back11/state_machine.hpp +++ b/include/boost/msm/back11/state_machine.hpp @@ -2010,7 +2010,7 @@ class state_machine : //public Derived 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; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 824a3d4a..6946096b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -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 diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 2f067c10..08292402 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -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 : : : msvc-14.3,32:no ] # GCC has a bug in std::type_info::before() that leads to a segfault. diff --git a/test/OrthogonalDeferred4.cpp b/test/OrthogonalDeferred4.cpp new file mode 100644 index 00000000..4e6758b8 --- /dev/null +++ b/test/OrthogonalDeferred4.cpp @@ -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 + +// 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 + void operator()(const Event&, Fsm& fsm, Source&, Target&) + { + fsm.action_counter++; + } +}; + +// Guards +struct Conditional +{ + template + bool operator()(const Event&, Fsm& fsm, Source&, Target&) + { + return fsm.conditional; + } +}; + +struct Machine_ : test::StateMachineBase_ +{ + typedef int activate_deferred_events; + + struct Deferring : state<> {}; + struct Acting : state<> {}; + + // Two orthogonal regions + typedef mpl::vector 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; + +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::value) + { + expected_action_counter = 1; + } +#endif + BOOST_REQUIRE(sm.action_counter == expected_action_counter); +} + +} // nmespace