2
0
mirror of https://github.com/boostorg/msm.git synced 2026-01-19 04:22:11 +00:00

refactor(backmp11): more efficient defer and enqueue handling

This commit is contained in:
Christian Granzin
2025-12-18 19:05:15 -05:00
committed by Christian Granzin
parent a10ec92f25
commit 155b3ef6d6
6 changed files with 233 additions and 110 deletions

View File

@@ -21,8 +21,8 @@ It offers a significant reduction in compilation time and RAM usage, as can be s
| back | 18 | 953 | 7
| back_favor_compile_time | 21 | 1000 | 8
| back11 | 43 | 2794 | 7
| backmp11 | 3 | 239 | 3
| backmp11_favor_compile_time | 3 | 220 | 13
| backmp11 | 3 | 229 | 3
| backmp11_favor_compile_time | 3 | 220 | 8
| sml | 12 | 363 | 3
|=======================================================================
@@ -34,9 +34,9 @@ It offers a significant reduction in compilation time and RAM usage, as can be s
| | Compile / sec | RAM / MB | Runtime / sec
| back | 68 | 2849 | 23
| back_favor_compile_time | 80 | 2551 | 261
| backmp11 | 11 | 472 | 10
| backmp11_favor_compile_time | 7 | 288 | 40
| backmp11_favor_compile_time_multi_cu | 5 | ~919 | 40
| backmp11 | 10 | 438 | 11
| backmp11_favor_compile_time | 7 | 288 | 26
| backmp11_favor_compile_time_multi_cu | 5 | ~919 | 26
| sml | 48 | 1128 | 11
|================================================================================

View File

@@ -14,6 +14,7 @@
#include <any>
#include <boost/msm/back/common_types.hpp>
#include <cstddef>
namespace boost { namespace msm { namespace backmp11
{
@@ -23,8 +24,7 @@ using process_result = back::HandledEnum;
using any_event = std::any;
// Selector for the visit mode.
// Can be active_states or all_states in recursive
// or non-recursive mode.
// Can be active_states or all_states in recursive or non-recursive mode.
enum class visit_mode
{
// State selection (mutually exclusive).
@@ -51,6 +51,39 @@ constexpr visit_mode operator|(visit_mode lhs, visit_mode rhs)
namespace detail
{
class enqueued_event
{
public:
virtual ~enqueued_event() = default;
// Process the event.
virtual process_result process() = 0;
};
class deferred_event
{
public:
virtual ~deferred_event() = default;
// Process the event.
virtual process_result process() = 0;
// Report whether the event is currently deferred.
virtual bool is_deferred() const = 0;
// Get the sequence counter of the event.
size_t get_seq_cnt() const
{
return m_seq_cnt;
}
protected:
deferred_event(size_t seq_cnt) : m_seq_cnt(seq_cnt) {}
private:
size_t m_seq_cnt;
};
using EventSource = back::EventSourceEnum;
using back::HandledEnum;
@@ -62,7 +95,57 @@ constexpr EventSource operator|(EventSource lhs, EventSource rhs)
);
}
}
// Minimal implementation of a unique_ptr.
// Used to keep header dependencies to a minimum
// (from C++20 the memory header includes ostream).
template <typename T>
class basic_unique_ptr
{
public:
explicit basic_unique_ptr(T* ptr) : m_ptr(ptr)
{
}
~basic_unique_ptr()
{
delete m_ptr;
}
basic_unique_ptr(const basic_unique_ptr&) = delete;
basic_unique_ptr& operator=(const basic_unique_ptr&) = delete;
basic_unique_ptr(basic_unique_ptr&& other) : m_ptr(other.m_ptr)
{
other.m_ptr = nullptr;
}
basic_unique_ptr& operator=(basic_unique_ptr&& other)
{
delete m_ptr;
m_ptr = other.m_ptr;
other.m_ptr = nullptr;
}
T* get() const
{
return m_ptr;
}
T& operator*() const
{
return *m_ptr;
}
T* operator->() const
{
return m_ptr;
}
private:
T* m_ptr;
};
} // namespace detail
}}} // namespace boost::msm::backmp11

View File

