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:
committed by
Christian Granzin
parent
a10ec92f25
commit
155b3ef6d6
@@ -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
|
||||
|================================================================================
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user