Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 14 additions & 38 deletions include/boost/msm/backmp11/state_machine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

Expand Down
63 changes: 32 additions & 31 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
106 changes: 106 additions & 0 deletions test/TestDefer.cpp
Original file line number Diff line number Diff line change
@@ -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 <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>

#ifndef BOOST_MSM_NONSTANDALONE_TEST
#define BOOST_TEST_MODULE defer_test
#endif
#include <boost/test/unit_test.hpp>

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 <typename Event, typename Fsm, typename SourceState, typename TargetState>
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<Event1>;
};

struct StateDeferEvent1And2 : front::state<>
{
using deferred_events = mp11::mp_list<Event1, Event2>;
};

struct StateMachine_ : front::state_machine_def<StateMachine_>
{
using initial_state =
mp11::mp_list<StateHandleAll, StateDeferEvent1, StateDeferEvent1And2>;

using transition_table = mp11::mp_list<
Row<StateHandleNone , SwitchToStateHandleAll , StateHandleAll , none >,
Row<StateHandleAll , SwitchToStateHandleNone , StateHandleNone , none >,
Row<StateHandleAll , Event1 , none , Action>,
Row<StateHandleAll , Event2 , none , Action>
>;

size_t action_calls{};
};

// Pick a back-end
using Fsms = mp11::mp_list<
#ifndef BOOST_MSM_TEST_SKIP_BACKMP11
msm::backmp11::state_machine_adapter<StateMachine_>
#endif // BOOST_MSM_TEST_SKIP_BACKMP11
// msm::back::state_machine<StateMachine_>
// 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<Front>
>;


// 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
Loading