@@ -9,8 +9,8 @@
// file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_MSM_BACKMP11_FAVOR_RUNTIME_SPEED_H
#define BOOST_MSM_BACKMP11_FAVOR_RUNTIME_SPEED_H
#ifndef BOOST_MSM_BACKMP11_DETAIL_FAVOR_RUNTIME_SPEED_H
#define BOOST_MSM_BACKMP11_DETAIL_FAVOR_RUNTIME_SPEED_H
#include <boost/msm/backmp11/detail/metafunctions.hpp>
#include <boost/msm/backmp11/detail/dispatch_table.hpp>
@@ -62,22 +62,65 @@ struct compile_policy_impl<favor_runtime_speed>
return sm.process_event_internal_impl(event, source);
}
template <typename Event, typename StateMachine>
static bool is_event_deferred(StateMachine& sm)
{
bool result = false;
auto visitor = [&result](auto& state)
{
using State = std::decay_t<decltype(state)>;
result |= has_state_deferred_event<State, Event>::value;
};
sm.template visit<visit_mode::active_non_recursive>(visitor);
return result;
}
template <typename StateMachine, typename Event>
static bool is_event_deferred(StateMachine& sm, const Event&)
class deferred_event_impl : public deferred_event
{
return is_event_deferred<Event>(sm);
public:
deferred_event_impl(StateMachine& sm, const Event& event, size_t seq_cnt)
: deferred_event(seq_cnt), m_sm(sm), m_event(event)
{
}
process_result process() override
{
return process_event_internal(
m_sm,
m_event,
EventSource::EVENT_SOURCE_DEFERRED);
}
bool is_deferred() const override
{
return is_event_deferred(m_sm, m_event);
}
private:
StateMachine& m_sm;
Event m_event;
};
template <class State, class Event>
using has_deferred_event = mp11::mp_contains<
to_mp_list_t<typename State::deferred_events>,
Event
>;
template <typename StateMachine, typename Event>
class is_event_deferred_helper
{
public:
static bool execute(const StateMachine& sm)
{
bool result = false;
auto visitor = [&result](const auto& /*state*/)
{
result = true;
};
sm.template visit_if<defers_event,
visit_mode::active_non_recursive>(visitor);
return result;
}
private:
template <typename State>
using defers_event = has_deferred_event<State, Event>;
};
template <typename StateMachine, typename Event>
static bool is_event_deferred(const StateMachine& sm, const Event&)
{
using helper = is_event_deferred_helper<StateMachine, Event>;
return helper::execute(sm);
}
template <typename StateMachine, typename Event>
@@ -117,22 +160,9 @@ struct compile_policy_impl<favor_runtime_speed>
static void do_defer_event(StateMachine& sm, const Event& event)
{
auto& deferred_events = sm.get_deferred_events();
deferred_events.queue.push_back(
{
[&sm, event]()
{
return process_event_internal(
sm,
event,
EventSource::EVENT_SOURCE_DEFERRED);
},
[&sm]()
{
return is_event_deferred<Event>(sm);
},
deferred_events.cur_seq_cnt
}
);
deferred_events.queue.push_back(basic_unique_ptr<deferred_event>{
new deferred_event_impl<StateMachine, Event>(
sm, event, deferred_events.cur_seq_cnt)});
}
struct cell_initializer
@@ -147,13 +177,6 @@ struct compile_policy_impl<favor_runtime_speed>
}
};
// returns a mp11::mp_bool<true> if State has Event as deferred event
template <class State, class Event>
using has_state_deferred_event = mp11::mp_contains<
to_mp_list_t<typename State::deferred_events>,
Event
>;
template<typename Fsm, typename State>
struct table_index
{
@@ -483,4 +506,4 @@ struct compile_policy_impl<favor_runtime_speed>
}}} // boost::msm::backmp11
#endif //BOOST_MSM_BACKMP11_FAVOR_RUNTIME_SPEED_H
#endif // BOOST_MSM_BACKMP11_DETAIL_FAVOR_RUNTIME_SPEED_H

View File

