SObjectizer 5.8
Loading...
Searching...
No Matches
agent.cpp
Go to the documentation of this file.
1/*
2 SObjectizer 5.
3*/
4
5#include <so_5/agent.hpp>
6#include <so_5/mbox.hpp>
7#include <so_5/enveloped_msg.hpp>
8#include <so_5/environment.hpp>
9#include <so_5/send_functions.hpp>
10
11#include <so_5/impl/internal_env_iface.hpp>
12#include <so_5/impl/coop_private_iface.hpp>
13
14#include <so_5/impl/delivery_filter_storage.hpp>
15#include <so_5/impl/msg_tracing_helpers.hpp>
16#include <so_5/impl/process_unhandled_exception.hpp>
17#include <so_5/impl/std_message_sinks.hpp>
18#include <so_5/impl/subscription_storage_iface.hpp>
19
20#include <so_5/impl/enveloped_msg_details.hpp>
21
22#include <so_5/details/abort_on_fatal_error.hpp>
23
24#include <so_5/spinlocks.hpp>
25
26#include <algorithm>
27#include <sstream>
28#include <cstdint>
29#include <cstdlib>
30#include <limits>
31
32namespace so_5
33{
34
35//
36// agent_identity_t::pointer_only_t
37//
40 {
41 static constexpr std::array<char, 16> hex_symbols{
42 '0', '1', '2', '3', '4', '5', '6', '7',
43 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
44 };
45
46 std::array<char, c_string_size> result;
47 auto it = std::copy(
48 c_string_prefix.begin(),
49 c_string_prefix.end(),
50 result.begin() );
51
52 // NOTE: this code won't compile if there is no std::uintptr_t type,
53 // but we don't care about such platforms at the moment.
54 std::uintptr_t ptr_as_uint =
55 reinterpret_cast<std::uintptr_t>(m_pointer_value);
56
57 // Handle by 4 bits portions from the most significant bit.
58 // Leading zeros are not skipped (for the simplicity of implementation).
59 constexpr unsigned int half_octet = 4u;
60 unsigned bits_to_process = sizeof(void *) * (half_octet * 2u);
61 while( bits_to_process >= half_octet )
62 {
63 const auto index = static_cast<std::size_t>(
64 (ptr_as_uint >> (bits_to_process - half_octet)) & 0x0Fu
65 );
66
67 *(it++) = hex_symbols[ index ];
68
69 bits_to_process -= half_octet;
70 }
71
72 it = std::copy( c_string_suffix.begin(), c_string_suffix.end(), it );
73 *it = 0;
74
75 return result;
76 }
77
78namespace
79{
80
81[[nodiscard]]
82unsigned int
83ensure_valid_agent_name_length( std::size_t length )
84 {
85 if( 0u == length )
87 "Name of an agent can't be empty" );
88
89 constexpr std::size_t max_unit_value =
90 std::numeric_limits<unsigned int>::max();
91 if( max_unit_value < length )
93 "Name of an agent is too long (length should fit "
94 "into unsigned int)" );
95
96 return static_cast<unsigned int>( length );
97 }
98
99} /* namespace anonymous */
100
101//
102// name_for_agent_t
103//
107
108name_for_agent_t::name_for_agent_t( std::string_view value )
110 {
111 m_value.reset( new char[ value.size() ] );
112 std::copy( std::begin(value), std::end(value), m_value.get() );
113 }
114
116 : m_length{ other.m_length }
117 {
118 if( other.has_value() )
119 {
120 m_value.reset( new char[ m_length ] );
121 std::copy(
122 other.m_value.get(),
123 other.m_value.get() + m_length,
124 m_value.get() );
125 }
126 }
127
130 {
131 name_for_agent_t tmp{ other };
132 swap( *this, tmp );
133 return *this;
134 }
135
137 : m_value{ std::exchange( other.m_value, std::unique_ptr< char[] >{} ) }
138 , m_length{ std::exchange( other.m_length, 0u ) }
139 {}
140
143 {
144 name_for_agent_t tmp{ std::move(other) };
145 swap( *this, tmp );
146 return *this;
147 }
148
149SO_5_FUNC void
151 {
152 using std::swap;
153 swap( a.m_value, b.m_value );
154 swap( a.m_length, b.m_length );
155 }
156
158
159std::string_view
161 {
162 std::string_view result;
163 if( has_value() )
164 result = std::string_view{ m_value.get(), m_length };
165 return result;
166 }
167
168bool
170 {
171 return 0u != m_length;
172 }
173
174namespace
175{
176
177/*!
178 * \since
179 * v.5.4.0
180 *
181 */
182std::string
183create_anonymous_state_name( const agent_t * agent, const state_t * st )
184 {
185 std::ostringstream ss;
186 ss << "<state:target=" << agent << ":this=" << st << ">";
187 return ss.str();
188 }
189
190} /* namespace anonymous */
191
192// NOTE: Implementation of state_t is moved to that file in v.5.4.0.
193
194//
195// state_t::time_limit_t
196//
197struct state_t::time_limit_t
198{
199 using msg_timeout = so_5::details::msg_state_timeout;
200
201 duration_t m_limit;
202 const state_t & m_state_to_switch;
203
206
208 duration_t limit,
209 const state_t & state_to_switch )
210 : m_limit( limit )
211 , m_state_to_switch( state_to_switch )
212 {}
213
214 void
216 agent_t & agent,
217 const state_t & current_state ) noexcept
218 {
219 // Because this method is called from on_enter handler it can't
220 // throw exceptions. Any exception will lead to abort of the application.
221 // So we don't care about exception safety.
223
224 // New unique mbox is necessary for time limit.
226 // A new MPSC mbox will be used for that.
228 // New MPSC mbox will be directly connected to target agent.
229 agent );
230
231 // A subscription must be created for msg_timeout signal.
233 .in( current_state )
234 .event( [&agent, this](mhood_t<msg_timeout>) {
236 } );
237
238 // Delayed timeout signal must be sent.
239 m_timer = send_periodic< msg_timeout >(
242 duration_t::zero() );
243 } );
244 }
245
246 void
248 agent_t & agent,
249 const state_t & current_state ) noexcept
250 {
251 // Because this method is called from on_exit handler it can't
252 // throw exceptions. Any exception will lead to abort of the application.
253 // So we don't care about exception safety.
256
257 if( m_unique_mbox )
258 {
259 // Old subscription must be removed.
260 agent.so_drop_subscription< msg_timeout >(
261 m_unique_mbox, current_state );
262 // Unique mbox is no more needed.
263 m_unique_mbox = mbox_t{};
264 }
265 } );
266 }
267};
268
269//
270// state_t
271//
272
273state_t::state_t(
274 agent_t * target_agent,
275 std::string state_name,
276 state_t * parent_state,
277 std::size_t nested_level,
278 history_t state_history )
279 : m_target_agent{ target_agent }
280 , m_state_name( std::move(state_name) )
281 , m_parent_state{ parent_state }
282 , m_initial_substate{ nullptr }
283 , m_state_history{ state_history }
284 , m_last_active_substate{ nullptr }
285 , m_nested_level{ nested_level }
286 , m_substate_count{ 0 }
287{
288 if( parent_state )
289 {
290 // We should check the deep of nested states.
291 if( m_nested_level >= max_deep )
293 "max nesting deep for agent states is " +
294 std::to_string( max_deep ) );
295
296 // Now we can safely mark parent state as composite.
297 parent_state->m_substate_count += 1;
298 }
299}
300
301state_t::state_t(
302 agent_t * agent )
303 : state_t{ agent, history_t::none }
304{
305}
306
307state_t::state_t(
308 agent_t * agent,
309 history_t state_history )
310 : state_t{ agent, std::string(), nullptr, 0, state_history }
311{
312}
313
314state_t::state_t(
315 agent_t * agent,
316 std::string state_name )
317 : state_t{ agent, std::move(state_name), history_t::none }
318{}
319
320state_t::state_t(
321 agent_t * agent,
322 std::string state_name,
323 history_t state_history )
324 : state_t{ agent, std::move(state_name), nullptr, 0, state_history }
325{}
326
327state_t::state_t(
328 initial_substate_of parent )
329 : state_t{ parent, std::string(), history_t::none }
330{}
331
332state_t::state_t(
333 initial_substate_of parent,
334 std::string state_name )
335 : state_t{ parent, std::move(state_name), history_t::none }
336{}
337
338state_t::state_t(
339 initial_substate_of parent,
340 std::string state_name,
341 history_t state_history )
342 : state_t{
344 std::move(state_name),
345 parent.m_parent_state,
347 state_history }
348{
351 "initial substate for state " + m_parent_state->query_name() +
352 " is already defined: " +
354
356}
357
358state_t::state_t(
359 substate_of parent )
360 : state_t{ parent, std::string(), history_t::none }
361{}
362
363state_t::state_t(
364 substate_of parent,
365 std::string state_name )
366 : state_t{ parent, std::move(state_name), history_t::none }
367{}
368
369state_t::state_t(
370 substate_of parent,
371 std::string state_name,
372 history_t state_history )
373 : state_t{
375 std::move(state_name),
376 parent.m_parent_state,
378 state_history }
379{}
380
381state_t::state_t(
382 state_t && other )
384 , m_state_name( std::move( other.m_state_name ) )
391 , m_on_enter{ std::move(other.m_on_enter) }
392 , m_on_exit{ std::move(other.m_on_exit) }
393{
396}
397
398state_t::~state_t()
399{
400}
401
402bool
403state_t::operator == ( const state_t & state ) const noexcept
404{
405 return &state == this;
406}
407
408std::string
409state_t::query_name() const
410{
411 auto getter = [this]() -> std::string {
412 if( m_state_name.empty() )
414 else
415 return m_state_name;
416 };
417
418 if( m_parent_state )
419 return m_parent_state->query_name() + "." + getter();
420 else
421 return getter();
422}
423
424namespace {
425
426#if defined(__clang__)
427#pragma clang diagnostic push
428#pragma clang diagnostic ignored "-Wexit-time-destructors"
429#pragma clang diagnostic ignored "-Wglobal-constructors"
430#endif
431
432/*!
433 * \since
434 * v.5.4.0
435 *
436 * \brief A special object for the state in which agent is awaiting
437 * for deregistration after unhandled exception.
438 *
439 * This object will be shared between all agents.
440 */
442 nullptr, "<AWAITING_DEREGISTRATION_AFTER_UNHANDLED_EXCEPTION>" );
443
444/*!
445 * \since
446 * v.5.5.21
447 *
448 * \brief A special object to be used as state for make subscriptions
449 * for deadletter handlers.
450 *
451 * This object will be shared between all agents.
452 */
453const state_t deadletter_state(
454 nullptr, "<DEADLETTER_STATE>" );
455
456#if defined(__clang__)
457#pragma clang diagnostic pop
458#endif
459
460} /* namespace anonymous */
461
462bool
463state_t::is_target( const agent_t * agent ) const noexcept
464{
465 if( m_target_agent )
466 return m_target_agent == agent;
467 else if( this == &awaiting_deregistration_state )
468 return true;
469 else
470 return false;
471}
472
473void
474state_t::activate() const
475{
477}
478
479state_t &
480state_t::time_limit(
481 duration_t timeout,
482 const state_t & state_to_switch )
483{
484 if( duration_t::zero() == timeout )
486 "zero can't be used as time limit for state: " +
487 query_name() );
488
489 // Old time limit must be dropped if it exists.
490 {
491 // As a defense from exception create new time_limit object first.
492 auto fresh_limit = std::make_unique< time_limit_t >(
493 timeout, std::cref(state_to_switch) );
495 m_time_limit = std::move(fresh_limit);
496 }
497
498 // If this state is active then new time limit must be activated.
499 if( is_active() )
501 [&] {
503 },
504 [&] {
505 // Time limit must be dropped because it is not activated
506 // for the current state.
508 } );
509
510 return *this;
511}
512
513state_t &
515{
516 if( m_time_limit )
517 {
519 m_time_limit.reset();
520 }
521
522 return *this;
523}
524
525const state_t *
526state_t::actual_state_to_enter() const
527{
528 const state_t * s = this;
529 while( 0 != s->m_substate_count )
530 {
532 // Note: for states with shallow history m_last_active_substate
533 // can point to composite substate. This substate must be
534 // processed usual way with checking for substate count, presence
535 // of initial substate and so on...
537 else if( !s->m_initial_substate )
539 "there is no initial substate for composite state: " +
540 query_name() );
541 else
543 }
544
545 return s;
546}
547
548void
550{
551 auto p = m_parent_state;
552
553 // This pointer will be used for update states with shallow history.
554 // This pointer will be changed on every iteration.
555 auto c = this;
556
557 while( p )
558 {
563
564 c = p;
565 p = p->m_parent_state;
566 }
567}
568
569void
574
575void
580
581namespace
582{
583
584/*!
585 * \brief Helper for creation of the direct mbox for an agent.
586 *
587 * If there is a custom direct mbox factory in \a tuning_options
588 * then the return value of that factory is used. Otherwise the
589 * \a standard_mbox is returned.
590 */
591[[nodiscard]]
592mbox_t
595 const agent_tuning_options_t & tuning_options,
596 mbox_t standard_mbox )
597 {
598 mbox_t result{ std::move(standard_mbox) };
599
600 const auto & factory =
602 if( factory )
603 {
604 result = factory( agent_ptr, std::move(result) );
605
607 != result->type() )
608 {
611 "MPSC mbox is expected as the direct mbox "
612 "for an agent" );
613 }
614 }
615
616 return result;
617 }
618
619/*!
620 * \brief Helper for selection of subscription storage factory.
621 *
622 * If a factory is specified in @a tuning_options explicitly then it's
623 * used. Otherwise the default subscription storage factory from
624 * SObjectizer Environment will be used.
625 *
626 * \since v.5.8.2
627 */
628[[nodiscard]]
629subscription_storage_factory_t
639
640} /* namespace anonymous */
641
642//
643// agent_t
644//
645
647 environment_t & env )
649{
650}
651
653 environment_t & env,
654 agent_tuning_options_t options )
655 : agent_t( context_t( env, std::move( options ) ) )
656{
657}
658
692
694{
695 // Sometimes it is possible that agent is destroyed without
696 // correct deregistration from SO Environment.
698}
699
700void
702{
703 // Default implementation do nothing.
704}
705
706void
708{
709 // Default implementation do nothing.
710}
711
712bool
713agent_t::so_is_active_state( const state_t & state_to_check ) const noexcept
714{
715 state_t::path_t path;
717
718 const auto past_the_end = [&path, this]() {
719 auto r = begin(path);
720 std::advance( r, m_current_state_ptr->nested_level() + 1u );
721 return r;
722 }();
723
724 return past_the_end != std::find( begin(path), past_the_end, &state_to_check );
725}
726
727void
729 agent_state_listener_t & state_listener )
730{
732 impl::state_listener_controller_t::wrap_nondestroyable(
733 state_listener ) );
734}
735
736void
738 agent_state_listener_unique_ptr_t state_listener )
739{
741 impl::state_listener_controller_t::wrap_destroyable(
742 std::move( state_listener ) ) );
743}
744
747{
748 if( m_agent_coop )
750 else
751 // This is very strange case. So it would be better to abort.
752 return abort_on_exception;
753}
754
755void
760
761const mbox_t &
763{
764 return m_direct_mbox;
765}
766
767mbox_t
773
774const state_t &
776{
777 return st_default;
778}
779
780namespace impl {
781
783{
786
787public :
789 agent_t & agent )
790 : m_agent( agent )
792 {
794 == agent.m_current_status )
797 "an attempt to switch agent state when another state "
798 "switch operation is in progress for the same agent" );
799
801 }
806};
807
808} /* namespace impl */
809
810void
812 const state_t & new_state )
813{
815
816 do_change_agent_state( new_state );
817}
818
819void
827
828void
839
840void
842{
843 // Default implementation do nothing.
844}
845
846bool
851
853agent_t::so_environment() const noexcept
854{
855 return m_env;
856}
857
858[[nodiscard]]
861{
862 if( !m_agent_coop )
865 "agent_t::so_coop() can be completed because agent is not bound "
866 "to any cooperation" );
867
869}
870
871void
873 event_queue_t & queue ) noexcept
874{
875 // Since v.5.5.24 we should use event_queue_hook to get an
876 // actual event_queue.
877 auto * actual_queue = impl::internal_env_iface_t{ m_env }
878 .event_queue_on_bind( this, &queue );
879
880 std::lock_guard< default_rw_spinlock_t > queue_lock{ m_event_queue_lock };
881
882 // Cooperation usage counter should be incremented.
883 // It will be decremented during final agent event execution.
885
886 // A starting demand must be sent first.
887 actual_queue->push_evt_start(
889 this,
891 0,
892 typeid(void),
893 message_ref_t(),
895
896 // Only then pointer to the queue could be stored.
897 m_event_queue = actual_queue;
898}
899
903{
904 enum class demand_type_t {
905 message, enveloped_msg, other
906 };
907
908 // We can't use message_kind_t here because there are special
909 // demands like demands for so_evt_start/so_evt_finish.
910 // Because of that a pointer to demand handler will be analyzed.
911 const auto demand_type =
913 demand_type_t::message :
915 demand_type_t::enveloped_msg : demand_type_t::other));
916
917 if( demand_type_t::other != demand_type )
918 {
919 // Try to find handler for the demand.
920 auto handler = d.m_receiver->m_handler_finder(
921 d, "create_execution_hint" );
922 if( demand_type_t::message == demand_type )
923 {
924 if( handler )
925 return execution_hint_t(
926 d,
927 [handler](
928 execution_demand_t & demand,
929 current_thread_id_t thread_id ) {
931 thread_id,
932 demand,
933 handler->m_thread_safety,
934 handler->m_method );
935 },
936 handler->m_thread_safety );
937 else
938 // Handler not found.
940 }
941 else
942 {
943 // Execution hint for enveloped message is
944 // very similar to hint for service request.
945 return execution_hint_t(
946 d,
947 [handler](
948 execution_demand_t & demand,
949 current_thread_id_t thread_id ) {
951 thread_id,
952 demand,
953 handler );
954 },
955 handler ? handler->m_thread_safety :
956 // If there is no real handler then
957 // there will only be actions from
958 // envelope.
959 // These actions should be thread safe.
961 }
962 }
963 else
964 // This is demand_handler_on_start or demand_handler_on_finish.
965 return execution_hint_t(
966 d,
967 []( execution_demand_t & demand,
968 current_thread_id_t thread_id ) {
969 demand.call_handler( thread_id );
970 },
972}
973
974void
980
981void
986
987disp_binder_shptr_t
989{
990 if( !m_agent_coop )
993 "agent_t::so_this_coop_disp_binder() can be completed "
994 "because agent is not bound to any cooperation" );
995
997}
998
1000agent_t::so_agent_name() const noexcept
1001{
1002 if( m_name.has_value() )
1004 else
1005 return { this };
1006}
1007
1008void
1014
1015agent_ref_t
1017{
1018 agent_ref_t agent_ref( this );
1019 return agent_ref;
1020}
1021
1022void
1024{
1025 m_agent_coop = &coop;
1026}
1027
1028void
1030{
1031 event_queue_t * actual_queue = nullptr;
1032 {
1033 std::lock_guard< default_rw_spinlock_t > queue_lock{ m_event_queue_lock };
1034
1035 // Since v.5.5.8 shutdown is done by two simple step:
1036 // - remove actual value from m_event_queue;
1037 // - pushing final demand to actual event queue.
1038 //
1039 // No new demands will be sent to the agent, but all the subscriptions
1040 // remains. They will be destroyed at the very end of agent's lifetime.
1041
1042 if( m_event_queue )
1043 {
1044 // This pointer will be used later.
1045 actual_queue = m_event_queue;
1046
1047 // Final event must be pushed to queue.
1051 this,
1053 0,
1054 typeid(void),
1055 message_ref_t(),
1057
1058 // No more events will be stored to the queue.
1059 m_event_queue = nullptr;
1060 } );
1061
1062 }
1063 else
1065 SO_5_LOG_ERROR( so_environment(), log_stream )
1066 {
1067 log_stream << "Unexpected error: m_event_queue contains "
1068 "nullptr. Unable to push demand_handler_on_finish for "
1069 "the agent (" << this << "). Application will be aborted"
1070 << std::endl;
1071 }
1072 } );
1073 }
1074
1075 if( actual_queue )
1076 // Since v.5.5.24 we should utilize event_queue via
1077 // event_queue_hook.
1079 .event_queue_on_unbind( this, actual_queue );
1080}
1081
1082void
1084 const mbox_t & mbox_ref,
1085 std::type_index msg_type,
1086 const state_t & target_state,
1087 const event_handler_method_t & method,
1088 thread_safety_t thread_safety,
1089 event_handler_kind_t handler_kind )
1090{
1091 // Since v.5.4.0 there is no need for locking agent's mutex
1092 // because this operation can be performed only on agent's
1093 // working thread.
1094
1095 ensure_operation_is_on_working_thread( "so_create_event_subscription" );
1096
1097 // A new subscription can't be made if agent is already deactivated.
1101 "new subscription can't made for deactivated agent" );
1102
1104 mbox_ref,
1105 msg_type,
1107 target_state,
1108 method,
1109 thread_safety,
1110 handler_kind );
1111}
1112
1113void
1115 const mbox_t & mbox,
1116 const std::type_index & msg_type,
1117 const event_handler_method_t & method,
1118 thread_safety_t thread_safety )
1119{
1120 ensure_operation_is_on_working_thread( "so_create_deadletter_subscription" );
1121
1122 // A new deadletter handler can't be made if agent is already deactivated.
1126 "new deadletter handler can't be set for deactivated agent" );
1127
1129 mbox,
1130 msg_type,
1133 method,
1134 thread_safety,
1136}
1137
1138void
1140 const mbox_t & mbox,
1141 const std::type_index & msg_type )
1142{
1143 // Since v.5.4.0 there is no need for locking agent's mutex
1144 // because this operation can be performed only on agent's
1145 // working thread.
1146
1147 ensure_operation_is_on_working_thread( "do_drop_deadletter_handler" );
1148
1150}
1151
1154 const std::type_index & msg_type )
1155{
1156 auto * result = m_message_sinks->find_or_create( msg_type );
1157
1158 if( !result )
1161 std::string( "message type without "
1162 "predefined limit for that type, type: " ) +
1163 msg_type.name() );
1164
1165 return *result;
1166}
1167
1168void
1170 const mbox_t & mbox,
1171 const std::type_index & msg_type,
1172 const state_t & target_state )
1173{
1174 // Since v.5.4.0 there is no need for locking agent's mutex
1175 // because this operation can be performed only on agent's
1176 // working thread.
1177
1178 ensure_operation_is_on_working_thread( "do_drop_subscription" );
1179
1180 m_subscriptions->drop_subscription( mbox, msg_type, target_state );
1181}
1182
1183void
1185 const mbox_t & mbox,
1186 const std::type_index & msg_type )
1187{
1188 // Since v.5.4.0 there is no need for locking agent's mutex
1189 // because this operation can be performed only on agent's
1190 // working thread.
1191
1193 "do_drop_subscription_for_all_states" );
1194
1196}
1197
1198bool
1200 const mbox_t & mbox,
1201 const std::type_index & msg_type,
1202 const state_t & target_state ) const noexcept
1203{
1204 return nullptr != m_subscriptions->find_handler(
1205 mbox->id(), msg_type, target_state );
1206}
1207
1208bool
1210 const mbox_t & mbox,
1211 const std::type_index & msg_type ) const noexcept
1212{
1213 return nullptr != m_subscriptions->find_handler(
1214 mbox->id(), msg_type, deadletter_state );
1215}
1216
1217namespace {
1218
1219/*!
1220 * \brief A helper function to select actual demand handler in
1221 * dependency of message kind.
1222 *
1223 * \since
1224 * v.5.5.23
1225 */
1226inline demand_handler_pfn_t
1228 const agent_t & agent,
1229 const message_ref_t & msg )
1230{
1231 demand_handler_pfn_t result = &agent_t::demand_handler_on_message;
1232 if( msg )
1233 {
1234 switch( message_kind( *msg ) )
1235 {
1236 case message_t::kind_t::classical_message : // Already has value.
1237 break;
1238
1239 case message_t::kind_t::user_type_message : // Already has value.
1240 break;
1241
1242 case message_t::kind_t::enveloped_msg :
1244 break;
1245
1246 case message_t::kind_t::signal :
1248 SO_5_LOG_ERROR( agent.so_environment(), log_stream )
1249 {
1250 log_stream << "message that has data and message_kind_t::signal!"
1251 "Signals can't have data. Application will be aborted!"
1252 << std::endl;
1253 }
1254 } );
1255 break;
1256 }
1257 }
1258
1259 return result;
1260}
1261
1262} /* namespace anonymous */
1263
1264void
1266 const message_limit::control_block_t * limit,
1267 mbox_id_t mbox_id,
1268 const std::type_index & msg_type,
1269 const message_ref_t & message )
1270{
1271 const auto handler = select_demand_handler_for_message( *this, message );
1272
1273 read_lock_guard_t< default_rw_spinlock_t > queue_lock{ m_event_queue_lock };
1274
1275 if( m_event_queue )
1278 this,
1279 limit,
1280 mbox_id,
1281 msg_type,
1282 message,
1283 handler ) );
1284}
1285
1286void
1288 current_thread_id_t working_thread_id,
1289 execution_demand_t & d )
1290{
1292
1295 working_thread_id );
1296
1297 try
1298 {
1300 }
1301 catch( const std::exception & x )
1302 {
1304 working_thread_id, x, *(d.m_receiver) );
1305 }
1306 catch( ... ) // Since v.5.5.24.3
1307 {
1309 working_thread_id, *(d.m_receiver) );
1310 }
1311}
1312
1313void
1315{
1316 // Nothing more to do.
1317 // Just lock coop's binding_lock. If cooperation is not finished yet
1318 // it would stop the current thread.
1319 std::lock_guard< std::mutex > binding_lock{ m_agent_coop->m_lock };
1320}
1321
1322demand_handler_pfn_t
1327
1328void
1330 current_thread_id_t working_thread_id,
1331 execution_demand_t & d )
1332{
1333 {
1334 // Sentinel must finish its work before decrementing
1335 // reference count to cooperation.
1338 working_thread_id );
1339
1340 try
1341 {
1343 }
1344 catch( const std::exception & x )
1345 {
1347 working_thread_id, x, *(d.m_receiver) );
1348 }
1349 catch( ... ) // Since v.5.5.24.3
1350 {
1352 working_thread_id, *(d.m_receiver) );
1353 }
1354
1355 // Since v.5.5.15 agent should be returned in default state.
1357 }
1358
1359 // Cooperation should receive notification about agent deregistration.
1362}
1363
1364demand_handler_pfn_t
1369
1370void
1372 current_thread_id_t working_thread_id,
1373 execution_demand_t & d )
1374{
1376
1377 auto handler = d.m_receiver->m_handler_finder(
1378 d, "demand_handler_on_message" );
1379 if( handler )
1381 working_thread_id,
1382 d,
1383 handler->m_thread_safety,
1384 handler->m_method );
1385}
1386
1387demand_handler_pfn_t
1392
1393void
1395 current_thread_id_t working_thread_id,
1396 execution_demand_t & d )
1397{
1399
1400 auto handler = d.m_receiver->m_handler_finder(
1401 d, "demand_handler_on_enveloped_msg" );
1402 process_enveloped_msg( working_thread_id, d, handler );
1403}
1404
1405demand_handler_pfn_t
1410
1411void
1413 current_thread_id_t working_thread_id,
1415 thread_safety_t thread_safety,
1416 event_handler_method_t method )
1417{
1420 // v.5.7.3
1421 // If event_handler is thread_safe-handler then null_thread_id
1422 // has to be used instead of the actual working_thread_id.
1423 so_5::thread_safe == thread_safety
1425 : working_thread_id
1426 };
1427
1428 try
1429 {
1430 method( d.m_message_ref );
1431 }
1432 catch( const std::exception & x )
1433 {
1435 working_thread_id, x, *(d.m_receiver) );
1436 }
1437 catch( ... ) // Since v.5.5.24.3
1438 {
1440 working_thread_id, *(d.m_receiver) );
1441 }
1442}
1443
1444void
1446 current_thread_id_t working_thread_id,
1448 const impl::event_handler_data_t * handler_data )
1449{
1450 using namespace enveloped_msg::impl;
1451
1452 if( handler_data )
1453 {
1454 // If this is intermediate_handler then we should pass the
1455 // whole envelope to it.
1457 // Just call process_message() in that case because
1458 // process_message() already does what we need (including
1459 // setting working_thread_id and handling of exceptions).
1461 working_thread_id,
1462 d,
1463 handler_data->m_thread_safety,
1464 handler_data->m_method );
1465 else
1466 // For a final_handler the payload should be extracted
1467 // from the envelope and the extracted payload should go
1468 // to the handler.
1469 // We don't expect exceptions here and can't restore after them.
1471 auto & envelope = message_to_envelope( d.m_message_ref );
1473 working_thread_id,
1474 d,
1475 *handler_data
1476 };
1477 envelope.access_hook(
1479 invoker );
1480 } );
1481 }
1482}
1483
1484void
1486 const char * operation_name ) const
1487{
1489 {
1490 std::ostringstream s;
1491
1492 s << operation_name
1493 << ": operation is enabled only on agent's working thread; "
1494 << "working_thread_id: ";
1495
1497 s << "<NONE>";
1498 else
1500
1501 s << ", current_thread_id: " << so_5::query_current_thread_id();
1502
1505 s.str() );
1506 }
1507}
1508
1509void
1511{
1512 if( m_delivery_filters )
1513 {
1515 m_delivery_filters.reset();
1516 }
1517}
1518
1519void
1521 const mbox_t & mbox,
1522 const std::type_index & msg_type,
1523 delivery_filter_unique_ptr_t filter )
1524{
1525 ensure_operation_is_on_working_thread( "set_delivery_filter" );
1526
1527 // A new delivery filter can't be set if agent is already deactivated.
1531 "new delivery filter can't be set for deactivated agent" );
1532
1533 // Message sink for that message type have to be obtained with
1534 // the respect to message limits.
1535 auto & target_sink = detect_sink_for_message_type( msg_type );
1536
1537 if( !m_delivery_filters )
1539
1541 mbox,
1542 msg_type,
1543 std::move(filter),
1544 outliving_mutable( target_sink ) );
1545}
1546
1547void
1549 const mbox_t & mbox,
1550 const std::type_index & msg_type ) noexcept
1551{
1552 ensure_operation_is_on_working_thread( "set_delivery_filter" );
1553
1554 if( m_delivery_filters )
1556}
1557
1561 const char * /*context_marker*/ )
1562{
1563 auto search_result = find_event_handler_for_current_state( d );
1564 if( !search_result )
1565 // Since v.5.5.21 we should check for deadletter handler for that demand.
1566 search_result = find_deadletter_handler( d );
1567
1568 return search_result;
1569}
1570
1574 const char * context_marker )
1575{
1576 auto search_result = find_event_handler_for_current_state( d );
1577
1578 if( !search_result )
1579 {
1580 // Since v.5.5.21 we should check for deadletter handler for that demand.
1581 search_result = find_deadletter_handler( d );
1582
1583 if( search_result )
1584 {
1585 // Deadletter handler found. This must be reflected in trace.
1587 d,
1588 context_marker,
1589 search_result );
1590
1591 return search_result;
1592 }
1593 }
1594
1595 // This trace will be made if an event_handler is found for the
1596 // current state or not found at all (including deadletter handlers).
1598 d,
1599 context_marker,
1600 search_result );
1601
1602 return search_result;
1603}
1604
1607 execution_demand_t & d )
1608{
1609 const impl::event_handler_data_t * search_result = nullptr;
1610 const state_t * s = &d.m_receiver->so_current_state();
1611
1612 do {
1616 *s );
1617
1618 if( !search_result )
1619 s = s->parent_state();
1620
1621 } while( search_result == nullptr && s != nullptr );
1622
1623 return search_result;
1624}
1625
1635
1636void
1638 const state_t & state_to_be_set )
1639{
1640 // The agent can't leave awaiting_deregistration_state if it's
1641 // in that state already.
1643 state_to_be_set != awaiting_deregistration_state )
1644 {
1647 "unable to switch agent to another state because the "
1648 "agent is already deactivated" );
1649 }
1650
1651 if( state_to_be_set.is_target( this ) )
1652 {
1653 // Since v.5.5.18 we must check nested state switch operations.
1654 // This object will drop pointer to the current state.
1655 impl::state_switch_guard_t switch_op_guard( *this );
1656
1657 auto actual_new_state = state_to_be_set.actual_state_to_enter();
1658 if( !( *actual_new_state == *m_current_state_ptr ) )
1659 {
1660 // New state differs from the current one.
1661 // Actual state switch must be performed.
1662 do_state_switch( *actual_new_state );
1663
1664 // State listener should be informed.
1666 *this,
1668 }
1669 }
1670 else
1673 "unable to switch agent to alien state "
1674 "(the state that doesn't belong to this agent)" );
1675}
1676
1677void
1679 const state_t & state_to_be_set ) noexcept
1680{
1681 state_t::path_t old_path;
1682 state_t::path_t new_path;
1683
1684 // Since v.5.5.22 we will change the value of m_current_state_ptr
1685 // during state change procedure.
1686 auto current_st = m_current_state_ptr;
1687
1688 current_st->fill_path( old_path );
1689 state_to_be_set.fill_path( new_path );
1690
1691 // Find the first item which is different in the paths.
1692 std::size_t first_diff = 0;
1693 for(; first_diff < std::min(
1694 current_st->nested_level(),
1695 state_to_be_set.nested_level() );
1696 ++first_diff )
1697 if( old_path[ first_diff ] != new_path[ first_diff ] )
1698 break;
1699
1700 // Do call for on_exit and on_enter for states.
1701 // on_exit and on_enter should not throw exceptions.
1703
1705 *this, *current_st );
1706
1707 for( std::size_t i = current_st->nested_level();
1708 i >= first_diff; )
1709 {
1710 // Modify current state before calling on_exit handler.
1711 m_current_state_ptr = old_path[ i ];
1712 // Perform on_exit actions.
1713 old_path[ i ]->call_on_exit();
1714 if( i )
1715 --i;
1716 else
1717 break;
1718 }
1719
1721 *this, state_to_be_set );
1722
1723 for( std::size_t i = first_diff;
1724 i <= state_to_be_set.nested_level();
1725 ++i )
1726 {
1727 // Modify current state before calling on_exit handler.
1728 m_current_state_ptr = new_path[ i ];
1729
1730 // Perform on_enter actions.
1731 new_path[ i ]->call_on_enter();
1732 }
1733 } );
1734
1735 // Now the current state for the agent can be changed.
1736 m_current_state_ptr = &state_to_be_set;
1738}
1739
1740void
1742{
1745 {
1746 // The agent must be returned to the default state.
1747 // All on_exit handlers must be called at this point.
1749 }
1750}
1751
1752bool
1757
1758} /* namespace so_5 */
virtual mbox_id_t id() const =0
Unique ID of this mbox.
virtual mbox_type_t type() const =0
Get the type of message box.
Interface for message sink.
agent_context_t(environment_t &env, agent_tuning_options_t options)
agent_tuning_options_t & options()
Read-Write access to agent options.
environment_t & env() const
Access to SObjectizer Environment.
Helper class for holding agent's identity (name or pointer).
agent_identity_t(std::string_view name) noexcept
Initializing constructor for case when agent has a user specified name.
agent_identity_t(const agent_t *pointer) noexcept
Initializing constructor for case when agent has no user specified name.
Interface of the agent state listener.
A base class for agents.
Definition agent.hpp:673
static demand_handler_pfn_t get_demand_handler_on_start_ptr() noexcept
Definition agent.cpp:1323
impl::state_listener_controller_t m_state_listener_controller
State listeners controller.
Definition agent.hpp:2803
static void process_enveloped_msg(current_thread_id_t working_thread_id, execution_demand_t &d, const impl::event_handler_data_t *handler_data)
Actual implementation of enveloped message handling.
Definition agent.cpp:1445
std::unique_ptr< impl::sinks_storage_t > m_message_sinks
Holder of message sinks for that agent.
Definition agent.hpp:2843
const state_t st_default
Definition agent.hpp:2774
void do_change_agent_state(const state_t &state_to_be_set)
Perform actual operations related to state switch.
Definition agent.cpp:1637
void so_initiate_agent_definition()
A correct initiation of so_define_agent method call.
Definition agent.cpp:829
so_5::current_thread_id_t m_working_thread_id
Working thread id.
Definition agent.hpp:2899
default_rw_spinlock_t m_event_queue_lock
Event queue operation protector.
Definition agent.hpp:2867
const name_for_agent_t m_name
Optional name for the agent.
Definition agent.hpp:2948
static const impl::event_handler_data_t * find_deadletter_handler(execution_demand_t &demand)
Search for event handler between deadletter handlers.
Definition agent.cpp:1627
static demand_handler_pfn_t get_demand_handler_on_message_ptr() noexcept
Definition agent.cpp:1388
bool is_agent_deactivated() const noexcept
Is agent already deactivated.
Definition agent.cpp:1753
void so_switch_to_awaiting_deregistration_state()
Switching agent to special state in case of unhandled exception.
Definition agent.cpp:756
const state_t & so_current_state() const
Access to the current agent state.
Definition agent.hpp:934
static void process_message(current_thread_id_t working_thread_id, execution_demand_t &d, thread_safety_t thread_safety, event_handler_method_t method)
Actual implementation of message handling.
Definition agent.cpp:1412
agent_ref_t create_ref()
Make an agent reference.
Definition agent.cpp:1016
bool so_is_active_state(const state_t &state_to_check) const noexcept
Is a state activated?
Definition agent.cpp:713
void ensure_operation_is_on_working_thread(const char *operation_name) const
Enables operation only if it is performed on agent's working thread.
Definition agent.cpp:1485
bool so_was_defined() const
Is method define_agent already called?
Definition agent.cpp:847
agent_status_t
Enumeration of possible agent statuses.
Definition agent.hpp:2785
@ defined
Agent is defined.
@ state_switch_in_progress
State switch operation is in progress.
void destroy_all_subscriptions_and_filters() noexcept
Destroy all agent's subscriptions.
Definition agent.cpp:1009
void so_create_deadletter_subscription(const mbox_t &mbox, const std::type_index &msg_type, const event_handler_method_t &method, thread_safety_t thread_safety)
Create a subscription for a deadletter handler.
Definition agent.cpp:1114
void shutdown_agent() noexcept
Agent shutdown deriver.
Definition agent.cpp:1029
void ensure_binding_finished()
Ensures that all agents from cooperation are bound to dispatchers.
Definition agent.cpp:1314
coop_t * m_agent_coop
Agent is belong to this cooperation.
Definition agent.hpp:2902
void so_drop_subscription(const mbox_t &mbox, const state_t &target_state)
Drop subscription for the state specified.
Definition agent.hpp:1560
agent_status_t m_current_status
Current agent status.
Definition agent.hpp:2800
event_queue_t * m_event_queue
A pointer to event_queue.
Definition agent.hpp:2882
virtual void so_define_agent()
Hook on define agent for SObjectizer.
Definition agent.cpp:841
void drop_all_delivery_filters() noexcept
Drops all delivery filters.
Definition agent.cpp:1510
bool do_check_subscription_presence(const mbox_t &mbox, const std::type_index &msg_type, const state_t &target_state) const noexcept
Check the presence of a subscription.
Definition agent.cpp:1199
void do_state_switch(const state_t &state_to_be_set) noexcept
Actual action for switching agent state.
Definition agent.cpp:1678
std::unique_ptr< impl::delivery_filter_storage_t > m_delivery_filters
Delivery filters for that agents.
Definition agent.hpp:2911
void return_to_default_state_if_possible() noexcept
Return agent to the default state.
Definition agent.cpp:1741
agent_t(environment_t &env)
Constructor.
Definition agent.cpp:646
void do_set_delivery_filter(const mbox_t &mbox, const std::type_index &msg_type, delivery_filter_unique_ptr_t filter)
Set a delivery filter.
Definition agent.cpp:1520
mbox_t so_make_new_direct_mbox()
Create a new direct mbox for that agent.
Definition agent.cpp:768
static execution_hint_t so_create_execution_hint(execution_demand_t &demand)
Create execution hint for the specified demand.
Definition agent.cpp:901
void so_change_state(const state_t &new_state)
Change the current state of the agent.
Definition agent.cpp:811
void push_event(const message_limit::control_block_t *limit, mbox_id_t mbox_id, const std::type_index &msg_type, const message_ref_t &message)
Push event into the event queue.
Definition agent.cpp:1265
coop_handle_t so_coop() const
Get a handle of agent's coop.
Definition agent.cpp:860
void so_deregister_agent_coop_normally()
A helper method for deregistering agent's coop in case of normal deregistration.
Definition agent.cpp:982
void do_drop_delivery_filter(const mbox_t &mbox, const std::type_index &msg_type) noexcept
Drop a delivery filter.
Definition agent.cpp:1548
virtual exception_reaction_t so_exception_reaction() const noexcept
A reaction from SObjectizer to an exception from agent's event.
Definition agent.cpp:746
void so_deactivate_agent()
Deactivate the agent.
Definition agent.cpp:820
disp_binder_shptr_t so_this_coop_disp_binder() const
Returns the dispatcher binder that is used as the default binder for the agent's coop.
Definition agent.cpp:988
abstract_message_sink_t & detect_sink_for_message_type(const std::type_index &msg_type)
Helper function that returns a message sink to be used for subscriptions for specified message type.
Definition agent.cpp:1153
static void demand_handler_on_message(current_thread_id_t working_thread_id, execution_demand_t &d)
Calls event handler for message.
Definition agent.cpp:1371
virtual void so_evt_finish()
Hook of agent finish in SObjectizer.
Definition agent.cpp:707
static demand_handler_pfn_t get_demand_handler_on_finish_ptr() noexcept
Definition agent.cpp:1365
void so_add_nondestroyable_listener(agent_state_listener_t &state_listener)
Add a state listener to the agent.
Definition agent.cpp:728
static const impl::event_handler_data_t * handler_finder_msg_tracing_disabled(execution_demand_t &demand, const char *context_marker)
Handler finder for the case when message delivery tracing is disabled.
Definition agent.cpp:1559
const state_t * m_current_state_ptr
Current agent state.
Definition agent.hpp:2777
const mbox_t m_direct_mbox
A direct mbox for the agent.
Definition agent.hpp:2889
agent_t * self_ptr()
Definition agent.hpp:840
const priority_t m_priority
Priority of the agent.
Definition agent.hpp:2918
agent_identity_t so_agent_name() const noexcept
Get an optional name of the agent.
Definition agent.cpp:1000
static const impl::event_handler_data_t * handler_finder_msg_tracing_enabled(execution_demand_t &demand, const char *context_marker)
Handler finder for the case when message delivery tracing is enabled.
Definition agent.cpp:1572
void bind_to_coop(coop_t &coop)
Bind agent to the cooperation.
Definition agent.cpp:1023
static const impl::event_handler_data_t * find_event_handler_for_current_state(execution_demand_t &demand)
Actual search for event handler with respect to parent-child relationship between agent states.
Definition agent.cpp:1606
static void demand_handler_on_start(current_thread_id_t working_thread_id, execution_demand_t &d)
Calls so_evt_start method for agent.
Definition agent.cpp:1287
const mbox_t & so_direct_mbox() const
Get the agent's direct mbox.
Definition agent.cpp:762
impl::subscription_storage_unique_ptr_t m_subscriptions
All agent's subscriptions.
Definition agent.hpp:2830
void so_destroy_deadletter_subscription(const mbox_t &mbox, const std::type_index &msg_type)
Destroy a subscription for a deadletter handler.
Definition agent.cpp:1139
static void demand_handler_on_finish(current_thread_id_t working_thread_id, execution_demand_t &d)
Calls so_evt_finish method for agent.
Definition agent.cpp:1329
bool do_check_deadletter_presence(const mbox_t &mbox, const std::type_index &msg_type) const noexcept
Check the presence of a deadletter handler.
Definition agent.cpp:1209
void do_drop_subscription_for_all_states(const mbox_t &mbox, const std::type_index &msg_type)
Remove subscription for all states.
Definition agent.cpp:1184
agent_t(context_t ctx)
Constructor which simplifies agent construction with or without agent's tuning options.
Definition agent.cpp:659
static agent_tuning_options_t tuning_options()
Create tuning options object with default values.
Definition agent.hpp:1103
void so_add_destroyable_listener(agent_state_listener_unique_ptr_t state_listener)
Add a state listener to the agent.
Definition agent.cpp:737
environment_t & so_environment() const noexcept
Access to the SObjectizer Environment which this agent is belong.
Definition agent.cpp:853
const state_t & so_default_state() const
Access to the agent's default state.
Definition agent.cpp:775
subscription_bind_t so_subscribe(const mbox_t &mbox_ref)
Initiate subscription.
Definition agent.hpp:1359
virtual ~agent_t()
Definition agent.cpp:693
void so_bind_to_dispatcher(event_queue_t &queue) noexcept
Binding agent to the dispatcher.
Definition agent.cpp:872
environment_t & m_env
SObjectizer Environment for which the agent is belong.
Definition agent.hpp:2846
void so_deregister_agent_coop(int dereg_reason)
A helper method for deregistering agent's coop.
Definition agent.cpp:975
void do_drop_subscription(const mbox_t &mbox, const std::type_index &msg_type, const state_t &target_state)
Remove subscription for the state specified.
Definition agent.cpp:1169
agent_t(environment_t &env, agent_tuning_options_t tuning_options)
Constructor which allows specification of agent's tuning options.
Definition agent.cpp:652
handler_finder_t m_handler_finder
Function for searching event handler.
Definition agent.hpp:2823
virtual void so_evt_start()
Hook on agent start inside SObjectizer.
Definition agent.cpp:701
static demand_handler_pfn_t get_demand_handler_on_enveloped_msg_ptr() noexcept
Definition agent.cpp:1406
void so_create_event_subscription(const mbox_t &mbox_ref, std::type_index type_index, const state_t &target_state, const event_handler_method_t &method, thread_safety_t thread_safety, event_handler_kind_t handler_kind)
Create a subscription for an event.
Definition agent.cpp:1083
static void demand_handler_on_enveloped_msg(current_thread_id_t working_thread_id, execution_demand_t &d)
Handles the enveloped message.
Definition agent.cpp:1394
A collector for agent tuning options.
so_5::priority_t query_priority() const noexcept
Get priority value.
name_for_agent_t giveout_agent_name() noexcept
Gives away the name for an agent.
const custom_direct_mbox_factory_t & query_custom_direct_mbox_factory() const noexcept
Get a reference to custom direct mbox factory.
bool is_user_provided_subscription_storage_factory() const noexcept
Does a user provide a specific subscription_storage_factory?
const subscription_storage_factory_t & query_subscription_storage_factory() const noexcept
message_limit::description_container_t giveout_message_limits()
Type of smart handle for a cooperation.
Agent cooperation.
Definition coop.hpp:389
exception_reaction_t exception_reaction() const noexcept
Get the current exception rection flag for that cooperation.
Definition coop.hpp:758
std::mutex m_lock
A lock for synchonization of some operations on coop.
Definition coop.hpp:1077
disp_binder_shptr_t coop_disp_binder() const noexcept(noexcept(disp_binder_shptr_t{ this->m_coop_disp_binder }))
Return the default dispatcher binder for the coop.
Definition coop.hpp:1316
coop_handle_t handle() noexcept
Get handle for this coop.
Definition coop.hpp:432
virtual void access_hook(access_context_t context, handler_invoker_t &invoker) noexcept=0
An implementation of handler_invoker interface.
agent_demand_handler_invoker_t(current_thread_id_t work_thread_id, execution_demand_t &demand, const so_5::impl::event_handler_data_t &handler_data)
Initializing constructor.
SObjectizer Environment.
void deregister_coop(coop_handle_t coop, int reason) noexcept
Deregister the cooperation.
An interface of event queue for agent.
virtual void push(execution_demand_t demand)=0
Enqueue new event to the queue.
virtual void push_evt_finish(execution_demand_t demand) noexcept=0
Enqueue a demand for evt_finish event.
virtual void push_evt_start(execution_demand_t demand)=0
Enqueue a demand for evt_start event.
A hint for a dispatcher for execution of event for the concrete execution_demand.
execution_hint_t(execution_demand_t &demand, direct_func_t direct_func, thread_safety_t thread_safety)
Initializing constructor.
static execution_hint_t create_empty_execution_hint(execution_demand_t &demand)
A special class for accessing private members of agent_coop.
static void decrement_usage_count(coop_t &coop)
static void increment_usage_count(coop_t &coop) noexcept
Storage for message delivery filters.
void drop_all() noexcept
Drop all defined filters.
void set_delivery_filter(const mbox_t &mbox, const std::type_index &msg_type, delivery_filter_unique_ptr_t filter, so_5::outliving_reference_t< abstract_message_sink_t > owner)
Set a delivery filter.
void drop_delivery_filter(const mbox_t &mbox, const std::type_index &msg_type) noexcept
Remove delivery filter.
A helper class for accessing the functionality of environment-class which is specific for SObjectizer...
event_queue_t * event_queue_on_bind(agent_t *agent, event_queue_t *original_queue) noexcept
Call the event_queue_hook when an agent is being bound to a particular event_queue.
subscription_storage_factory_t default_subscription_storage_factory() const noexcept(noexcept(subscription_storage_factory_t{}=subscription_storage_factory_t{}))
Get the default storage subscription factory.
void event_queue_on_unbind(agent_t *agent, event_queue_t *queue) noexcept
Call the event_queue_hook when an agent is being unbound from its event_queue.
mbox_t create_ordinary_mpsc_mbox(agent_t &single_consumer)
Create multi-producer/single-consumer mbox that handles message limits.
bool is_msg_tracing_enabled() const
Is message delivery tracing enabled?
internal_env_iface_t(environment_t &env)
Initializing constructor.
mbox_t create_limitless_mpsc_mbox(agent_t &single_consumer)
Create multi-producer/single-consumer mbox that ignores message limits.
virtual abstract_message_sink_t * find_or_create(const std::type_index &msg_type)=0
void add(internal_state_listener_unique_ptr_t listener)
Add a new listener.
static auto wrap_nondestroyable(agent_state_listener_t &listener)
void changed(agent_t &agent, const state_t &state) noexcept
Handle state change.
static auto wrap_destroyable(agent_state_listener_unique_ptr_t listener)
agent_t::agent_status_t m_previous_status
Definition agent.cpp:785
state_switch_guard_t(agent_t &agent)
Definition agent.cpp:788
virtual const event_handler_data_t * find_handler(mbox_id_t mbox_id, const std::type_index &msg_type, const state_t &current_state) const noexcept=0
virtual void drop_subscription(const mbox_t &mbox, const std::type_index &msg_type, const state_t &target_state) noexcept=0
virtual void drop_subscription_for_all_states(const mbox_t &mbox, const std::type_index &msg_type) noexcept=0
virtual void create_event_subscription(const mbox_t &mbox, const std::type_index &msg_type, abstract_message_sink_t &message_sink, const state_t &target_state, const event_handler_method_t &method, thread_safety_t thread_safety, event_handler_kind_t handler_kind)=0
virtual void drop_all_subscriptions() noexcept=0
Drop all subscriptions.
intrusive_ptr_t(T *obj) noexcept
Constructor for a raw pointer.
intrusive_ptr_t(intrusive_ptr_t &&o) noexcept
Move constructor.
T * operator->() const noexcept
intrusive_ptr_t & operator=(intrusive_ptr_t &&o) noexcept
Move operator.
T & operator*() const noexcept
intrusive_ptr_t() noexcept
Default constructor.
A base class for agent messages.
Definition message.hpp:47
friend message_kind_t message_kind(const message_t &what)
Helper method for quering kind of the message.
Definition message.hpp:174
A message wrapped to be used as type of argument for event handlers.
Definition mhood.hpp:570
Type for holding agent name.
unsigned int m_length
Name length.
name_for_agent_t & operator=(name_for_agent_t &&other) noexcept
Definition agent.cpp:142
name_for_agent_t(const name_for_agent_t &)
Definition agent.cpp:115
name_for_agent_t(name_for_agent_t &&other) noexcept
Definition agent.cpp:136
std::unique_ptr< char[] > m_value
Name storage.
bool has_value() const noexcept
Does this object have a value?
Definition agent.cpp:169
name_for_agent_t & operator=(const name_for_agent_t &)
Definition agent.cpp:129
std::string_view as_string_view() const
Get the value as a string_view.
Definition agent.cpp:160
name_for_agent_t()
Default constructor makes an null value.
Definition agent.cpp:104
name_for_agent_t(std::string_view value)
Initializing constructor.
Definition agent.cpp:108
Wrapper around a pointer to partially constructed agent.
Scoped guard for shared locks.
bool is_target(const agent_t *agent) const noexcept
Is agent owner of this state?
Definition agent.cpp:463
state_t & time_limit(duration_t timeout, const state_t &state_to_switch)
Set up a time limit for the state.
Definition agent.cpp:480
state_t & drop_time_limit()
Drop time limit for the state if defined.
Definition agent.cpp:514
on_exit_handler_t m_on_exit
Handler for the exit from the state.
Definition state.hpp:1713
history_t m_state_history
Type of state history.
Definition state.hpp:1664
const state_t * parent_state() const noexcept
Get a parent state if exists.
Definition state.hpp:1748
void fill_path(path_t &path) const noexcept
A helper method for building a path from top-level state to this state.
Definition state.hpp:1793
state_t(initial_substate_of parent, std::string state_name, history_t state_history)
Constructor for the case when state is the initial substate of some parent state.
Definition agent.cpp:338
state_t(substate_of parent)
Constructor for the case when state is a substate of some parent state.
Definition agent.cpp:358
std::size_t nested_level() const noexcept
Query nested level for the state.
Definition state.hpp:1780
bool is_active() const noexcept
Is this state or any of its substates activated?
Definition agent.hpp:3751
state_t(state_t &&other)
Move constructor.
Definition agent.cpp:381
state_t(agent_t *target_agent, std::string state_name, state_t *parent_state, std::size_t nested_level, history_t state_history)
Fully initialized constructor.
Definition agent.cpp:273
std::string m_state_name
State name.
Definition state.hpp:1629
state_t(agent_t *agent, std::string state_name, history_t state_history)
Definition agent.cpp:320
state_t * m_parent_state
Parent state.
Definition state.hpp:1643
state_t(substate_of parent, std::string state_name, history_t state_history)
Constructor for the case when state is a substate of some parent state.
Definition agent.cpp:369
const state_t * actual_state_to_enter() const
Find actual state to be activated for agent.
Definition agent.cpp:526
state_t(initial_substate_of parent)
Constructor for the case when state is the initial substate of some parent state.
Definition agent.cpp:327
const state_t * m_initial_substate
The initial substate.
Definition state.hpp:1656
bool operator==(const state_t &state) const noexcept
Definition agent.cpp:403
state_t(initial_substate_of parent, std::string state_name)
Constructor for the case when state is the initial substate of some parent state.
Definition agent.cpp:332
size_t m_substate_count
Number of substates.
Definition state.hpp:1697
state_t(agent_t *agent, history_t state_history)
Definition agent.cpp:307
void call_on_enter() const noexcept
Call for on enter handler if defined.
Definition state.hpp:1837
bool operator!=(const state_t &state) const noexcept
Definition state.hpp:372
void call_on_exit() const noexcept
Call for on exit handler if defined.
Definition state.hpp:1850
agent_t *const m_target_agent
Owner of this state.
Definition state.hpp:1623
std::string query_name() const
Get textual name of the state.
Definition agent.cpp:409
static constexpr const std::size_t max_deep
Max deep of nested states.
Definition state.hpp:163
state_t(substate_of parent, std::string state_name)
Constructor for the case when state is a substate of some parent state.
Definition agent.cpp:363
const state_t * m_last_active_substate
Last active substate.
Definition state.hpp:1676
void handle_time_limit_on_enter() const
A special handler of time limit to be used on entering into state.
Definition agent.cpp:570
state_t(agent_t *agent, std::string state_name)
Definition agent.cpp:314
void activate() const
Switch agent to that state.
Definition agent.cpp:474
history_t
Type of history for state.
Definition state.hpp:172
@ none
State has no history.
@ deep
State has deep history.
@ shallow
State has shallow history.
on_enter_handler_t m_on_enter
Handler for the enter to the state.
Definition state.hpp:1705
std::unique_ptr< time_limit_t > m_time_limit
A definition of time limit for the state.
Definition state.hpp:1723
std::size_t m_nested_level
Nesting level for state.
Definition state.hpp:1686
void update_history_in_parent_states() const
A helper method which is used during state change for update state with history.
Definition agent.cpp:549
void handle_time_limit_on_exit() const
A special handler of time limit to be used on exiting from state.
Definition agent.cpp:576
state_t(agent_t *agent)
Definition agent.cpp:301
std::enable_if< details::lambda_traits::is_lambda< Lambda >::value, subscription_bind_t & >::type event(Lambda &&lambda, thread_safety_t thread_safety=not_thread_safe)
Make subscription to the message by lambda-function.
Definition agent.hpp:3510
subscription_bind_t & in(const state_t &state)
Set up a state in which events are allowed be processed.
Definition agent.hpp:3469
An indentificator for the timer.
Definition timers.hpp:73
void release() noexcept
Release the timer event.
Definition timers.hpp:99
#define SO_5_FUNC
Definition declspec.hpp:48
#define SO_5_LOG_ERROR(logger, var_name)
A special macro for helping error logging.
#define SO_5_THROW_EXCEPTION(error_code, desc)
Definition exception.hpp:74
demand_handler_pfn_t select_demand_handler_for_message(const agent_t &agent, const message_ref_t &msg)
A helper function to select actual demand handler in dependency of message kind.
Definition agent.cpp:1227
mbox_t make_direct_mbox_with_respect_to_custom_factory(partially_constructed_agent_ptr_t agent_ptr, const agent_tuning_options_t &tuning_options, mbox_t standard_mbox)
Helper for creation of the direct mbox for an agent.
Definition agent.cpp:593
unsigned int ensure_valid_agent_name_length(std::size_t length)
Definition agent.cpp:83
std::string create_anonymous_state_name(const agent_t *agent, const state_t *st)
Definition agent.cpp:183
const state_t deadletter_state(nullptr, "<DEADLETTER_STATE>")
A special object to be used as state for make subscriptions for deadletter handlers.
subscription_storage_factory_t detect_subscription_storage_factory_to_use(environment_t &env, const agent_tuning_options_t &tuning_options)
Helper for selection of subscription storage factory.
Definition agent.cpp:630
const state_t awaiting_deregistration_state(nullptr, "<AWAITING_DEREGISTRATION_AFTER_UNHANDLED_EXCEPTION>")
A special object for the state in which agent is awaiting for deregistration after unhandled exceptio...
Enumeration of cooperation deregistration reasons.
Definition coop.hpp:39
const int normal
Normal deregistration.
Definition coop.hpp:46
Some reusable and low-level classes/functions which can be used in public header files.
void abort_on_fatal_error(L logging_lambda) noexcept
auto do_with_rollback_on_exception(Main_Action main_action, Rollback_Action rollback_action) -> decltype(main_action())
Helper function for do some action with rollback in the case of an exception.
auto invoke_noexcept_code(L lambda) noexcept -> decltype(lambda())
envelope_t & message_to_envelope(const message_ref_t &src_msg)
A helper function for casting message instance to envelope instance.
access_context_t
Information about context on that enveloped message is handled.
Internal namespace with details of agent_t implementation.
Definition agent.hpp:462
Various helpers for message delivery tracing mechanism.
void safe_trace_state_leaving(const agent_t &state_owner, const state_t &state)
Helper for tracing the fact of leaving a state.
void safe_trace_state_entering(const agent_t &state_owner, const state_t &state)
Helper for tracing the fact of entering into a state.
void trace_deadletter_handler_search_result(const execution_demand_t &demand, const char *context_marker, const event_handler_data_t *search_result)
Helper for tracing the result of search of deadletter handler.
void trace_event_handler_search_result(const execution_demand_t &demand, const char *context_marker, const event_handler_data_t *search_result)
Helper for tracing the result of event handler search.
Details of SObjectizer run-time implementations.
Definition agent.cpp:780
void process_unhandled_unknown_exception(current_thread_id_t working_thread_id, agent_t &a_exception_producer) noexcept
Processor of unhandled exception of unknown type from agent's event handler.
void process_unhandled_exception(current_thread_id_t working_thread_id, const std::exception &ex, agent_t &a_exception_producer) noexcept
Processor of unhandled exception from agent's event handler.
static std::unique_ptr< sinks_storage_t > create_sinks_storage_if_necessary(partially_constructed_agent_ptr_t owner_ptr, so_5::message_limit::description_container_t &&descriptions)
Create info_storage object if there are some message limits.
All stuff related to message limits.
Definition message.hpp:862
Private part of message limit implementation.
Definition agent.cpp:33
const int rc_state_nesting_is_too_deep
Nesting of agent states is too deep.
Definition ret_code.hpp:59
const int rc_initial_substate_already_defined
Initial substate for a composite state is already defined.
Definition ret_code.hpp:66
mbox_type_t
Type of the message box.
Definition mbox.hpp:163
exception_reaction_t
A reaction of SObjectizer to an exception from agent event.
Definition agent.hpp:65
@ abort_on_exception
Execution of application must be aborted immediatelly.
Definition agent.hpp:67
current_thread_id_t null_current_thread_id()
Get NULL thread id.
const thread_safety_t not_thread_safe
Shorthand for thread unsafety indicator.
Definition types.hpp:62
const thread_safety_t thread_safe
Shorthand for thread safety indicator.
Definition types.hpp:69
const int rc_operation_enabled_only_on_agent_working_thread
An attempt to perform an operation which is enabled only on agent's working thread.
Definition ret_code.hpp:44
const int rc_agent_unknown_state
Trying to switch to the unknown state.
Definition ret_code.hpp:30
const int rc_invalid_time_limit_for_state
Invalid value of time limit for an agent's state.
Definition ret_code.hpp:539
thread_safety_t
Thread safety indicator.
Definition types.hpp:50
@ user_type_message
Message is an user type message.
@ enveloped_msg
Message is an envelope with some other message inside.
const int rc_empty_agent_name
Name for an agent can't be empty.
Definition ret_code.hpp:512
const int rc_agent_deactivated
Agent can't change state because the agent is already deactivated.
Definition ret_code.hpp:432
SO_5_FUNC void swap(name_for_agent_t &a, name_for_agent_t &b) noexcept
Definition agent.cpp:150
timer_id_t send_periodic(Target &&target, std::chrono::steady_clock::duration pause, std::chrono::steady_clock::duration period, Args &&... args)
A utility function for creating and delivering a periodic message to the specified destination.
const int rc_another_state_switch_in_progress
An attempt to switch agent state when another switch operation is in progress.
Definition ret_code.hpp:216
const int rc_no_initial_substate
An attempt to change agent state to a new composite state which have no initial state defined.
Definition ret_code.hpp:52
const int rc_agent_has_no_cooperation
Agent is not bound to a cooperation.
Definition ret_code.hpp:33
event_handler_kind_t
Kind of an event handler.
Definition types.hpp:154
current_thread_id_t query_current_thread_id()
Get the ID of the current thread.
const int rc_message_has_no_limit_defined
An attempt to create subscription to message without predefined limit for that message type.
Definition ret_code.hpp:123
outliving_reference_t< T > outliving_mutable(T &r)
Make outliving_reference wrapper for mutable reference.
const int rc_mpsc_mbox_expected
An instance of MPSC mbox is expected as custom direct mbox.
Definition ret_code.hpp:443
Type for case when agent has no user-provided name.
SO_5_FUNC std::array< char, c_string_size > make_c_string() const noexcept
Make a c-string with text representation of a value.
Definition agent.cpp:39
static constexpr std::string_view c_string_prefix
Prefix to be used for string representation.
static constexpr std::size_t c_string_size
static constexpr std::string_view c_string_suffix
Suffix to be used for string representation.
A description of event execution demand.
mbox_id_t m_mbox_id
ID of mbox.
void call_handler(current_thread_id_t thread_id)
Helper method to simplify demand execution.
demand_handler_pfn_t m_demand_handler
Demand handler.
agent_t * m_receiver
Receiver of demand.
message_ref_t m_message_ref
Event incident.
const message_limit::control_block_t * m_limit
Optional message limit for that message.
execution_demand_t(agent_t *receiver, const message_limit::control_block_t *limit, mbox_id_t mbox_id, std::type_index msg_type, message_ref_t message_ref, demand_handler_pfn_t demand_handler) noexcept
std::type_index m_msg_type
Type of the message.
A helper class for temporary setting and then dropping the ID of the current working thread.
Definition agent.hpp:474
working_thread_id_sentinel_t(so_5::current_thread_id_t &id_var, so_5::current_thread_id_t value_to_set)
Definition agent.hpp:477
Information about event_handler and its properties.
thread_safety_t m_thread_safety
Is event handler thread safe or not.
event_handler_method_t m_method
Method for handling event.
event_handler_kind_t m_kind
Kind of this event handler.
Helper for marking initial substate of composite state.
Definition state.hpp:57
A control block for one message limit.
Definition message.hpp:976
static const control_block_t * none()
A special indicator about absence of control_block.
Definition message.hpp:1023
static void decrement(const control_block_t *limit)
Definition message.hpp:1028
time_limit_t(duration_t limit, const state_t &state_to_switch)
Definition agent.cpp:207
void set_up_limit_for_agent(agent_t &agent, const state_t &current_state) noexcept
Definition agent.cpp:215
const state_t & m_state_to_switch
Definition agent.cpp:202
void drop_limit_for_agent(agent_t &agent, const state_t &current_state) noexcept
Definition agent.cpp:247
Helper for marking a substate of composite state.
Definition state.hpp:89
state_t * m_parent_state
Definition state.hpp:90