diff --git a/include/boost/msm/backmp11/state_machine.hpp b/include/boost/msm/backmp11/state_machine.hpp index 8efbe5b7..8d6787a3 100644 --- a/include/boost/msm/backmp11/state_machine.hpp +++ b/include/boost/msm/backmp11/state_machine.hpp @@ -1107,56 +1107,32 @@ class state_machine_base : public FrontEnd // Iteratively process all of the events within the deferred // queue upto (but not including) newly deferred events. - // if we did not defer one in the queue, then we need to try again - bool not_only_deferred = false; - while (!deferred_events.queue.empty()) + auto old_queue = std::move(deferred_events.queue); + deferred_events_queue_t left_queue; + deferred_events_queue_t right_queue; + while (!old_queue.empty()) { - deferred_event_t& deferred_event = - deferred_events.queue.front(); + deferred_event_t& deferred_event = old_queue.front(); if (deferred_events.cur_seq_cnt != deferred_event.seq_cnt) { + left_queue.insert(left_queue.end(), old_queue.begin(), old_queue.end()); break; } auto next = deferred_event.function; - deferred_events.queue.pop_front(); + old_queue.pop_front(); process_result res = next(); - if (res != process_result::HANDLED_FALSE && res != process_result::HANDLED_DEFERRED) + if (res & process_result::HANDLED_DEFERRED) { - not_only_deferred = true; + left_queue.push_back(std::move(deferred_events.queue.front())); + deferred_events.queue.pop_front(); } - if (not_only_deferred) - { - // handled one, stop processing deferred until next block reorders - break; - } - } - if (not_only_deferred) - { - // attempt to go back to the situation prior to processing, - // in case some deferred events would have been re-queued - // in that case those would have a higher sequence number - std::stable_sort( - deferred_events.queue.begin(), - deferred_events.queue.end(), - [](const deferred_event_t& a, const deferred_event_t& b) - { - return a.seq_cnt > b.seq_cnt; - } - ); - // reset sequence number for all - std::for_each( - deferred_events.queue.begin(), - deferred_events.queue.end(), - [&deferred_events](deferred_event_t& deferred_event) - { - deferred_event.seq_cnt = deferred_events.cur_seq_cnt + 1; - } - ); - // one deferred event was successfully processed, try again - try_process_deferred_events(true); + right_queue.insert(right_queue.end(), deferred_events.queue.begin(), deferred_events.queue.end()); + deferred_events.queue.clear(); } + deferred_events.queue = std::move(left_queue); + deferred_events.queue.insert(deferred_events.queue.end(), right_queue.begin(), right_queue.end()); } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a0809598..4ae303ef 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,37 +18,38 @@ endif() add_executable(boost_msm_tests EXCLUDE_FROM_ALL - AnonymousAndGuard.cpp - Anonymous.cpp - Back11CompositeMachine.cpp - BigWithFunctors.cpp - CompositeMachine.cpp - Constructor.cpp - Entries.cpp - EventQueue.cpp - History.cpp - KleeneDeferred.cpp - ManyDeferTransitions.cpp - OrthogonalDeferred2.cpp - OrthogonalDeferred3.cpp - OrthogonalDeferred.cpp - Serialize.cpp - SerializeWithHistory.cpp - SetStates.cpp - SimpleInternal.cpp - SimpleInternalFunctors.cpp - SimpleKleene.cpp - SimpleMachine.cpp - SimpleWithFunctors.cpp - Test2RegionsAnonymous.cpp - TestConstructor.cpp - TestConstructorMovableOnlyTypes.cpp - TestDeferAndMessageQueue2.cpp - TestDeferAndMessageQueue3.cpp - TestDeferAndMessageQueue.cpp - TestDeferIn2Regions.cpp - Throwing.cpp - TransitionSkipping.cpp + # AnonymousAndGuard.cpp + # Anonymous.cpp + # Back11CompositeMachine.cpp + # BigWithFunctors.cpp + # CompositeMachine.cpp + # Constructor.cpp + # Entries.cpp + # EventQueue.cpp + # History.cpp + # KleeneDeferred.cpp + # ManyDeferTransitions.cpp + # OrthogonalDeferred2.cpp + # OrthogonalDeferred3.cpp + # OrthogonalDeferred.cpp + # Serialize.cpp + # SerializeWithHistory.cpp + # SetStates.cpp + # SimpleInternal.cpp + # SimpleInternalFunctors.cpp + # SimpleKleene.cpp + # SimpleMachine.cpp + # SimpleWithFunctors.cpp + # Test2RegionsAnonymous.cpp + # TestConstructor.cpp + # TestConstructorMovableOnlyTypes.cpp + TestDefer.cpp + # TestDeferAndMessageQueue2.cpp + # TestDeferAndMessageQueue3.cpp + # TestDeferAndMessageQueue.cpp + # TestDeferIn2Regions.cpp + # Throwing.cpp + # TransitionSkipping.cpp main.cpp ) add_test(NAME boost_msm_tests COMMAND boost_msm_tests) diff --git a/test/TestDefer.cpp b/test/TestDefer.cpp new file mode 100644 index 00000000..789726dd --- /dev/null +++ b/test/TestDefer.cpp @@ -0,0 +1,106 @@ +// Copyright 2025 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) + +// back-end +#include "BackCommon.hpp" +// front-end +#include +#include + +#ifndef BOOST_MSM_NONSTANDALONE_TEST +#define BOOST_TEST_MODULE defer_test +#endif +#include + +namespace msm = boost::msm; +namespace front = msm::front; +using front::Row; +using front::Internal; +using front::none; +using front::state_machine_def; +namespace mp11 = boost::mp11; + +namespace { + +struct Event1 {}; +struct Event2 {}; +struct SwitchToStateHandleNone {}; +struct SwitchToStateHandleAll {}; + +struct Action +{ + template + void operator()(const Event&, Fsm& fsm, SourceState&, TargetState&) + { + fsm.action_calls++; + } +}; + +struct StateHandleAll : front::state<> {}; + +struct StateHandleNone : front::state<> {}; + +struct StateDeferEvent1 : front::state<> +{ + using deferred_events = mp11::mp_list; +}; + +struct StateDeferEvent1And2 : front::state<> +{ + using deferred_events = mp11::mp_list; +}; + +struct StateMachine_ : front::state_machine_def +{ + using initial_state = + mp11::mp_list; + + using transition_table = mp11::mp_list< + Row, + Row, + Row, + Row + >; + + size_t action_calls{}; +}; + +// Pick a back-end +using Fsms = mp11::mp_list< +#ifndef BOOST_MSM_TEST_SKIP_BACKMP11 + msm::backmp11::state_machine_adapter +#endif // BOOST_MSM_TEST_SKIP_BACKMP11 + // msm::back::state_machine + // back11 requires a const boost::any overload to identify the Kleene event. + // Leave it out of this test to ensure backwards compatibility. + // msm::back11::state_machine + >; + + +// TODO: +// Test case where an event is deferred in both regions. +BOOST_AUTO_TEST_CASE_TEMPLATE(defer_test, Fsm, Fsms) +{ + Fsm fsm; + + fsm.start(); + + fsm.process_event(Event1{}); + BOOST_REQUIRE(fsm.get_deferred_events_queue().size() == 2); + fsm.process_event(Event2{}); + BOOST_REQUIRE(fsm.get_deferred_events_queue().size() == 3); + + // fsm.process_event(Event1{}); + + fsm.stop(); +} + +} // namespace