@@ -202,10 +202,16 @@ struct get_event_id
};
template <class State>
using has_state_deferred_events = mp11::mp_not<
using has_deferred_events = mp11::mp_not<
mp11::mp_empty<to_mp_list_t<typename State::deferred_events>>
>;
template <class State, class Event>
using has_deferred_event = mp11::mp_contains<
to_mp_list_t<typename State::deferred_events>,
Event
>;
// Template used to create dummy entries for initial states not found in the stt.
template< typename T1 >
struct not_a_row

View File

@@ -73,6 +73,33 @@ struct compile_policy_impl<favor_compile_time>
return sm.process_event_internal_impl(event, source);
}
template <typename StateMachine>
class deferred_event_impl : public deferred_event
{
public:
deferred_event_impl(StateMachine& sm, const any_event& event, size_t seq_cnt)
: deferred_event(seq_cnt), m_sm(sm), m_event(event)
{
}
process_result process() override
{
return process_event_internal(
m_sm,
m_event,
EventSource::EVENT_SOURCE_DEFERRED);
}
bool is_deferred() const override
{
return is_event_deferred(m_sm, m_event);
}
private:
StateMachine& m_sm;
any_event m_event;
};
template <typename State>
static const std::unordered_set<std::type_index>& get_deferred_event_type_indices()
{
@@ -94,43 +121,26 @@ struct compile_policy_impl<favor_compile_time>
}
template <typename StateMachine>
static bool is_event_deferred(StateMachine& sm, std::type_index type_index)
static bool is_event_deferred(const StateMachine& sm, const any_event& event)
{
const std::type_index type_index = event.type();
bool result = false;
auto visitor = [&result, &type_index](auto& state) {
auto visitor = [&result, type_index](auto& state) {
using State = std::decay_t<decltype(state)>;
auto& set = get_deferred_event_type_indices<State>();
const auto& set = get_deferred_event_type_indices<State>();
result |= (set.find(type_index) != set.end());
};
sm.template visit<visit_mode::active_non_recursive>(visitor);
sm.template visit_if<has_deferred_events,
visit_mode::active_non_recursive>(visitor);
return result;
}
template <typename StateMachine>
static bool is_event_deferred(StateMachine& sm, const any_event& event)
{
return is_event_deferred(sm, event.type());
}
template <typename StateMachine>
static void defer_event(StateMachine& sm, any_event const& event)
{
auto& deferred_events = sm.get_deferred_events();
deferred_events.queue.push_back(
{
[&sm, event]()
{
return process_event_internal(
sm,
event,
EventSource::EVENT_SOURCE_DEFERRED);
},
[&sm, type_index = std::type_index{event.type()}]()
{
return is_event_deferred(sm, type_index);
},
deferred_events.cur_seq_cnt
}
);
deferred_events.queue.push_back(basic_unique_ptr<deferred_event>{
new deferred_event_impl(sm, event, deferred_events.cur_seq_cnt)});
}
template<typename Stt>

View File

@@ -38,10 +38,10 @@
#include <boost/msm/active_state_switching_policies.hpp>
#include <boost/msm/row_tags.hpp>
#include <boost/msm/backmp11/common_types.hpp>
#include <boost/msm/backmp11/detail/history_impl.hpp>
#include <boost/msm/backmp11/detail/favor_runtime_speed.hpp>
#include <boost/msm/backmp11/detail/state_visitor.hpp>
#include <boost/msm/backmp11/common_types.hpp>
#include <boost/msm/backmp11/state_machine_config.hpp>
namespace boost { namespace msm { namespace backmp11
@@ -74,7 +74,7 @@ class state_machine_base : public FrontEnd
using front_end_t = FrontEnd;
using derived_t = Derived;
using events_queue_t = typename config_t::template
queue_container<std::function<process_result()>>;
queue_container<basic_unique_ptr<enqueued_event>>;
// Event that describes the SM is starting.
// Used when the front-end does not define an initial_event.
@@ -578,17 +578,7 @@ class state_machine_base : public FrontEnd
// define the dispatch table used for event dispatch
using sm_dispatch_table = typename compile_policy_impl::template dispatch_table<state_machine_base>;
struct deferred_event_t
{
std::function<process_result()> process_event;
std::function<bool()> is_event_deferred;
// Deferred events are added with a correlation sequence that helps to
// identify when an event was added.
// Newly deferred events will not be considered for procesing
// within the same sequence.
size_t seq_cnt;
};
using deferred_events_queue_t = std::list<deferred_event_t>;
using deferred_events_queue_t = std::list<basic_unique_ptr<deferred_event>>;
struct deferred_events_t
{
@@ -596,7 +586,7 @@ class state_machine_base : public FrontEnd
size_t cur_seq_cnt;
};
using has_any_deferred_event =
mp11::mp_any_of<state_set, has_state_deferred_events>;
mp11::mp_any_of<state_set, has_deferred_events>;
using deferred_events_member =
optional_instance<deferred_events_t,
has_any_deferred_event::value ||
@@ -624,12 +614,6 @@ class state_machine_base : public FrontEnd
return m_optional_members.template get<deferred_events_member>();
}
template <class Event>
bool is_event_deferred(const Event& event) const
{
return compile_policy_impl::is_event_deferred(*this, event);
}
// Visit states with a compile-time filter (reduces template instantiations).
template <template <typename> typename Predicate, visit_mode Mode, typename Visitor>
void visit_if(Visitor&& visitor)
@@ -811,6 +795,30 @@ class state_machine_base : public FrontEnd
return process_event_internal(event, EventSource::EVENT_SOURCE_DIRECT);
}
private:
template <typename Event>
class enqueued_event_impl : public enqueued_event
{
public:
enqueued_event_impl(state_machine_base& sm, const Event& event)
: m_sm(sm), m_event(event)
{
}
process_result process() override
{
return m_sm.process_event_internal(
m_event,
EventSource::EVENT_SOURCE_DIRECT |
EventSource::EVENT_SOURCE_MSG_QUEUE);
}
private:
state_machine_base& m_sm;
Event m_event;
};
public:
// Enqueues an event in the message queue.
// Call process_queued_events to process all queued events.
// Be careful if you do this during event processing, the event will be processed immediately
@@ -820,15 +828,8 @@ class state_machine_base : public FrontEnd
typename = std::enable_if_t<C>>
void enqueue_event(Event const& event)
{
get_events_queue().push_back(
[this, event]
{
return process_event_internal(
event,
EventSource::EVENT_SOURCE_DIRECT |
EventSource::EVENT_SOURCE_MSG_QUEUE);
}
);
get_events_queue().push_back(basic_unique_ptr<enqueued_event>{
new enqueued_event_impl<Event>(*this, event)});
}
// Process all queued events.
@@ -847,9 +848,9 @@ class state_machine_base : public FrontEnd
typename = std::enable_if_t<C>>
void process_single_queued_event()
{
auto to_call = get_events_queue().front();
basic_unique_ptr<enqueued_event> event = std::move(get_events_queue().front());
get_events_queue().pop_front();
to_call();
event->process();
}
// Get the context of the state machine.
@@ -1089,19 +1090,19 @@ class state_machine_base : public FrontEnd
auto it = deferred_events.queue.begin();
do
{
if (deferred_events.cur_seq_cnt == it->seq_cnt)
if (deferred_events.cur_seq_cnt == (*it)->get_seq_cnt())
{
return;
}
if (it->is_event_deferred())
if ((*it)->is_deferred())
{
it = std::next(it);
}
else
{
deferred_event_t deferred_event = std::move(*it);
basic_unique_ptr<deferred_event> deferred_event = std::move(*it);
it = deferred_events.queue.erase(it);
const process_result result = deferred_event.process_event();
const process_result result = deferred_event->process();
if ((result & process_result::HANDLED_TRUE) &&
(active_state_ids != m_active_state_ids))
@@ -1187,7 +1188,7 @@ class state_machine_base : public FrontEnd
// in the active state configuration, then defer it for later processing.
if constexpr (has_any_deferred_event::value)
{
if (is_event_deferred(event))
if (compile_policy_impl::is_event_deferred(*this, event))
{
compile_policy_impl::defer_event(*this, event);
return process_result::HANDLED_DEFERRED;