SObjectizer  5.8
Loading...
Searching...
No Matches
agent.hpp
Go to the documentation of this file.
1/*
2 SObjectizer 5.
3*/
4
5/*!
6 \file
7 \brief A base class for agents.
8*/
9
10#pragma once
11
12#include <so_5/compiler_features.hpp>
13#include <so_5/declspec.hpp>
14#include <so_5/types.hpp>
15#include <so_5/current_thread_id.hpp>
16#include <so_5/atomic_refcounted.hpp>
17#include <so_5/spinlocks.hpp>
18#include <so_5/outliving.hpp>
19
20#include <so_5/exception.hpp>
21#include <so_5/error_logger.hpp>
22
23#include <so_5/details/rollback_on_exception.hpp>
24#include <so_5/details/at_scope_exit.hpp>
25
26#include <so_5/fwd.hpp>
27
28#include <so_5/agent_ref_fwd.hpp>
29#include <so_5/agent_context.hpp>
30#include <so_5/agent_identity.hpp>
31#include <so_5/mbox.hpp>
32#include <so_5/agent_state_listener.hpp>
33#include <so_5/event_queue.hpp>
34#include <so_5/subscription_storage_fwd.hpp>
35#include <so_5/handler_makers.hpp>
36#include <so_5/message_handler_format_detector.hpp>
37#include <so_5/coop_handle.hpp>
38
39#include <so_5/disp_binder.hpp>
40
41#include <atomic>
42#include <map>
43#include <memory>
44#include <vector>
45#include <utility>
46#include <type_traits>
47
48#if defined( SO_5_MSVC )
49 #pragma warning(push)
50 #pragma warning(disable: 4251)
51#endif
52
53namespace so_5
54{
55
56//
57// exception_reaction_t
58//
59/*!
60 * \brief A reaction of SObjectizer to an exception from agent event.
61 *
62 * \since v.5.2.3
63 */
65{
66 //! Execution of application must be aborted immediatelly.
68 //! Agent must be switched to special state and SObjectizer
69 //! Environment will be stopped.
71 //! Agent must be switched to special state and agent's cooperation
72 //! must be deregistered.
74 //! Exception should be ignored and agent should continue its work.
76 /*!
77 * \brief Exception reaction should be inherited from SO Environment.
78 *
79 * \since v.5.3.0
80 */
82};
83
84//
85// subscription_bind_t
86//
87
88/*!
89 * \brief A class for creating a subscription to messages from the mbox.
90 *
91 * This type provides one of the ways to subscribe an agent's event handlers.
92 * There are two way to do that. The first one uses so_5::state_t::event()
93 * methods:
94 * \code
95 * class subscribe_demo : public so_5::agent_t
96 * {
97 * // Some states for the agent.
98 * state_t st_first{this}, st_second{this}, st_third{this};
99 * ...
100 * virtual void so_define_agent() override {
101 * // Subscribe just one event handler for st_first.
102 * st_first.event(some_mbox, &subscribe_demo::event_handler_1);
103 *
104 * // Subscribe two event handlers for st_second.
105 * st_second
106 * .event(some_mbox, &subscribe_demo::event_handler_1)
107 * .event(some_mbox, &subscribe_demo::event_handler_2);
108 *
109 * // Subscribe two event handlers for st_third.
110 * st_third
111 * .event(some_mbox, &subscribe_demo::event_handler_1)
112 * .event(some_mbox, &subscribe_demo::event_handler_3)
113 * }
114 * };
115 * \endcode
116 * But this way do not allow to subscribe the same event handler for
117 * several states in the compact way.
118 *
119 * This can be done via agent_t::so_subscribe(), agent_t::so_subscribe_self()
120 * and subscription_bind_t object:
121 * \code
122 * class subscribe_demo : public so_5::agent_t
123 * {
124 * // Some states for the agent.
125 * state_t st_first{this}, st_second{this}, st_third{this};
126 * ...
127 * virtual void so_define_agent() override {
128 * // Subscribe event_handler_1 for all three states
129 * so_subscribe(some_mbox)
130 * .in(st_first)
131 * .in(st_second)
132 * .in(st_third)
133 * .event(&subscribe_demo::event_handler_1);
134 *
135 * // Subscribe just one event handler for st_second and st_third.
136 * so_subscribe(some_mbox)
137 * .in(st_second)
138 * .event(&subscribe_demo::event_handler_2);
139 *
140 * // Subscribe two event handlers for st_third.
141 * so_subscribe(some_mbox)
142 * .in(st_third)
143 * .event(&subscribe_demo::event_handler_3)
144 * }
145 * };
146 * \endcode
147 *
148 * \par Some words about binder logic...
149 * An object of type subscription_bind_t collects list of states
150 * enumerated by calls to subscription_bind_t::in() method.
151 * Every call to in() method add a state to that list. It means:
152 * \code
153 * so_subscribe(some_mbox) // list is: {}
154 * .in(st_first) // list is: {st_first}
155 * .in(st_second) // list is: {st_first, st_second}
156 * .in(st_third) // list is: {st_first, st_second, st_third}
157 * ...
158 * \endcode
159 * A call to event() or suppress() or just_switch_to() applies subscription
160 * to all states which are currently in the list. But these calls do not
161 * remove the content of that list. It means:
162 * \code
163 * so_subscribe(some_mbox) // list is: {}
164 * .in(st_first) // list is: {st_first}
165 * .event(handler_1) // subscribe for state st_first only.
166 * .in(st_second) // list is: {st_first, st_second}
167 * .event(handler_2) // subscribe for state st_first and for st_second.
168 * .in(st_third) // list is: {st_first, st_second, st_third}
169 * .event(handler_3) // subscribe for state st_first, st_second and st_third.
170 * ...
171 * \endcode
172 */
174{
175 public:
176 inline
178 //! Agent to subscribe.
179 agent_t & agent,
180 //! Mbox for messages to be subscribed.
181 const mbox_t & mbox_ref );
182
183 //! Set up a state in which events are allowed be processed.
184 inline subscription_bind_t &
185 in(
186 //! State in which events are allowed.
187 const state_t & state );
188
189 //! Make subscription to the message.
190 /*!
191 * \note Can be used for message and signal handlers.
192 *
193 * \par Usage example
194 * \code
195 struct engine_control : public so_5::message_t { ... };
196 struct check_status : public so_5::signal_t {};
197 class engine_controller : public so_5::agent_t
198 {
199 public :
200 virtual void so_define_agent() override
201 {
202 so_subscribe_self()
203 .event( &engine_controller::control )
204 .event( &engine_controller::check_status );
205 .event( &engine_controller::accelerate );
206 ...
207 }
208 ...
209 private :
210 void control( so_5::mhood_t< engine_control > & cmd ) { ... }
211 void check_status( so_5::mhood_t< check_status > & cmd ) { ... }
212 void accelerate( so_5::mhood_t< int > & cmd ) { ... }
213 };
214 * \endcode
215 *
216 * \since v.5.5.14
217 */
218 template< typename Method_Pointer >
219 typename std::enable_if<
224 event(
225 //! Event handling method.
227 //! Thread safety of the event handler.
229
230 /*!
231 * \brief Make subscription to the message by lambda-function.
232 *
233 * \attention Only lambda-function in the forms:
234 * \code
235 Result (const Message &)
236 Result (Message)
237 Result (so_5::mhood_t<Message>)
238 Result (const so_5::mhood_t<Message> &)
239 * \endcode
240 * are supported.
241 *
242 * \par Usage example.
243 * \code
244 enum class engine_control { turn_on, turn_off, slow_down };
245 struct setup_params : public so_5::message_t { ... };
246 struct update_settings { ... };
247
248 class engine_controller : public so_5::agent_t
249 {
250 public :
251 virtual void so_define_agent() override
252 {
253 so_subscribe_self()
254 .event( [this]( engine_control evt ) {...} )
255 .event( [this]( const setup_params & evt ) {...} )
256 .event( [this]( const update_settings & evt ) {...} )
257 ...
258 }
259 ...
260 };
261 * \endcode
262 *
263 * \since v.5.3.0
264 */
265 template< class Lambda >
266 typename std::enable_if<
269 event(
270 //! Event handler code.
271 Lambda && lambda,
272 //! Thread safety of the event handler.
274
275 /*!
276 * \brief An instruction for switching agent to the specified
277 * state and transfering event proceessing to new state.
278 *
279 * \par Usage example:
280 * \code
281 class device : public so_5::agent_t {
282 state_t off{ this, "off" };
283 state_t on{ this, "on" };
284 public :
285 virtual void so_define_agent() override {
286 so_subscribe_self().in( off )
287 .transfer_to_state< key_on >( on )
288 .transfer_to_state< key_info >( on );
289 }
290 ...
291 };
292 * \endcode
293 *
294 * \note Event will not be postponed or returned to event queue.
295 * A search for a handler for this event will be performed immediately
296 * after switching to the new state.
297 *
298 * \note New state can use transfer_to_state for that event too:
299 * \code
300 class device : public so_5::agent_t {
301 state_t off{ this, "off" };
302 state_t on{ this, "on" };
303 state_t status_dialog{ this, "status" };
304 public :
305 virtual void so_define_agent() override {
306 so_subscribe_self().in( off )
307 .transfer_to_state< key_on >( on )
308 .transfer_to_state< key_info >( on );
309
310 so_subscribe_self().in( on )
311 .transfer_to_state< key_info >( status_dialog )
312 ...;
313 }
314 ...
315 };
316 * \endcode
317 *
318 * \note Since v.5.5.22.1 actual execution of transfer_to_state operation
319 * can raise so_5::exception_t with so_5::rc_transfer_to_state_loop
320 * error code if a loop in transfer_to_state is detected.
321 *
322 * \since v.5.5.15
323 */
324 template< typename Msg >
327 const state_t & target_state );
328
329 /*!
330 * \brief Suppress processing of event in this state.
331 *
332 * \note This method is useful because the event is not passed to
333 * event handlers from parent states. For example:
334 * \code
335 class demo : public so_5::agent_t
336 {
337 state_t S1{ this, "1" };
338 state_t S2{ initial_substate_of{ S1 }, "2" };
339 state_t S3{ initial_substate_of{ S2 }, "3" };
340 public :
341 virtual void so_define_agent() override
342 {
343 so_subscribe_self().in( S1 )
344 // Default event handler which will be inherited by states S2 and S3.
345 .event< msg1 >(...)
346 .event< msg2 >(...)
347 .event< msg3 >(...);
348
349 so_subscribe_self().in( S2 )
350 // A special handler for msg1.
351 // For msg2 and msg3 event handlers from state S1 will be used.
352 .event< msg1 >(...);
353
354 so_subscribe_self().in( S3 )
355 // Message msg1 will be suppressed. It will be simply ignored.
356 // No events from states S1 and S2 will be called.
357 .suppress< msg1 >()
358 // The same for msg2.
359 .suppress< msg2 >()
360 // A special handler for msg3. Overrides handler from state S1.
361 .event< msg3 >(...);
362 }
363 };
364 * \endcode
365 *
366 * \since v.5.5.15
367 */
368 template< typename Msg >
370 suppress();
371
372 /*!
373 * \brief Define handler which only switches agent to the specified
374 * state.
375 *
376 * \note This method differes from transfer_to_state() method:
377 * just_switch_to() changes state of the agent, but there will not be a
378 * look for event handler for message/signal in the new state. It means
379 * that just_switch_to() is just a shorthand for:
380 * \code
381 virtual void demo::so_define_agent() override
382 {
383 so_subscribe_self().in( S1 )
384 .event< some_signal >( [this]{ this >>= S2; } );
385 }
386 * \endcode
387 * With just_switch_to() this code can looks like:
388 * \code
389 virtual void demo::so_define_agent() override
390 {
391 so_subscribe_self().in( S1 )
392 .just_switch_to< some_signal >( S2 );
393 }
394 * \endcode
395 *
396 * \since v.5.5.15
397 */
398 template< typename Msg >
401 const state_t & target_state );
402
403 private:
404 //! Agent to which we are subscribing.
406 //! Mbox for messages to subscribe.
408
409 /*!
410 * \brief Type of vector of states.
411 *
412 * \since v.5.3.0
413 */
414 typedef std::vector< const state_t * > state_vector_t;
415
416 /*!
417 * \brief States of agents the event to be subscribed in.
418 *
419 * \since v.5.3.0
420 */
422
423 /*!
424 * \brief Create subscription of event for all states.
425 *
426 * \since v.5.3.0
427 */
428 void
430 const std::type_index & msg_type,
431 const event_handler_method_t & method,
432 thread_safety_t thread_safety,
433 event_handler_kind_t handler_kind ) const;
434
435 /*!
436 * \brief Additional check for subscription to a mutable message
437 * from MPMC mbox.
438 *
439 * Such attempt must be disabled because delivery of mutable
440 * messages via MPMC mboxes is prohibited.
441 *
442 * \throw so_5::exception_t if m_mbox_ref is a MPMC mbox and
443 * \a handler is for mutable message.
444 *
445 * \since v.5.5.19
446 */
447 void
449 const so_5::details::msg_type_and_handler_pair_t & handler ) const;
450};
451
452/*!
453 * \brief Internal namespace with details of agent_t implementation.
454 *
455 * \attention
456 * Nothing from that namespace can be used in user code. All of this is an
457 * implementation detail and is subject to change without any prior notice.
458 *
459 * \since v.5.8.0
460 */
461namespace impl::agent_impl
462{
463
464/*!
465 * \brief A helper class for temporary setting and then dropping
466 * the ID of the current working thread.
467 *
468 * \note New working thread_id is set only if it is not an
469 * null thread_id.
470 *
471 * \since v.5.4.0
472 */
474 {
476
478 so_5::current_thread_id_t & id_var,
479 so_5::current_thread_id_t value_to_set )
480 : m_id( id_var )
481 {
482 if( value_to_set != null_current_thread_id() )
483 m_id = value_to_set;
484 }
486 {
487 if( m_id != null_current_thread_id() )
488 m_id = null_current_thread_id();
489 }
490 };
491
492} /* namespace impl::agent_impl */
493
494//
495// agent_t
496//
497
498//! A base class for agents.
499/*!
500 An agent in SObjctizer must be derived from the agent_t.
501
502 The base class provides various methods whose can be splitted into
503 the following groups:
504 \li methods for the interaction with SObjectizer;
505 \li predefined hook-methods which are called during: cooperation
506 registration, starting and stopping of an agent;
507 \li methods for the message subscription and unsubscription;
508 \li methods for working with an agent state;
509
510 <b>Methods for the interaction with SObjectizer</b>
511
512 Method so_5::agent_t::so_environment() serves for the access to the
513 SObjectizer Environment (and, therefore, to all methods of the
514 SObjectizer Environment).
515 This method could be called immediatelly after the agent creation.
516 This is because agent is bound to the SObjectizer Environment during
517 the creation process.
518
519 <b>Hook methods</b>
520
521 The base class defines several hook-methods. Its default implementation
522 do nothing.
523
524 The method agent_t::so_define_agent() is called just before agent will
525 started by SObjectizer as a part of the agent registration process.
526 It should be reimplemented for the initial subscription of the agent
527 to messages.
528
529 There are two hook-methods related to important agent's lifetime events:
530 agent_t::so_evt_start() and agent_t::so_evt_finish(). They are called
531 by SObjectizer in next circumstances:
532 - method so_evt_start() is called when the agent is starting its work
533 inside of SObjectizer. At that moment all agents are defined (all
534 their methods agent_t::so_define_agent() have executed);
535 - method so_evt_finish() is called during the agent's cooperation
536 deregistration just after agent processed the last pending event.
537
538 Methods so_evt_start() and so_evt_finish() are called by SObjectizer and
539 user can just reimplement them to implement the agent-specific logic.
540
541 <b>Message subscription and unsubscription methods</b>
542
543 Any method with one of the following prototypes can be used as an event
544 handler:
545 \code
546 return_type evt_handler( mhood_t< Message > msg );
547 return_type evt_handler( const mhood_t< Message > & msg );
548 return_type evt_handler( const Message & msg );
549 return_type evt_handler( Message msg );
550 // Since v.5.5.20:
551 return_type evt_handler( mhood_t< Message > msg ) const;
552 return_type evt_handler( const mhood_t< Message > & msg ) const;
553 return_type evt_handler( const Message & msg ) const;
554 return_type evt_handler( Message msg ) const;
555 \endcode
556 Where \c evt_handler is a name of the event handler, \c Message is a
557 message type.
558
559 The class mhood_t is a wrapper on pointer to an instance
560 of the \c Message. It is very similar to <tt>std::unique_ptr</tt>.
561 The pointer to \c Message can be a nullptr. It happens in case when
562 the message has no actual data and servers just a signal about something.
563
564 Please note that handlers with the following prototypes can be used
565 only for messages, not signals:
566 \code
567 return_type evt_handler( const Message & msg );
568 return_type evt_handler( Message msg );
569 // Since v.5.5.20:
570 return_type evt_handler( const Message & msg ) const;
571 return_type evt_handler( Message msg ) const;
572 \endcode
573
574 A subscription to the message is performed by the methods so_subscribe()
575 and so_subscribe_self().
576 This method returns an instance of the so_5::subscription_bind_t which
577 does all actual actions of the subscription process. This instance already
578 knows agents and message mbox and uses the default agent state for
579 the event subscription (binding to different state is also possible).
580
581 The presence of a subscription can be checked by so_has_subscription()
582 method.
583
584 A subscription can be dropped (removed) by so_drop_subscription() and
585 so_drop_subscription_for_all_states() methods.
586
587 <b>Deadletter handlers subscription and unsubscription</b>
588
589 Since v.5.5.21 SObjectizer supports deadletter handlers. Such handlers
590 are called if there is no any ordinary event handler for a specific
591 messages from a specific mbox.
592
593 Deadletter handler can be implemented by an agent method or by lambda
594 function. Deadletter handler can have one of the following formats:
595 \code
596 void evt_handler( mhood_t< Message > msg );
597 void return_type evt_handler( mhood_t< Message > msg ) const;
598 void return_type evt_handler( const mhood_t< Message > & msg );
599 void return_type evt_handler( const mhood_t< Message > & msg ) const;
600 void return_type evt_handler( const Message & msg );
601 void return_type evt_handler( const Message & msg ) const;
602 void return_type evt_handler( Message msg );
603 void return_type evt_handler( Message msg ) const;
604 \endcode
605
606 Subscription for a deadletter handler can be created by
607 so_subscribe_deadletter_handler() method.
608
609 The presence of a deadletter handler can be checked by
610 so_has_deadletter_handler() method.
611
612 A deadletter can be dropped (removed) by so_drop_deadletter_handler()
613 and so_drop_subscription_for_all_states() methods.
614
615 <b>Methods for working with an agent state</b>
616
617 The agent can change its state by his so_change_state() method.
618
619 An attempt to switch an agent to the state which belongs to the another
620 agent is an error. If state is belong to the same agent there are
621 no possibility to any run-time errors. In this case changing agent
622 state is a very safe operation.
623
624 In some cases it is necessary to detect agent state switching.
625 For example for application monitoring purposes. This can be done
626 by "state listeners".
627
628 Any count of state listeners can be set for an agent. There are
629 two methods for that:
630 - so_add_nondestroyable_listener() is for listeners whose lifetime
631 are controlled by a programmer, not by SObjectizer;
632 - so_add_destroyable_listener() is for listeners whose lifetime
633 must be controlled by agent itself.
634
635 <b>Work thread identification</b>
636
637 Since v.5.4.0 some operations for agent are enabled only on agent's
638 work thread. They are:
639 - subscription management operations (creation or dropping);
640 - changing agent's state.
641
642 Work thread for an agent is defined as follows:
643 - before invocation of so_define_agent() the work thread is a
644 thread on which agent is created (id of that thread is detected in
645 agent's constructor);
646 - during cooperation registration the working thread is a thread on
647 which so_environment::register_coop() is working;
648 - after successful agent registration the work thread for it is
649 specified by the dispatcher.
650
651 \note Some dispatchers could provide several work threads for
652 an agent. In such case there would not be work thread id. And
653 operations like changing agent state or creation of subscription
654 would be prohibited after agent registration.
655
656 <b>Accessing dispatcher binders</b>
657
658 Since v.5.8.1 there are two methods that allow to get a dispatcher binder
659 related to the agent or agent's coop:
660
661 - so_this_agent_disp_binder(). It returns the dispatcher binder that is
662 used for binding the agent itself;
663 - so_this_coop_disp_binder(). It returns the dispatcher binder that is the
664 default disp binder for the agent's coop.
665
666 Please note that binders returned by so_this_agent_disp_binder() and
667 so_this_coop_disp_binder() may be different binders.
668*/
670 : private atomic_refcounted_t
673{
675 friend class state_t;
676
677 friend class so_5::impl::mpsc_mbox_t;
679 friend class so_5::impl::internal_agent_iface_t;
680
682
683 template< typename T >
684 friend class intrusive_ptr_t;
685
686 public:
687 /*!
688 * \brief Short alias for agent_context.
689 *
690 * \since v.5.5.4
691 */
692 using context_t = so_5::agent_context_t;
693 /*!
694 * \brief Short alias for %so_5::state_t.
695 *
696 * \since v.5.5.13
697 */
698 using state_t = so_5::state_t;
699 /*!
700 * \brief Short alias for %so_5::mhood_t.
701 *
702 * \since v.5.5.14
703 */
704 template< typename T >
705 using mhood_t = so_5::mhood_t< T >;
706 /*!
707 * \brief Short alias for %so_5::mutable_mhood_t.
708 *
709 * \since v.5.5.19
710 */
711 template< typename T >
713 /*!
714 * \brief Short alias for %so_5::initial_substate_of.
715 *
716 * \since v.5.5.15
717 */
718 using initial_substate_of = so_5::initial_substate_of;
719 /*!
720 * \brief Short alias for %so_5::substate_of.
721 *
722 * \since v.5.5.15
723 */
724 using substate_of = so_5::substate_of;
725 /*!
726 * \brief Short alias for %so_5::state_t::history_t::shallow.
727 *
728 * \since v.5.5.15
729 */
730 static constexpr const state_t::history_t shallow_history =
732 /*!
733 * \brief Short alias for %so_5::state_t::history_t::deep.
734 *
735 * \since v.5.5.15
736 */
737 static constexpr const state_t::history_t deep_history =
738 state_t::history_t::deep;
739
740 //! Constructor.
741 /*!
742 Agent must be bound to the SObjectizer Environment during
743 its creation. And that binding cannot be changed anymore.
744 */
745 explicit agent_t(
746 //! The Environment for this agent must exist.
747 environment_t & env );
748
749 /*!
750 * \brief Constructor which allows specification of
751 * agent's tuning options.
752 *
753 * \par Usage sample:
754 \code
755 using namespace so_5;
756 class my_agent : public agent_t
757 {
758 public :
759 my_agent( environment_t & env )
760 : agent_t( env, agent_t::tuning_options()
761 .subscription_storage_factory(
762 vector_based_subscription_storage_factory() ) )
763 {...}
764 }
765 \endcode
766 *
767 * \since v.5.5.3
768 */
769 agent_t(
770 environment_t & env,
771 agent_tuning_options_t tuning_options );
772
773 /*!
774 * \brief Constructor which simplifies agent construction with
775 * or without agent's tuning options.
776 *
777 * \par Usage sample:
778 * \code
779 class my_agent : public so_5::agent_t
780 {
781 public :
782 my_agent( context_t ctx )
783 : so_5::agent( ctx + limit_then_drop< get_status >(1) )
784 {}
785 ...
786 };
787 class my_more_specific_agent : public my_agent
788 {
789 public :
790 my_more_specific_agent( context_t ctx )
791 : my_agent( ctx + limit_then_drop< reconfigure >(1) )
792 {}
793 };
794
795 // Then somewhere in the code:
796 auto coop = env.make_coop();
797 auto a = coop->make_agent< my_agent >();
798 auto b = coop->make_agent< my_more_specific_agent >();
799 * \endcode
800 *
801 * \since v.5.5.4
802 */
803 explicit agent_t( context_t ctx );
804
805 virtual ~agent_t();
806
807 //! Get the raw pointer of itself.
808 /*!
809 This method is intended for use in the member initialization
810 list instead 'this' to suppres compiler warnings.
811 For example for an agent state initialization:
812 \code
813 class a_sample_t : public so_5::agent_t
814 {
815 typedef so_5::agent_t base_type_t;
816
817 // Agent state.
818 const so_5::state_t m_sample_state;
819 public:
820 a_sample_t( so_5::environment_t & env )
821 :
822 base_type_t( env ),
823 m_sample_state( self_ptr() )
824 {
825 // ...
826 }
827
828 // ...
829
830 };
831 \endcode
832 */
833 inline const agent_t *
834 self_ptr() const
835 {
836 return this;
837 }
838
839 inline agent_t *
841 {
842 return this;
843 }
844
845 //! Hook on agent start inside SObjectizer.
846 /*!
847 It is guaranteed that this method will be called first
848 just after end of the cooperation registration process.
849
850 During cooperation registration agent is bound to some
851 working thread. And the first method which is called for
852 the agent on that working thread context is this method.
853
854 \code
855 class a_sample_t : public so_5::agent_t
856 {
857 // ...
858 virtual void
859 so_evt_start();
860 // ...
861 };
862
863 a_sample_t::so_evt_start()
864 {
865 std::cout << "first agent action on bound dispatcher" << std::endl;
866 ... // Some application logic actions.
867 }
868 \endcode
869 */
870 virtual void
871 so_evt_start();
872
873 //! Hook of agent finish in SObjectizer.
874 /*!
875 It is guaranteed that this method will be called last
876 just before deattaching agent from it's working thread.
877
878 This method should be used to perform some cleanup
879 actions on it's working thread.
880 \code
881 class a_sample_t : public so_5::agent_t
882 {
883 // ...
884 virtual void
885 so_evt_finish();
886 // ...
887 };
888
889 a_sample_t::so_evt_finish()
890 {
891 std::cout << "last agent activity";
892
893 if( so_current_state() == m_db_error_happened )
894 {
895 // Delete the DB connection on the same thread where
896 // connection was established and where some
897 // error happened.
898 m_db.release();
899 }
900 }
901 \endcode
902 */
903 virtual void
905
906 //! Access to the current agent state.
907 /*!
908 * Returns a reference to the current agent's state.
909 *
910 * If composite states are used then so_current_state() returns
911 * a reference to the inner-most state. For example:
912 * \code
913 * class demo final : public so_5::agent_t
914 * {
915 * state_t st_top_1{ this, "top_1" };
916 * state_t st_child_1_1{ initial_substate_of{ st_top_1 }, "child_1" };
917 * state_t st_child_1_2{ substate_of{ st_top_1 }, "child_2" };
918 *
919 * state_t st_child_1_1_1{ initial_substate_of{ st_child_1_1 }, "1" };
920 * state_t st_child_1_1_2{ substate_of{ st_child_1_1 }, "2" };
921 * state_t st_child_1_1_3{ substate_of{ st_child_1_1 }, "3" };
922 * ...
923 * void so_define_agent() override
924 * {
925 * // Change the agent state.
926 * this >>= st_top_1;
927 * // Now the agent has several active states:
928 * //
929 * // st_top_1 (because this state has been explicitly activated)
930 * // st_child_1_1 (because it's initial substate of st_top_1)
931 * // st_child_1_1_1 (because it's initial substate of st_child_1_1).
932 * //
933 * // And so_current_state() will return reference to st_child_1_1_1.
934 * assert(st_child_1_1_1 == so_current_state());
935 *
936 * ...
937 * }
938 * };
939 * \endcode
940 *
941 * \note
942 * There is a change in behaviour of this methon in v.5.5.22.
943 * If some on_enter/on_exit handler calls this method during
944 * the state change procedure this method will return the state
945 * for which this on_enter/on_exit handler is called. For example:
946 * \code
947 * class demo final : public so_5::agent_t {
948 * state_t st_1{ this };
949 * state_t st_1_1{ initial_substate_of{st_1} };
950 * state_t st_1_2{ substate_of{st_1}};
951 * ...
952 * virtual void so_define_agent() override {
953 * st_1.on_enter([this]{
954 * assert(st_1 == so_current_state());
955 * ...
956 * });
957 * st_1_1.on_enter([this]{
958 * assert(st_1_1 == so_current_state());
959 * ...
960 * });
961 * ...
962 * }
963 * };
964 * \endcode
965 */
966 inline const state_t &
968 {
969 return *m_current_state_ptr;
970 }
971
972 /*!
973 * \brief Is a state activated?
974 *
975 * \note Since v.5.5.15 a state can have substates. For example
976 * state A can have substates B and C. If B is the current state
977 * then so_current_state() will return a reference to B. But
978 * state A is active too because it is a superstate for B.
979 * Method so_is_active_state(A) will return \a true in that case:
980 * \code
981 class demo : public so_5::agent_t
982 {
983 state_t A{ this, "A" };
984 state_t B{ initial_substate_of{ A }, "B" };
985 state_t C{ substate_of{ A }, "C" };
986 ...
987 void some_event()
988 {
989 this >>= C;
990
991 assert( C == so_current_state() );
992 assert( !( A == so_current_state() ) );
993 assert( so_is_active_state(A) );
994 ...
995 }
996 };
997 * \endcode
998 *
999 * \attention This method is not thread safe. Be careful calling
1000 * this method from outside of agent's working thread.
1001 *
1002 * \return \a true if state \a state_to_check is the current state
1003 * or if the current state is a substate of \a state_to_check.
1004 *
1005 * \since v.5.5.15
1006 */
1007 bool
1008 so_is_active_state( const state_t & state_to_check ) const noexcept;
1009
1010 //! Add a state listener to the agent.
1011 /*!
1012 * A programmer should guarantee that the lifetime of
1013 * \a state_listener is exceeds lifetime of the agent.
1014 */
1015 void
1017 agent_state_listener_t & state_listener );
1018
1019 //! Add a state listener to the agent.
1020 /*!
1021 * Agent takes care of the \a state_listener destruction.
1022 */
1023 void
1025 agent_state_listener_unique_ptr_t state_listener );
1026
1027 /*!
1028 * \brief A reaction from SObjectizer to an exception from
1029 * agent's event.
1030 *
1031 * If an exception is going out from agent's event it will be
1032 * caught by SObjectizer. Then SObjectizer will call this method
1033 * and perform some actions in dependence of return value.
1034 *
1035 * \note Since v.5.3.0 default implementation calls
1036 * coop_t::exception_reaction() for agent's cooperation
1037 * object.
1038 *
1039 * \note
1040 * This method is noexcept since v.5.8.0.
1041 *
1042 * \since v.5.2.3
1043 */
1044 virtual exception_reaction_t
1045 so_exception_reaction() const noexcept;
1046
1047 /*!
1048 * \brief Switching agent to special state in case of unhandled
1049 * exception.
1050 *
1051 * \note
1052 * Since v.5.7.3 it's implemented via so_deactivate_agent().
1053 *
1054 * \attention
1055 * The method is not noexcept, it can throw an exception. So additional
1056 * care has to be taken when it's called in catch-block and/or in
1057 * noexcept contexts.
1058 *
1059 * \since 5.2.3
1060 */
1061 void
1063
1064 //! Push an event to the agent's event queue.
1065 /*!
1066 This method is used by SObjectizer for the
1067 agent's event scheduling.
1068 */
1069 static inline void
1071 agent_t & agent,
1072 const message_limit::control_block_t * limit,
1073 mbox_id_t mbox_id,
1074 const std::type_index & msg_type,
1075 const message_ref_t & message )
1076 {
1077 agent.push_event( limit, mbox_id, msg_type, message );
1078 }
1079
1080 /*!
1081 * \brief Get the agent's direct mbox.
1082 *
1083 * \since v.5.4.0
1084 */
1085 const mbox_t &
1086 so_direct_mbox() const;
1087
1088 /*!
1089 * \brief Create a new direct mbox for that agent.
1090 *
1091 * This method creates a new MPSC mbox which is connected
1092 * with that agent. Only agent for that so_make_new_direct_mbox()
1093 * has been called can make subscriptions for a new mbox.
1094 *
1095 * Note. The new mbox doesn't replaces the standard direct mbox
1096 * for the agent. Old direct mbox is still here and can still be used
1097 * for sending messages to the agent. But new mbox is not related
1098 * to the old direct mbox: they are different mboxes and can be used
1099 * for different subscriptions.
1100 * For example:
1101 * \code
1102 * class my_agent final : public so_5::agent_t {
1103 * ...
1104 * void so_evt_start() override {
1105 * so_subscribe_self().event( [](mhood_t<hello>) {
1106 * std::cout << "hello from the direct mbox" << std::endl;
1107 * } );
1108 *
1109 * const auto new_mbox = so_make_new_direct_mbox();
1110 * so_subscribe( new_mbox ).event( [](mhood_t<hello) {
1111 * std::cout << "hello from a new mbox" << std::endl;
1112 * }
1113 *
1114 * so_5::send<hello>(*this);
1115 * so_5::send<hello>(new_mbox);
1116 * }
1117 * };
1118 * \endcode
1119 * The output will be:
1120 \verbatim
1121 hello from the direct mbox
1122 hello from a new mbox
1123 \endverbatim
1124 *
1125 * \since v.5.6.0
1126 */
1127 mbox_t
1129
1130 /*!
1131 * \brief Create tuning options object with default values.
1132 *
1133 * \since v.5.5.3
1134 */
1135 inline static agent_tuning_options_t
1137 {
1139 }
1140
1141 /*!
1142 * \brief Helper for creation a custom direct mbox factory.
1143 *
1144 * Usage example:
1145 * \code
1146 * class my_agent : public so_5::agent_t {
1147 * ...
1148 * public:
1149 * my_agent( context_t ctx )
1150 * : so_5::agent_t{ ctx + custom_direct_mbox_factory(
1151 * []( so_5::partially_constructed_agent_ptr_t agent_ptr,
1152 * so_5::mbox_t actual_mbox )
1153 * {
1154 * return so_5::mbox_t{ new my_custom_mbox{ agent_ptr.ptr(), std::move(actual_mbox) } };
1155 * } )
1156 * }
1157 * {...}
1158 *
1159 * ...
1160 * };
1161 * \endcode
1162 *
1163 * \since v.5.7.4
1164 */
1165 template< typename Lambda >
1166 [[nodiscard]]
1169 {
1170 return { std::forward<Lambda>(lambda) };
1171 }
1172
1173 protected:
1174 /*!
1175 * \name Accessing the default state.
1176 * \{
1177 */
1178
1179 //! Access to the agent's default state.
1180 const state_t &
1181 so_default_state() const;
1182 /*!
1183 * \}
1184 */
1185
1186 public : /* Note: since v.5.5.1 method so_change_state() is public */
1187
1188 /*!
1189 * \name Changing agent's state.
1190 * \{
1191 */
1192 //! Change the current state of the agent.
1193 /*!
1194 Usage sample:
1195 \code
1196 void a_sample_t::evt_smth( mhood_t< message_one_t > msg )
1197 {
1198 // If something wrong with the message then we should
1199 // switch to the error_state.
1200 if( error_in_data( *msg ) )
1201 so_change_state( m_error_state );
1202 }
1203 \endcode
1204
1205 \attention
1206 This method has to be called from a worker thread assigned
1207 to the agent by the dispatcher. This method can't be called from
1208 thread_safe event-handlers because so_change_state() modifies
1209 the state of the agent.
1210 */
1211 void
1213 //! New agent state.
1214 const state_t & new_state );
1215
1216 /*!
1217 * \brief Deactivate the agent.
1218 *
1219 * This method deactivates the agent:
1220 *
1221 * - drops all agent's subscriptions (including deadletter handlers) and
1222 * delivery filters;
1223 * - switches the agent to a special state in that the agent does nothing
1224 * and just waits the deregistration.
1225 *
1226 * Sometimes it is necessary to mark an agent as 'failed'. Such an agent
1227 * shouldn't process anything and the only thing that is allowed
1228 * is waiting for the deregistration. For example:
1229 *
1230 * \code
1231 * class some_agent final : public so_5::agent_t
1232 * {
1233 * state_t st_working{ this, "working" };
1234 * state_t st_failed{ this, "failed" };
1235 * ...
1236 * void on_enter_st_failed()
1237 * {
1238 * // Notify some supervisor about the failure.
1239 * // It will deregister the whole cooperation with failed agent.
1240 * so_5::send<msg_failure>( supervisor_mbox(), ... );
1241 * }
1242 * ...
1243 * void so_define_agent() override
1244 * {
1245 * this >>= st_working;
1246 *
1247 * st_failed.on_enter( &some_agent::on_enter_st_failed );
1248 *
1249 * ...
1250 * }
1251 *
1252 * void evt_some_event(mhood_t<some_msg> cmd)
1253 * {
1254 * try
1255 * {
1256 * do_some_processing_of(*cmd);
1257 * }
1258 * catch(...)
1259 * {
1260 * // Processing failed, agent can't continue work normally.
1261 * // Have to switch it to the failed state and wait for
1262 * // the deregistration.
1263 * this >>= st_failed;
1264 * }
1265 * }
1266 * ...
1267 * };
1268 * \endcode
1269 *
1270 * This approach works but has a couple of drawbacks:
1271 *
1272 * - it's necessary to define a separate state for an agent (like
1273 * st_failed shown above);
1274 * - agent still has all its subscriptions. It means that messages will
1275 * be delivered to the agent's event queue and dispatched by the
1276 * agent's dispatcher. They won't be handled because there are no
1277 * subscriptions in a state like st_failed, but message dispatching
1278 * will consume some resources, although the agent is a special
1279 * 'failed' state.
1280 *
1281 * To cope with those drawbacks so_deactivate_agent was introduced in
1282 * v.5.7.3. That method drops all agent's subscriptions (including
1283 * deadletter handlers and delivery filters) and switches the agent to a
1284 * special hidden state in that the agent doesn't handle anything.
1285 *
1286 * The example above now can be rewritten that way:
1287 * \code
1288 * class some_agent final : public so_5::agent_t
1289 * {
1290 * state_t st_working{ this, "working" };
1291 * ...
1292 * void switch_to_failed_state()
1293 * {
1294 * // Notify some supervisor about the failure.
1295 * // It will deregister the whole cooperation with failed agent.
1296 * so_5::send<msg_failure>( supervisor_mbox(), ... );
1297 * // Deactivate the agent.
1298 * so_deactivate_agent();
1299 * }
1300 * ...
1301 * void so_define_agent() override
1302 * {
1303 * this >>= st_working;
1304 * ...
1305 * }
1306 *
1307 * void evt_some_event(mhood_t<some_msg> cmd)
1308 * {
1309 * try
1310 * {
1311 * do_some_processing_of(*cmd);
1312 * }
1313 * catch(...)
1314 * {
1315 * // Processing failed, agent can't continue work normally.
1316 * // Have to switch it to the failed state and wait for
1317 * // the deregistration.
1318 * switch_to_failed_state();
1319 * }
1320 * }
1321 * ...
1322 * };
1323 * \endcode
1324 *
1325 * \note
1326 * The method uses so_change_state(), so it has all requirements of
1327 * so_change_state(). Because the agent state will be changed,
1328 * so_deactivate_state() has to be called on the working thread
1329 * assigned to the agent by the dispatcher, and
1330 * so_deactivate_state() can't be invoked from a thread_safe event
1331 * handler.
1332 *
1333 * \attention
1334 * The method is not noexcept, it can throw an exception. So additional
1335 * care has to be taken when it's called in catch-block and/or in
1336 * noexcept contexts.
1337 *
1338 * \since v.5.7.3
1339 */
1340 void
1342
1343 /*!
1344 * \brief Dropping all agents subscriptions and filters.
1345 *
1346 * This method is similar to so_deactivate_agent(), but it leaves
1347 * the agent in the current state. The agent can change its state
1348 * again or make new subscriptions later.
1349 *
1350 * \since v.5.8.5
1351 */
1352 void
1354 /*!
1355 * \}
1356 */
1357
1358 public : /* Note: since v.5.2.3.2 subscription-related method are
1359 made public. */
1360
1361 /*!
1362 * \name Subscription methods.
1363 * \{
1364 */
1365
1366 //! Initiate subscription.
1367 /*!
1368 This method starts a subscription procedure by returning
1369 an instance of subscription_bind_t. The subscription details and
1370 the completion of a subscription is controlled by this
1371 subscription_bind_t object.
1372
1373 Usage sample:
1374 \code
1375 void a_sample_t::so_define_agent()
1376 {
1377 // Subscription for state `state_one`
1378 so_subscribe( mbox_target )
1379 .in( state_one )
1380 .event( &a_sample_t::evt_sample_handler );
1381
1382 // Subscription for the default state.
1383 so_subscribe( another_mbox )
1384 .event( &a_sample_t::evt_another_handler );
1385
1386 // Subscription for several event handlers in the default state.
1387 so_subscribe( yet_another_mbox )
1388 .event( &a_sample_t::evt_yet_another_handler )
1389 // Lambda-function can be used as event handler too.
1390 .event( [this](mhood_t<some_message> cmd) {...} );
1391
1392 // Subscription for several event handlers.
1393 // All of them will be subscribed for states first_state and second_state.
1394 so_subscribe( some_mbox )
1395 .in( first_state )
1396 .in( second_state )
1397 .event( &a_sample_t::evt_some_handler_1 )
1398 .event( &a_sample_t::evt_some_handler_2 )
1399 .event( &a_sample_t::evt_some_handler_3 );
1400 }
1401 \endcode
1402 */
1403 inline subscription_bind_t
1405 //! Mbox for messages to subscribe.
1406 const mbox_t & mbox_ref )
1407 {
1408 return subscription_bind_t( *this, mbox_ref );
1409 }
1410
1411 /*!
1412 * \brief Initiate subscription to agent's direct mbox.
1413 *
1414 * Note that is just a short form of:
1415 * \code
1416 * void a_sample_t::so_define_agent()
1417 * {
1418 * so_subscribe( so_direct_mbox() )
1419 * .in( some_state )
1420 * .in( another_state )
1421 * .event( some_event_handler )
1422 * .event( some_another_handler );
1423 * }
1424 * \endcode
1425 * Instead of writting `so_subscribe(so_direct_mbox())` it is possible
1426 * to write just `so_subscribe_self()`.
1427 *
1428 * \par Usage sample:
1429 \code
1430 void a_sample_t::so_define_agent()
1431 {
1432 // Subscription for state `state_one`
1433 so_subscribe_self()
1434 .in( state_one )
1435 .event( &a_sample_t::evt_sample_handler );
1436
1437 // Subscription for the default state.
1438 so_subscribe_self()
1439 .event( &a_sample_t::evt_another_handler );
1440
1441 // Subscription for several event handlers in the default state.
1442 so_subscribe_self()
1443 .event( &a_sample_t::evt_yet_another_handler )
1444 // Lambda-function can be used as event handler too.
1445 .event( [this](mhood_t<some_message> cmd) {...} );
1446
1447 // Subscription for several event handlers.
1448 // All of them will be subscribed for states first_state and second_state.
1449 so_subscribe_self()
1450 .in( first_state )
1451 .in( second_state )
1452 .event( &a_sample_t::evt_some_handler_1 )
1453 .event( &a_sample_t::evt_some_handler_2 )
1454 .event( &a_sample_t::evt_some_handler_3 );
1455 }
1456 \endcode
1457 *
1458 * \since v.5.5.1
1459 */
1460 inline subscription_bind_t
1465
1466 /*!
1467 * \brief Create a subscription for an event.
1468 *
1469 * \note
1470 * Before v.5.5.21 it was a private method. Since v.5.5.21
1471 * it is a public method with a standard so_-prefix.
1472 * It was made public to allow creation of subscriptions
1473 * to agent from outside of agent.
1474 *
1475 * \note
1476 * Parameter \a handler_kind was introduced in v.5.7.0.
1477 */
1478 void
1480 //! Message's mbox.
1481 const mbox_t & mbox_ref,
1482 //! Message type.
1483 std::type_index type_index,
1484 //! State for event.
1485 const state_t & target_state,
1486 //! Event handler caller.
1487 const event_handler_method_t & method,
1488 //! Thread safety of the event handler.
1489 thread_safety_t thread_safety,
1490 //! Kind of that event handler.
1491 event_handler_kind_t handler_kind );
1492
1493 /*!
1494 * \brief Destroy event subscription.
1495 *
1496 * \note
1497 * This method was introduced in v.5.5.21 to allow manipulation
1498 * of agent's subscriptions from outside of an agent.
1499 *
1500 * \note
1501 * It is safe to try to destroy nonexistent subscription.
1502 *
1503 * \since v.5.5.21
1504 */
1505 void
1507 //! Message's mbox.
1508 const mbox_t & mbox,
1509 //! Message's type.
1510 const std::type_index & subscription_type,
1511 //! Target state of a subscription.
1512 const state_t & target_state )
1513 {
1514 do_drop_subscription(
1515 mbox,
1516 subscription_type,
1517 target_state );
1518 }
1519
1520 /*!
1521 * \brief Drop subscription for the state specified.
1522 *
1523 * This overload is indended to be used when there is an event-handler in
1524 * the form of agent's method. And there is a need to unsubscribe this
1525 * event handler.
1526 * For example:
1527 * \code
1528 * class demo : public so_5::agent_t {
1529 * void on_some_event(mhood_t<some_msg> cmd) {
1530 * if(cmd->some_condition)
1531 * // New subscription must be created.
1532 * so_subscribe(some_mbox).in(some_state)
1533 * .event(&demo::one_shot_message_handler);
1534 * ...
1535 * }
1536 *
1537 * void one_shot_message_handler(mhood_t<another_msg> cmd) {
1538 * ... // Some actions.
1539 * // Subscription is no more needed.
1540 * so_drop_subscription(some_mbox, some_state,
1541 * &demo::one_shot_message_handler);
1542 * }
1543 * };
1544 * \endcode
1545 *
1546 * \note Doesn't throw if there is no such subscription.
1547 *
1548 * \note Subscription is removed even if agent was subscribed
1549 * for this message type with different method pointer.
1550 * The pointer to event routine is necessary only to
1551 * detect MSG type.
1552 *
1553 * \since v.5.2.3
1554 */
1555 template< typename Method_Pointer >
1556 typename std::enable_if<
1560 void >::type
1577
1578 /*!
1579 * \brief Drop subscription for the state specified.
1580 *
1581 * Usage example:
1582 * \code
1583 * class demo : public so_5::agent_t {
1584 * void on_turn_listening_on(mhood_t<turn_on> cmd) {
1585 * // New subscription must be created.
1586 * so_subscribe(cmd->listeting_mbox()).in(some_state)
1587 * .event([this](mhood_t<state_change_notify> cmd) {...});
1588 * ...
1589 * }
1590 *
1591 * void on_turn_listening_off(mhood_t<turn_off> cmd) {
1592 * // Subscription is no more needed.
1593 * so_drop_subscription<state_change_notify>(cmd->listening_mbox(), some_state);
1594 * ...
1595 * }
1596 * };
1597 * \endcode
1598 *
1599 * \note Doesn't throw if there is no such subscription.
1600 *
1601 * \since v.5.5.3
1602 */
1603 template< class Message >
1604 inline void
1614
1615 /*!
1616 * \brief Drop subscription for the default agent state.
1617 *
1618 * This overload is indended to be used when there is an event-handler in
1619 * the form of agent's method. And there is a need to unsubscribe this
1620 * event handler.
1621 * For example:
1622 * \code
1623 * class demo : public so_5::agent_t {
1624 * void on_some_event(mhood_t<some_msg> cmd) {
1625 * if(cmd->some_condition)
1626 * // New subscription must be created.
1627 * so_subscribe(some_mbox)
1628 * .event(&demo::one_shot_message_handler);
1629 * ...
1630 * }
1631 *
1632 * void one_shot_message_handler(mhood_t<another_msg> cmd) {
1633 * ... // Some actions.
1634 * // Subscription is no more needed.
1635 * so_drop_subscription(some_mbox,
1636 * &demo::one_shot_message_handler);
1637 * }
1638 * };
1639 * \endcode
1640 *
1641 * \note Doesn't throw if there is no such subscription.
1642 *
1643 * \note Subscription is removed even if agent was subscribed
1644 * for this message type with different method pointer.
1645 * The pointer to event routine is necessary only to
1646 * detect Msg type.
1647 *
1648 * \since v.5.2.3
1649 */
1650 template< typename Method_Pointer >
1651 typename std::enable_if<
1655 void >::type
1672
1673 /*!
1674 * \brief Drop subscription for the default agent state.
1675 *
1676 * Usage example:
1677 * \code
1678 * class demo : public so_5::agent_t {
1679 * void on_turn_listening_on(mhood_t<turn_on> cmd) {
1680 * // New subscription must be created.
1681 * so_subscribe(cmd->listening_mbox())
1682 * .event([this](mhood_t<state_change_notify> cmd) {...});
1683 * ...
1684 * }
1685 *
1686 * void on_turn_listening_off(mhood_t<turn_off> cmd) {
1687 * // Subscription is no more needed.
1688 * so_drop_subscription<state_change_notify>(cmd->listening_mbox());
1689 * ...
1690 * }
1691 * };
1692 * \endcode
1693 *
1694 * \note Doesn't throw if there is no such subscription.
1695 *
1696 * \since v.5.5.3
1697 */
1698 template< class Message >
1699 inline void
1708
1709 /*!
1710 * \brief Drop subscription for all states.
1711 *
1712 * Usage example:
1713 * \code
1714 * class demo : public so_5::agent_t {
1715 * state_t st_working{this}, st_waiting{this}, st_stopping{this};
1716 * ...
1717 * void on_turn_listening_on(mhood_t<turn_on> cmd) {
1718 * // Make subscriptions for message of type state_change_notify.
1719 * st_working.event(cmd->listening_mbox(),
1720 * &demo::on_state_notify_when_working);
1721 * st_waiting.event(cmd->listening_mbox(),
1722 * &demo::on_state_notify_when_waiting);
1723 * st_waiting.event(cmd->listening_mbox(),
1724 * &demo::on_state_notify_when_stopping);
1725 * ...
1726 * }
1727 * void on_turn_listening_off(mhood_t<turn_off> cmd) {
1728 * // Subscriptions are no more needed.
1729 * // All three event handlers for state_change_notify
1730 * // will be unsubscribed.
1731 * so_drop_subscription_for_all_states(cmd->listening_mbox(),
1732 * &demo::on_state_notify_when_working);
1733 * }
1734 * ...
1735 * void on_state_notify_when_working(mhood_t<state_change_notify> cmd) {...}
1736 * void on_state_notify_when_waiting(mhood_t<state_change_notify> cmd) {...}
1737 * void on_state_notify_when_stopping(mhood_t<state_change_notify> cmd) {...}
1738 * };
1739 * \endcode
1740 *
1741 * \note Doesn't throw if there is no any subscription for
1742 * that mbox and message type.
1743 *
1744 * \note Subscription is removed even if agent was subscribed
1745 * for this message type with different method pointer.
1746 * The pointer to event routine is necessary only to
1747 * detect Msg type.
1748 *
1749 * \note
1750 * Since v.5.5.21 this method also drops the subscription
1751 * for a deadletter handler for that type of message/signal.
1752 *
1753 * \since v.5.2.3
1754 */
1755 template< typename Method_Pointer >
1756 typename std::enable_if<
1760 void >::type
1776
1777 /*!
1778 * \brief Drop subscription for all states.
1779 *
1780 * Usage example:
1781 * \code
1782 * class demo : public so_5::agent_t {
1783 * state_t st_working{this}, st_waiting{this}, st_stopping{this};
1784 * ...
1785 * void on_turn_listening_on(mhood_t<turn_on> cmd) {
1786 * // Make subscriptions for message of type state_change_notify.
1787 * st_working.event(cmd->listening_mbox(),
1788 * [this](mhood_t<state_change_notify> cmd) {...});
1789 * st_waiting.event(cmd->listening_mbox(),
1790 * [this](mhood_t<state_change_notify> cmd) {...});
1791 * st_waiting.event(cmd->listening_mbox(),
1792 * [this](mhood_t<state_change_notify> cmd) {...});
1793 * ...
1794 * }
1795 * void on_turn_listening_off(mhood_t<turn_off> cmd) {
1796 * // Subscriptions are no more needed.
1797 * // All three event handlers for state_change_notify
1798 * // will be unsubscribed.
1799 * so_drop_subscription_for_all_states<state_change_notify>(cmd->listening_mbox());
1800 * }
1801 * ...
1802 * };
1803 * \endcode
1804 * \note Doesn't throw if there is no any subscription for
1805 * that mbox and message type.
1806 *
1807 * \note
1808 * Since v.5.5.21 this method also drops the subscription
1809 * for a deadletter handler for that type of message/signal.
1810 *
1811 * \since v.5.5.3
1812 */
1813 template< class Message >
1814 inline void
1822
1823 /*!
1824 * \brief Check the presence of a subscription.
1825 *
1826 * This method can be used to avoid an exception from so_subscribe()
1827 * in the case if the subscription is already present. For example:
1828 * \code
1829 * void my_agent::evt_create_new_subscription(mhood_t<data_source> cmd)
1830 * {
1831 * // cmd can contain mbox we have already subscribed to.
1832 * // If we just call so_subscribe() then an exception can be thrown.
1833 * // Because of that check the presence of subscription first.
1834 * if(!so_has_subscription<message>(cmd->mbox(), so_default_state()))
1835 * {
1836 * // There is no subscription yet. New subscription can be
1837 * // created.
1838 * so_subscribe(cmd->mbox()).event(...);
1839 * }
1840 * }
1841 * \endcode
1842 *
1843 * \note
1844 * Please do not call this method from outside of working context
1845 * of the agent.
1846 *
1847 * \return true if subscription is present for \a target_state.
1848 *
1849 * \tparam Message a type of message/signal subscription to which
1850 * must be checked.
1851 *
1852 * \since v.5.5.19.5
1853 */
1854 template< class Message >
1855 bool
1857 //! A mbox from which message/signal of type \a Message is expected.
1858 const mbox_t & mbox,
1859 //! A target state for the subscription.
1860 const state_t & target_state ) const noexcept
1861 {
1863 mbox,
1865 target_state );
1866 }
1867
1868 /*!
1869 * \brief Check the presence of a subscription in the default_state.
1870 *
1871 * This method can be used to avoid an exception from so_subscribe()
1872 * in the case if the subscription is already present. For example:
1873 * \code
1874 * void my_agent::evt_create_new_subscription(mhood_t<data_source> cmd)
1875 * {
1876 * // cmd can contain mbox we have already subscribed to.
1877 * // If we just call so_subscribe() then an exception can be thrown.
1878 * // Because of that check the presence of subscription first.
1879 * if(!so_has_subscription<message>(cmd->mbox()))
1880 * {
1881 * // There is no subscription yet. New subscription can be
1882 * // created.
1883 * so_subscribe(cmd->mbox()).event(...);
1884 * }
1885 * }
1886 * \endcode
1887 *
1888 * \note
1889 * Please do not call this method from outside of working context
1890 * of the agent.
1891 *
1892 * \return true if subscription is present for the default_state.
1893 *
1894 * \tparam Message a type of message/signal subscription to which
1895 * must be checked.
1896 *
1897 * \since v.5.5.19.5
1898 */
1899 template< class Message >
1900 bool
1902 //! A mbox from which message/signal of type \a Message is expected.
1903 const mbox_t & mbox ) const noexcept
1904 {
1906 mbox,
1908 so_default_state() );
1909 }
1910
1911 /*!
1912 * \brief Check the presence of a subscription.
1913 *
1914 * Type of message is deducted from event-handler signature.
1915 *
1916 * Usage example:
1917 * \code
1918 * void my_agent::evt_create_new_subscription(mhood_t<data_source> cmd)
1919 * {
1920 * // cmd can contain mbox we have already subscribed to.
1921 * // If we just call so_subscribe() then an exception can be thrown.
1922 * // Because of that check the presence of subscription first.
1923 * if(!so_has_subscription(cmd->mbox(), my_state, &my_agent::my_event))
1924 * {
1925 * // There is no subscription yet. New subscription can be
1926 * // created.
1927 * so_subscribe(cmd->mbox()).event(...);
1928 * }
1929 * }
1930 * \endcode
1931 *
1932 * \note
1933 * Please do not call this method from outside of working context
1934 * of the agent.
1935 *
1936 * \return true if subscription is present for \a target_state.
1937 *
1938 * \since v.5.5.19.5
1939 */
1940 template< typename Method_Pointer >
1941 typename std::enable_if<
1945 bool >::type
1947 //! A mbox from which message/signal is expected.
1948 const mbox_t & mbox,
1949 //! A target state for the subscription.
1950 const state_t & target_state,
1951 Method_Pointer /*pfn*/ ) const noexcept
1952 {
1955
1956 using message_type =
1958 typename pfn_traits::argument_type>::type;
1959
1960 return this->so_has_subscription<message_type>(
1961 mbox, target_state );
1962 }
1963
1964 /*!
1965 * \brief Check the presence of a subscription.
1966 *
1967 * Subscription is checked for the default agent state.
1968 *
1969 * Type of message is deducted from event-handler signature.
1970 *
1971 * Usage example:
1972 * \code
1973 * void my_agent::evt_create_new_subscription(mhood_t<data_source> cmd)
1974 * {
1975 * // cmd can contain mbox we have already subscribed to.
1976 * // If we just call so_subscribe() then an exception can be thrown.
1977 * // Because of that check the presence of subscription first.
1978 * if(!so_has_subscription(cmd->mbox(), &my_agent::my_event))
1979 * {
1980 * // There is no subscription yet. New subscription can be
1981 * // created.
1982 * so_subscribe(cmd->mbox()).event(...);
1983 * }
1984 * }
1985 * \endcode
1986 *
1987 * \note
1988 * Please do not call this method from outside of working context
1989 * of the agent.
1990 *
1991 * \return true if subscription is present for the default state.
1992 *
1993 * \since v.5.5.19.5
1994 */
1995 template< typename Method_Pointer >
1996 typename std::enable_if<
2000 bool >::type
2002 //! A mbox from which message/signal is expected.
2003 const mbox_t & mbox,
2004 Method_Pointer /*pfn*/ ) const noexcept
2005 {
2008
2009 using message_type =
2011 typename pfn_traits::argument_type>::type;
2012
2013 return this->so_has_subscription<message_type>(
2015 }
2016 /*!
2017 * \}
2018 */
2019
2020 /*!
2021 * \name Methods for dealing with deadletter subscriptions.
2022 * \{
2023 */
2024 /*!
2025 * \brief Create a subscription for a deadletter handler.
2026 *
2027 * \note
2028 * This is low-level method intended to be used by libraries writters.
2029 * Do not call it directly if you don't understand its purpose and
2030 * what its arguments mean. Use so_subscribe_deadletter_handler()
2031 * instead.
2032 *
2033 * This method actually creates a subscription to deadletter handler
2034 * for messages/signal of type \a msg_type from mbox \a mbox.
2035 *
2036 * \throw so_5::exception_t in the case when the subscription
2037 * of a deadletter handler for type \a msg_type from \a mbox is
2038 * already exists.
2039 *
2040 * \since v.5.5.21
2041 */
2042 void
2044 //! Message's mbox.
2045 const mbox_t & mbox,
2046 //! Message type.
2047 const std::type_index & msg_type,
2048 //! Event handler caller.
2050 //! Thread safety of the event handler.
2052
2053 /*!
2054 * \brief Destroy a subscription for a deadletter handler.
2055 *
2056 * \note
2057 * This is low-level method intended to be used by libraries writters.
2058 * Do not call it directly if you don't understand its purpose and
2059 * what its arguments mean. Use so_drop_deadletter_handler() instead.
2060 *
2061 * This method actually destroys a subscription to deadletter handler
2062 * for messages/signal of type \a msg_type from mbox \a mbox.
2063 *
2064 * \note
2065 * It is safe to call this method if there is no such
2066 * deadletter handler. It will do nothing in such case.
2067 *
2068 * \since v.5.5.21
2069 */
2070 void
2072 //! Message's mbox.
2073 const mbox_t & mbox,
2074 //! Message type.
2075 const std::type_index & msg_type );
2076
2077 /*!
2078 * \brief Create a subscription for deadletter handler for
2079 * a specific message from a specific mbox.
2080 *
2081 * Type of a message for deadletter handler will be detected
2082 * automatically from the signature of the \a handler.
2083 *
2084 * A deadletter handler can be a pointer to method of agent or
2085 * lambda-function. The handler should have one of the following
2086 * format:
2087 * \code
2088 * void deadletter_handler(message_type);
2089 * void deadletter_handler(const message_type &);
2090 * void deadletter_handler(mhood_t<message_type>);
2091 * \endcode
2092 *
2093 * Usage example:
2094 * \code
2095 * class demo : public so_5::agent_t {
2096 * void on_some_message(mhood_t<some_message> cmd) {...}
2097 * ...
2098 * virtual void so_define_agent() override {
2099 * // Create deadletter handler via pointer to method.
2100 * // Event handler will be not-thread-safe.
2101 * so_subscribe_deadletter_handler(
2102 * so_direct_mbox(),
2103 * &demo::on_some_message );
2104 *
2105 * // Create deadletter handler via lambda-function.
2106 * so_subscribe_deadletter_handler(
2107 * status_mbox(), // Any mbox can be used, not only agent's direct mbox.
2108 * [](mhood_t<status_request> cmd) {
2109 * so_5::send<current_status>(cmd->reply_mbox, "workind");
2110 * },
2111 * // This handler will be thread-safe one.
2112 * so_5::thread_safe );
2113 * }
2114 * };
2115 * \endcode
2116 *
2117 * \throw so_5::exception_t in the case when the subscription
2118 * of a deadletter handler for type \a msg_type from \a mbox is
2119 * already exists.
2120 *
2121 * \since v.5.5.21
2122 */
2123 template< typename Event_Handler >
2124 void
2126 const so_5::mbox_t & mbox,
2127 Event_Handler && handler,
2129 {
2130 using namespace details::event_subscription_helpers;
2131
2133 mbox,
2134 *this,
2136
2138 mbox,
2139 ev.m_msg_type,
2140 ev.m_handler,
2141 thread_safety );
2142 }
2143
2144 /*!
2145 * \brief Drops the subscription for deadletter handler.
2146 *
2147 * A message type must be specified explicitely via template
2148 * parameter.
2149 *
2150 * Usage example:
2151 * \code
2152 * class demo : public so_5::agent_t {
2153 * void some_deadletter_handler(mhood_t<some_message> cmd) {
2154 * ... // Do some stuff.
2155 * // There is no need for deadletter handler.
2156 * so_drop_deadletter_handler<some_message>(some_mbox);
2157 * }
2158 * ...
2159 * };
2160 * \endcode
2161 *
2162 * \note
2163 * Is is safe to call this method if there is no a deadletter
2164 * handler for message of type \a Message from message box
2165 * \a mbox.
2166 *
2167 * \tparam Message Type of a message or signal for deadletter
2168 * handler.
2169 *
2170 * \since v.5.5.21
2171 */
2172 template< typename Message >
2173 void
2175 //! A mbox from which the message is expected.
2176 const so_5::mbox_t & mbox )
2177 {
2179 mbox,
2181 }
2182
2183 /*!
2184 * \brief Checks the presence of deadletter handler for a message of
2185 * a specific type from a specific mbox.
2186 *
2187 * Message type must be specified explicitely via template
2188 * parameter \a Message.
2189 *
2190 * \return true if a deadletter for a message/signal of type
2191 * \a Message from message mbox \a mbox exists.
2192 *
2193 * Usage example:
2194 * \code
2195 * class demo : public so_5::agent_t {
2196 * void on_some_request(mhood_t<request_data> cmd) {
2197 * if(!so_has_deadletter_handler<some_message>(some_mbox))
2198 * // There is no deadletter handler yet.
2199 * // It should be created now.
2200 * so_subscribe_deadletter_handler(
2201 * some_mbox,
2202 * [this](mhood_t<some_message> cmd) {...});
2203 * ...
2204 * }
2205 * };
2206 * \endcode
2207 *
2208 * \tparam Message Type of a message or signal for deadletter
2209 * handler.
2210 *
2211 * \since v.5.5.21
2212 */
2213 template< typename Message >
2214 bool
2216 //! A mbox from which the message is expected.
2217 const so_5::mbox_t & mbox ) const noexcept
2218 {
2220 mbox,
2222 }
2223 /*!
2224 * \}
2225 */
2226
2227 protected :
2228
2229 /*!
2230 * \name Agent initialization methods.
2231 * \{
2232 */
2233 /*!
2234 * \brief A correct initiation of so_define_agent method call.
2235 *
2236 * Before the actual so_define_agent() method it is necessary
2237 * to temporary set working thread id. And then drop this id
2238 * to non-actual value after so_define_agent() return.
2239 *
2240 * Because of that this method must be called during cooperation
2241 * registration procedure instead of direct call of so_define_agent().
2242 *
2243 * \since v.5.4.0
2244 */
2245 void
2247
2248 //! Hook on define agent for SObjectizer.
2249 /*!
2250 This method is called by SObjectizer during the cooperation
2251 registration process before agent will be bound to its
2252 working thread.
2253
2254 Should be used by the agent to make necessary message subscriptions.
2255
2256 Usage sample;
2257 \code
2258 class a_sample_t : public so_5::agent_t
2259 {
2260 // ...
2261 virtual void
2262 so_define_agent();
2263
2264 void evt_handler_1( mhood_t< message1 > msg );
2265 // ...
2266 void evt_handler_N( mhood_t< messageN > & msg );
2267
2268 };
2269
2270 void
2271 a_sample_t::so_define_agent()
2272 {
2273 // Make subscriptions...
2274 so_subscribe( m_mbox1 )
2275 .in( m_state_1 )
2276 .event( &a_sample_t::evt_handler_1 );
2277 // ...
2278 so_subscribe( m_mboxN )
2279 .in( m_state_N )
2280 .event( &a_sample_t::evt_handler_N );
2281 }
2282 \endcode
2283 */
2284 virtual void
2286
2287 //! Is method define_agent already called?
2288 /*!
2289 Usage sample:
2290 \code
2291 class a_sample_t : public so_5::agent_t
2292 {
2293 // ...
2294
2295 public:
2296 void
2297 set_target_mbox( const so_5::mbox_t & mbox )
2298 {
2299 // mbox cannot be changed after agent registration.
2300 if( !so_was_defined() )
2301 {
2302 m_target_mbox = mbox;
2303 }
2304 }
2305
2306 private:
2307 so_5::mbox_t m_target_mbox;
2308 };
2309 \endcode
2310 */
2311 bool
2312 so_was_defined() const;
2313 /*!
2314 * \}
2315 */
2316
2317 public:
2318 //! Access to the SObjectizer Environment which this agent is belong.
2319 /*!
2320 Usage sample for other cooperation registration:
2321 \code
2322 void a_sample_t::evt_on_smth( mhood_t< some_message_t > msg )
2323 {
2324 so_5::coop_unique_holder_t coop = so_environment().make_coop();
2325
2326 // Filling the cooperation...
2327 coop->make_agent< a_another_t >( ... );
2328 // ...
2329
2330 // Registering cooperation.
2331 so_environment().register_coop( std::move(coop) );
2332 }
2333 \endcode
2334
2335 Usage sample for the SObjectizer shutting down:
2336 \code
2337 void a_sample_t::evt_last_event( mhood_t< message_one_t > msg )
2338 {
2339 ...
2340 so_environment().stop();
2341 }
2342 \endcode
2343 */
2345 so_environment() const noexcept;
2346
2347 /*!
2348 * \brief Get a handle of agent's coop.
2349 *
2350 * \note
2351 * This method is a replacement for so_coop_name() method
2352 * from previous versions of SObjectizer-5.
2353 *
2354 * \attention
2355 * If this method is called when agent is not registered (e.g.
2356 * there is no coop for agent) then this method will throw.
2357 *
2358 * Usage example:
2359 * \code
2360 * class parent final : public so_5::agent_t {
2361 * ...
2362 * void so_evt_start() override {
2363 * // Create a child coop.
2364 * auto coop = so_environment().make_coop(
2365 * // We as a parent coop.
2366 * so_coop() );
2367 * ...; // Fill the coop.
2368 * so_environment().register_coop( std::move(coop) );
2369 * }
2370 * };
2371 * \endcode
2372 *
2373 * \since v.5.6.0
2374 */
2375 [[nodiscard]]
2377 so_coop() const;
2378
2379 /*!
2380 * \brief Binding agent to the dispatcher.
2381 *
2382 * This is an actual start of agent's work in SObjectizer.
2383 *
2384 * \note
2385 * This method was a de-facto noexcept in previous versions of
2386 * SObjectizer. But didn't marked as noexcept because of need of
2387 * support old C++ compilers. Since v.5.6.0 it is officially noexcept.
2388 *
2389 * \since v.5.4.0
2390 */
2391 void
2393 //! Actual event queue for an agent.
2394 event_queue_t & queue ) noexcept;
2395
2396 /*!
2397 * \brief Create execution hint for the specified demand.
2398 *
2399 * The hint returned is intendent for the immediately usage.
2400 * It must not be stored for the long time and used sometime in
2401 * the future. It is because internal state of the agent
2402 * can be changed and some references from hint object to
2403 * agent's internals become invalid.
2404 *
2405 * \since v.5.4.0
2406 */
2407 static execution_hint_t
2409 //! Demand for execution of event handler.
2410 execution_demand_t & demand );
2411
2412 /*!
2413 * \brief A helper method for deregistering agent's coop.
2414 *
2415 * Usage example:
2416 * \code
2417 * class demo : public so_5::agent_t {
2418 * ...
2419 * void on_some_event(mhood_t<some_msg> cmd) {
2420 * try {
2421 * ... // Some processing.
2422 * if(no_more_work_left())
2423 * // Normal deregistration of the coop.
2424 * so_deregister_agent_coop_normally();
2425 * }
2426 * catch(...) {
2427 * // Some error.
2428 * // Deregister the coop with special 'exit code'.
2429 * so_deregister_agent_coop(so_5::dereg_reason::user_defined_reason+10);
2430 * }
2431 * }
2432 * };
2433 * \endcode
2434 *
2435 * \since v.5.4.0
2436 */
2437 void
2438 so_deregister_agent_coop( int dereg_reason );
2439
2440 /*!
2441 * \brief A helper method for deregistering agent's coop
2442 * in case of normal deregistration.
2443 *
2444 * \note It is just a shorthand for:
2445 \code
2446 so_deregister_agent_coop( so_5::dereg_reason::normal );
2447 \endcode
2448 *
2449 * \since v.5.4.0
2450 */
2451 void
2453
2454 /*!
2455 * \name Methods for dealing with message delivery filters.
2456 * \{
2457 */
2458 /*!
2459 * \brief Set a delivery filter.
2460 *
2461 * \note
2462 * Since v.5.7.4 it can be used for mutable messages too (if mbox is MPSC mbox).
2463 * In that case \a Message should be in form `so_5::mutable_msg<Message>`.
2464 *
2465 * \tparam Message type of message to be filtered.
2466 *
2467 * \since v.5.5.5
2468 */
2469 template< typename Message >
2470 void
2472 //! Message box from which message is expected.
2473 //! This must be MPMC-mbox.
2474 const mbox_t & mbox,
2475 //! Delivery filter instance.
2476 delivery_filter_unique_ptr_t filter )
2477 {
2479
2481 mbox,
2483 std::move(filter) );
2484 }
2485
2486 /*!
2487 * \brief Set a delivery filter.
2488 *
2489 * \tparam Lambda type of lambda-function or functional object which
2490 * must be used as message filter.
2491 *
2492 * \par Usage sample:
2493 \code
2494 void my_agent::so_define_agent() {
2495 so_set_delivery_filter( temp_sensor,
2496 []( const current_temperature & msg ) {
2497 return !is_normal_temperature( msg );
2498 } );
2499 ...
2500 }
2501 \endcode
2502 *
2503 * \since v.5.5.5
2504 */
2505 template< typename Lambda >
2506 void
2508 //! Message box from which message is expected.
2509 //! This must be MPMC-mbox.
2510 const mbox_t & mbox,
2511 //! Delivery filter as lambda-function or functional object.
2512 Lambda && lambda );
2513
2514 /*!
2515 * \brief Set a delivery filter for a mutable message.
2516 *
2517 * \tparam Lambda type of lambda-function or functional object which
2518 * must be used as message filter.
2519 *
2520 * \note
2521 * The content of the message will be passed to delivery-filter
2522 * lambda-function by a const reference.
2523 *
2524 * \par Usage sample:
2525 \code
2526 void my_agent::so_define_agent() {
2527 so_set_delivery_filter_for_mutable_msg( temp_sensor,
2528 []( const current_temperature & msg ) {
2529 return !is_normal_temperature( msg );
2530 } );
2531 ...
2532 }
2533 \endcode
2534 *
2535 * \since v.5.7.4
2536 */
2537 template< typename Lambda >
2538 void
2540 //! Message box from which message is expected.
2541 //! This must be MPMC-mbox.
2542 const mbox_t & mbox,
2543 //! Delivery filter as lambda-function or functional object.
2544 Lambda && lambda );
2545
2546 /*!
2547 * \brief Drop a delivery filter.
2548 *
2549 * Usage example:
2550 * \code
2551 * // For a case of an immutable message.
2552 * void some_agent::some_event(mhood_t<my_message> cmd) {
2553 * ... // Some actions.
2554 * // Now we want to drop the subscription and the delivery
2555 * // filter for this message.
2556 * so_drop_subscription_for_all_states<my_message>(source_mbox);
2557 * so_drop_delivery_filter<my_message>(source_mbox);
2558 * }
2559 *
2560 * // For a case of a mutable message.
2561 * void some_agent::some_event(mutable_mhood_t<my_message> cmd) {
2562 * ... // Some actions.
2563 * // Now we want to drop the subscription and the delivery
2564 * // filter for this message.
2565 * so_drop_subscription_for_all_states<so_5::mutable_msg<my_message>>(source_mbox);
2566 * so_drop_delivery_filter<so_5::mutable_msg<my_message>>(source_mbox);
2567 * }
2568 * \endcode
2569 *
2570 * \tparam Message type of message filtered.
2571 *
2572 * \since v.5.5.5
2573 */
2574 template< typename Message >
2575 void
2577 //! Message box to which delivery filter was set.
2578 //! This must be MPMC-mbox.
2579 const mbox_t & mbox ) noexcept
2580 {
2582 mbox,
2584 }
2585 /*!
2586 * \}
2587 */
2588
2589 /*!
2590 * \name Dealing with priority.
2591 * \{
2592 */
2593 /*!
2594 * \brief Get the priority of the agent.
2595 *
2596 * \since v.5.5.8
2597 */
2598 [[nodiscard]]
2600 so_priority() const noexcept
2601 {
2602 return m_priority;
2603 }
2604 /*!
2605 * \}
2606 */
2607
2608 /*!
2609 * \brief Helper method that allows to run a block of code as
2610 * non-thread-safe event handler.
2611 *
2612 * \attention
2613 * This is a low-level method. Using it may destroy all thread-safety
2614 * guarantees provided by SObjectizer. Please use it only when you know
2615 * what your are doing. All responsibility rests with the user.
2616 *
2617 * Use of this method may be necessary when an agent is bound to a
2618 * special dispatcher that runs not only the agent's event-handlers, but
2619 * also other callbacks on the same worker thread.
2620 *
2621 * A good example of such a dispatcher is so5extra's asio_one_thread
2622 * dispatcher. It guarantees that an IO completion handler is called on
2623 * the same worker thread as agent's event-handlers. For example:
2624 * \code
2625 * class agent_that_uses_asio : public so_5::agent_t
2626 * {
2627 * state_t st_not_ready{this};
2628 * state_t st_ready{this};
2629 *
2630 * asio::io_context & io_ctx_;
2631 *
2632 * public:
2633 * agent_that_uses_asio(context_t ctx, asio::io_context & io_ctx)
2634 * : so_5::agent_t{std::move(ctx)}
2635 * , io_ctx_{io_ctx}
2636 * {}
2637 * ...
2638 * void so_define_agent() override
2639 * {
2640 * st_not_ready.activate();
2641 * ...
2642 * }
2643 *
2644 * void so_evt_start() override
2645 * {
2646 * auto resolver = std::make_shared<asio::ip::tcp::resolver>(io_ctx_);
2647 * resolver->async_resolve("some.host.name", "",
2648 * asio::ip::tcp::numeric_service | asio::ip::tcp::address_configured,
2649 * // IO completion handler to be run on agent's worker thread.
2650 * [resolver, this](auto ec, auto results) {
2651 * // It's necessary to wrap the block of code, otherwise
2652 * // modification of the agent's state (or managing of subscriptions)
2653 * // will be prohibited because SObjectizer doesn't see
2654 * // the IO completion handler as event handler.
2655 * so_low_level_exec_as_event_handler( [&]() {
2656 * ...
2657 * st_ready.activate();
2658 * });
2659 * });
2660 * }
2661 * }
2662 * \endcode
2663 *
2664 * \attention
2665 * Using this method inside a running event-handler (non-thread-safe and
2666 * especially thread-safe) is undefined behavior. SObjectizer can't check
2667 * such a case without a significant performance penalty, so there won't
2668 * be any warnings or errors from SObjectizer's side, anything can
2669 * happen.
2670 *
2671 * \since v.5.8.0
2672 */
2673 template< typename Lambda >
2674 decltype(auto)
2676 Lambda && lambda ) noexcept( noexcept(lambda()) )
2677 {
2679 m_working_thread_id,
2680 query_current_thread_id()
2681 };
2682
2683 return lambda();
2684 }
2685
2686 /*!
2687 * \brief Returns the dispatcher binder that is used for binding this
2688 * agent.
2689 *
2690 * \attention
2691 * It's safe to use this method only while the agent is registered
2692 * in a SObjectizer Environment -- from the start of so_evt_start() until
2693 * the completion so_evt_finish(). The calling of this method when agent
2694 * it not registered (e.g. before the invocation of so_evt_start() or
2695 * after the completion of so_evt_finish()) may lead to undefined behavior.
2696 *
2697 * This method is intended to simplify creation of children cooperations:
2698 * \code
2699 * void parent_agent::evt_some_command(mhood_t<msg_command> cmd) {
2700 * ...
2701 * // A new child coop has to be created and bound to the same
2702 * // dispatcher as the parent agent.
2703 * so_5::introduce_child_coop( *this,
2704 * // Get the binder of the parent.
2705 * so_this_agent_disp_binder(),
2706 * [&](so_5::coop_t & coop) {
2707 * ... // Creation of children agents.
2708 * } );
2709 * }
2710 * \endcode
2711 *
2712 * \since v.5.8.1
2713 */
2714 [[nodiscard]]
2717 {
2718 return m_disp_binder;
2719 }
2720
2721 /*!
2722 * \brief Returns the dispatcher binder that is used as the default
2723 * binder for the agent's coop.
2724 *
2725 * \attention
2726 * It's safe to use this method only while the agent is registered
2727 * in a SObjectizer Environment -- from the start of so_evt_start() until
2728 * the completion so_evt_finish(). The calling of this method when agent
2729 * it not registered (e.g. before the invocation of so_evt_start() or
2730 * after the completion of so_evt_finish()) may lead to undefined behavior.
2731 *
2732 * This method is intended to simplify creation of children cooperations:
2733 * \code
2734 * void parent_agent::evt_some_command(mhood_t<msg_command> cmd) {
2735 * ...
2736 * // A new child coop has to be created and bound to the same
2737 * // dispatcher as the parent agent.
2738 * so_5::introduce_child_coop( *this,
2739 * // Get the binder of the parent's coop.
2740 * so_this_coop_disp_binder(),
2741 * [&](so_5::coop_t & coop) {
2742 * ... // Creation of children agents.
2743 * } );
2744 * }
2745 * \endcode
2746 *
2747 * \note
2748 * This method may return a different binder that so_this_agent_disp_binder()
2749 * in a case when the agent was bound by a separate dispatcher. For example:
2750 * \code
2751 * // The parent coop will use thread_pool dispatcher as
2752 * // the default dispatcher.
2753 * env.introduce_coop(
2754 * so_5::disp::thread_pool::make_dispatcher( env, 8u )
2755 * .binder( []( auto & params ) {
2756 * // Every agent will have a separate event queue.
2757 * params.fifo( so_5::disp::thread_pool::fifo_t::individual );
2758 * } ),
2759 * [&]( so_5::coop_t & coop ) {
2760 * // The parent agent itself will use a separate dispatcher.
2761 * coop.make_agent_with_binder< parent_agent >(
2762 * so_5::disp::one_thread::make_dispatcher( env ).binder(),
2763 * ... );
2764 *
2765 * ... // Creation of other agents.
2766 * } );
2767 * \endcode
2768 * In that case use of so_this_agent_disp_binder() instead of
2769 * so_this_coop_disp_binder() will bind children agents to the
2770 * parent's one_thread dispatcher instead of coop's thread_pool
2771 * dispatcher.
2772 *
2773 * \since v.5.8.1
2774 */
2775 [[nodiscard]]
2778
2779 /*!
2780 * \brief Get an optional name of the agent.
2781 *
2782 * If agent has the name then a reference to this name will be returned.
2783 * Otherwise a small object with a pointer to agent will be returned.
2784 *
2785 * The result can be printed to std::ostream or converted into a string:
2786 * \code
2787 * class my_agent final : public so_5::agent_t
2788 * {
2789 * ...
2790 * void so_evt_start() override
2791 * {
2792 * std::cout << so_agent_name() << ": started" << std::endl;
2793 * ...
2794 * so_5::send<std::string>(some_mbox, so_agent_name().to_string());
2795 * }
2796 * void so_evt_finished() override
2797 * {
2798 * std::cout << so_agent_name() << ": stopped" << std::endl;
2799 * }
2800 * ...
2801 * }
2802 * \endcode
2803 *
2804 * \attention
2805 * This method returns a lightweight object that just holds a reference
2806 * to the agent's name (or a pointer to the agent). This object should
2807 * not be stored for the long time, because the references/pointers it
2808 * holds may become invalid. If you have to store the agent name for
2809 * a long time please convert the returned value into std::string and
2810 * store the resulting std::string object.
2811 *
2812 * \since v.5.8.2
2813 */
2814 [[nodiscard]]
2816 so_agent_name() const noexcept;
2817
2818 private:
2819 const state_t st_default{ self_ptr(), "<DEFAULT>" };
2820
2821 //! Current agent state.
2822 /*!
2823 * If composite states are used then m_current_state_ptr points
2824 * to the inner-most state. See so_current_state() for an example.
2825 */
2826 const state_t * m_current_state_ptr;
2827
2828 /*!
2829 * \brief Enumeration of possible agent statuses.
2830 *
2831 * \since v.5.5.18
2832 */
2833 enum class agent_status_t : char
2834 {
2835 //! Agent is not defined yet.
2836 //! This is an initial agent status.
2838 //! Agent is defined.
2839 defined,
2840 //! State switch operation is in progress.
2842 //! Agent was shutdown and all pending demands have to be skipped.
2844 };
2845
2846 /*!
2847 * \brief Current agent status.
2848 *
2849 * \since v.5.5.18
2850 */
2852
2853 //! State listeners controller.
2854 impl::state_listener_controller_t m_state_listener_controller;
2855
2856 /*!
2857 * \brief Type of function for searching event handler.
2858 *
2859 * \since v.5.5.9
2860 */
2861 using handler_finder_t =
2862 const impl::event_handler_data_t *(*)(
2863 execution_demand_t & /* demand */,
2864 const char * /* context_marker */ );
2865
2866 /*!
2867 * \brief Function for searching event handler.
2868 *
2869 * \note The value is set only once in the constructor and
2870 * doesn't changed anymore.
2871 *
2872 * \since v.5.5.9
2873 */
2874 handler_finder_t m_handler_finder;
2875
2876 /*!
2877 * \brief All agent's subscriptions.
2878 *
2879 * \since v.5.4.0
2880 */
2882
2883 /*!
2884 * \brief Holder of message sinks for that agent.
2885 *
2886 * If message limits are defined for the agent it will be an actual
2887 * storage with separate sinks for every (message_type, message_limit).
2888 *
2889 * If message limits are not defined then it will be a special storage
2890 * with just one message sink (that sink will be used for all subscriptions).
2891 *
2892 * \since v.5.8.0
2893 */
2895
2896 //! SObjectizer Environment for which the agent is belong.
2898
2899 /*!
2900 * \brief Event queue operation protector.
2901 *
2902 * Initially m_event_queue is NULL. It is changed to actual value
2903 * in so_bind_to_dispatcher() method. And reset to nullptr again
2904 * in shutdown_agent().
2905 *
2906 * nullptr in m_event_queue means that methods push_event() will throw
2907 * away any new demand.
2908 *
2909 * It is necessary to provide guarantee that m_event_queue will be reset
2910 * to nullptr in shutdown_agent() only if there is no working
2911 * push_event() methods. To do that default_rw_spinlock_t is used. Method
2912 * push_event() acquire it in read-mode and shutdown_agent() acquires it
2913 * in write-mode. It means that shutdown_agent() cannot get access to
2914 * m_event_queue until there is working push_event().
2915 *
2916 * \since v.5.5.8
2917 */
2918 default_rw_spinlock_t m_event_queue_lock;
2919
2920 /*!
2921 * \brief A pointer to event_queue.
2922 *
2923 * After binding to the dispatcher is it pointed to the actual
2924 * event queue.
2925 *
2926 * After shutdown it is set to nullptr.
2927 *
2928 * \attention Access to m_event_queue value must be done only
2929 * under acquired m_event_queue_lock.
2930 *
2931 * \since v.5.5.8
2932 */
2934
2935 /*!
2936 * \brief A direct mbox for the agent.
2937 *
2938 * \since v.5.4.0
2939 */
2940 const mbox_t m_direct_mbox;
2941
2942 /*!
2943 * \brief Working thread id.
2944 *
2945 * Some actions like managing subscriptions and changing states
2946 * are enabled only on working thread id.
2947 *
2948 * \since v.5.4.0
2949 */
2951
2952 //! Agent is belong to this cooperation.
2954
2955 /*!
2956 * \brief Delivery filters for that agents.
2957 *
2958 * \note Storage is created only when necessary.
2959 *
2960 * \since v.5.5.5
2961 */
2963
2964 /*!
2965 * \brief Priority of the agent.
2966 *
2967 * \since v.5.5.8
2968 */
2970
2971 /*!
2972 * \brief Binder for this agent.
2973 *
2974 * Since v.5.7.5 disp_binder for the agent is stored inside the agent.
2975 * It guarantees that disp_binder will be deleted after destruction
2976 * of the agent (if there is no circular references between the agent
2977 * and the disp_binder).
2978 *
2979 * This value will be set by coop_t when agent is being add to the
2980 * coop.
2981 *
2982 * \note
2983 * Access to that field provided by so_5::impl::internal_agent_iface_t.
2984 *
2985 * \since v.5.7.5
2986 */
2988
2989 /*!
2990 * \brief Optional name for the agent.
2991 *
2992 * This value can be set in the constructor only and can't be changed
2993 * later.
2994 *
2995 * Empty value means that the name for the agent wasn't specified.
2996 *
2997 * \since v.5.8.2
2998 */
3000
3001 /*!
3002 * \brief Type for holding information necessary for handling
3003 * time limits for agent states.
3004 *
3005 * \note
3006 * Instance of this type can be in one of the following states:
3007 *
3008 * - empty. There is no data for handling of time limits. This
3009 * is the default and initial state;
3010 * - defained. In this case m_timeout_mbox has an actual value.
3011 * The instance has to be switched to this state explicitly.
3012 *
3013 * \note
3014 * This class in not Copyable, nor Moveable.
3015 *
3016 * \since v.5.8.5
3017 */
3019 {
3020 /// Message box to be used for state_t::time_limit_t::msg_timeout
3021 /// messages.
3022 ///
3023 /// \note
3024 /// If this field is nullptr then the whole instance of
3025 /// state_time_limit_handling_data_t is in empty state.
3027
3028 public:
3029 /// Default constructor.
3030 ///
3031 /// Creates an empty instance.
3033
3035
3036 /// Is the data for handling time limits defined?
3037 [[nodiscard]]
3038 bool
3039 is_defined() const noexcept;
3040
3041 /// Define the data for handling time limits.
3042 ///
3043 /// \attention
3044 /// The actual value of \a timeout_mbox is not checked.
3045 /// It's just assumed that \a timeout_mbox is not nullptr.
3046 void
3048 /// Message box to be used for msg_timeout signals.
3049 mbox_t timeout_mbox );
3050
3051 /// Get the mbox for msg_timeout signals.
3052 ///
3053 /// \attention
3054 /// This method doesn't check the actual value of
3055 /// m_timeout_mbox. So it can be called even if
3056 /// (is_defined() != true).
3057 [[nodiscard]]
3058 mbox_t
3059 timeout_mbox() const noexcept;
3060 };
3061
3062 /*!
3063 * \brief Data that is necessary for handling time limits
3064 * of agent's states.
3065 *
3066 * \note
3067 * It is not defined by default. The actual value is assigned
3068 * when state_t::time_limit is called for the first time.
3069 *
3070 * \attention
3071 * The current implementation never drops this value after
3072 * the first call of state_t::time_limit, even if all time limits
3073 * are dropped after then.
3074 *
3075 * \since v.5.8.5
3076 */
3078
3079 /// \brief What to do with pending demands on deregistration.
3080 ///
3081 /// This value is extracted from agent_tuning_options in the constructor
3082 /// and never changes later.
3083 ///
3084 /// \since v.5.8.5
3086
3087 //! Destroy all agent's subscriptions.
3088 /*!
3089 * \note
3090 * This method is intended to be used in the destructor and
3091 * methods like so_deactivate_agent().
3092 *
3093 * \attention
3094 * It's noexcept method because there is no way to recover in case
3095 * when deletion of subscriptions throws.
3096 *
3097 * \since v.5.7.3
3098 */
3099 void
3101
3102 //! Make an agent reference.
3103 /*!
3104 * This is an internal SObjectizer method. It is called when
3105 * it is guaranteed that the agent is still necessary and something
3106 * has reference to it.
3107 */
3108 agent_ref_t
3109 create_ref();
3110
3111 /*!
3112 * \name Embedding agent into the SObjectizer Run-time.
3113 * \{
3114 */
3115
3116 //! Bind agent to the cooperation.
3117 /*!
3118 * Initializes an internal cooperation pointer.
3119 */
3120 void
3122 //! Cooperation for that agent.
3123 coop_t & coop );
3124
3125 //! Agent shutdown deriver.
3126 /*!
3127 * Method destroys all agent subscriptions.
3128 *
3129 * \since v.5.2.3
3130 */
3131 void
3132 shutdown_agent() noexcept;
3133 /*!
3134 * \}
3135 */
3136
3137 /*!
3138 * \name Subscription/unsubscription implementation details.
3139 * \{
3140 */
3141
3142 /*!
3143 * \brief Helper function that returns a message sink to be used
3144 * for subscriptions for specified message type.
3145 *
3146 * \since v.5.8.0
3147 */
3148 [[nodiscard]]
3151 const std::type_index & msg_type );
3152
3153 /*!
3154 * \brief Remove subscription for the state specified.
3155 *
3156 * \since v.5.2.3
3157 */
3158 void
3160 //! Message's mbox.
3161 const mbox_t & mbox,
3162 //! Message type.
3163 const std::type_index & msg_type,
3164 //! State for event.
3165 const state_t & target_state );
3166
3167 /*!
3168 * \brief Remove subscription for all states.
3169 *
3170 * \since v.5.2.3
3171 */
3172 void
3174 //! Message's mbox.
3175 const mbox_t & mbox,
3176 //! Message type.
3177 const std::type_index & msg_type );
3178
3179 /*!
3180 * \brief Check the presence of a subscription.
3181 *
3182 * \since v.5.5.19.5
3183 */
3184 bool
3186 //! Message's mbox.
3187 const mbox_t & mbox,
3188 //! Message type.
3189 const std::type_index & msg_type,
3190 //! State for the subscription.
3191 const state_t & target_state ) const noexcept;
3192
3193 /*!
3194 * \brief Check the presence of a deadletter handler.
3195 *
3196 * \since v.5.5.21
3197 */
3198 bool
3200 //! Message's mbox.
3201 const mbox_t & mbox,
3202 //! Message type.
3203 const std::type_index & msg_type ) const noexcept;
3204 /*!
3205 * \}
3206 */
3207
3208 /*!
3209 * \name Event handling implementation details.
3210 * \{
3211 */
3212
3213 //! Push event into the event queue.
3214 void
3215 push_event(
3216 //! Optional message limit.
3217 const message_limit::control_block_t * limit,
3218 //! ID of mbox for this event.
3219 mbox_id_t mbox_id,
3220 //! Message type for event.
3221 const std::type_index & msg_type,
3222 //! Event message.
3223 const message_ref_t & message );
3224 /*!
3225 * \}
3226 */
3227
3228 // NOTE: demand handlers declared as public to allow
3229 // access this handlers from unit-tests.
3230 public :
3231 /*!
3232 * \name Demand handlers.
3233 * \{
3234 */
3235 /*!
3236 * \brief Calls so_evt_start method for agent.
3237 *
3238 * \since v.5.2.0
3239 */
3240 static void
3242 current_thread_id_t working_thread_id,
3243 execution_demand_t & d );
3244
3245 /*!
3246 * \brief Ensures that all agents from cooperation are
3247 * bound to dispatchers.
3248 *
3249 * \since v.5.5.8
3250 */
3251 void
3253
3254 /*!
3255 * \note This method is necessary for GCC on Cygwin.
3256 *
3257 * \since v.5.4.0
3258 */
3259 static demand_handler_pfn_t
3261
3262 /*!
3263 * \brief Calls so_evt_finish method for agent.
3264 *
3265 * \since v.5.2.0
3266 */
3267 static void
3269 current_thread_id_t working_thread_id,
3270 execution_demand_t & d );
3271
3272 /*!
3273 * \note This method is necessary for GCC on Cygwin.
3274 *
3275 * \since v.5.4.0
3276 */
3277 static demand_handler_pfn_t
3279
3280 /*!
3281 * \brief Calls event handler for message.
3282 *
3283 * \since v.5.2.0
3284 */
3285 static void
3287 current_thread_id_t working_thread_id,
3288 execution_demand_t & d );
3289
3290 /*!
3291 * \note This method is necessary for GCC on Cygwin.
3292 *
3293 * \since v.5.4.0
3294 */
3295 static demand_handler_pfn_t
3297
3298 /*!
3299 * \brief Handles the enveloped message.
3300 *
3301 * \since v.5.5.23
3302 */
3303 static void
3305 current_thread_id_t working_thread_id,
3306 execution_demand_t & d );
3307
3308 /*!
3309 * \since v.5.5.24
3310 */
3311 static demand_handler_pfn_t
3313 /*!
3314 * \}
3315 */
3316
3317 private :
3318 /*!
3319 * \brief Actual implementation of message handling.
3320 *
3321 * \note Since v.5.5.17.1 argument \a method is passed as copy.
3322 * It prevents deallocation of event_handler_method in the following
3323 * case:
3324 * \code
3325 auto mbox = so_environment().create_mbox();
3326 so_subscribe( mbox ).event< some_signal >( [this, mbox] {
3327 so_drop_subscription< some_signal >( mbox );
3328 ... // Some other actions.
3329 } );
3330 * \endcode
3331 *
3332 * \attention
3333 * Implementation notes: it's important that \a method is passed
3334 * by value. It's because subscription can be deleted during
3335 * the work of process_message (due to unsubscription inside the
3336 * event handler) and if pass \a method by a reference then
3337 * that reference can become invalid.
3338 *
3339 * \since v.5.4.0
3340 */
3341 static void
3343 current_thread_id_t working_thread_id,
3345 thread_safety_t thread_safety,
3346 event_handler_method_t method );
3347
3348 /*!
3349 * \brief Actual implementation of enveloped message handling.
3350 *
3351 * \note
3352 * handler_data can be nullptr. It means that an event handler
3353 * for that message type if not found and special hook will
3354 * be called for the envelope.
3355 *
3356 * \since v.5.5.23
3357 */
3358 static void
3360 current_thread_id_t working_thread_id,
3362 const impl::event_handler_data_t * handler_data );
3363
3364 /*!
3365 * \brief Enables operation only if it is performed on agent's
3366 * working thread.
3367 *
3368 * \since v.5.4.0
3369 */
3370 void
3372 const char * operation_name ) const;
3373
3374 /*!
3375 * \brief Drops all delivery filters.
3376 *
3377 * \since v.5.5.0
3378 */
3379 void
3380 drop_all_delivery_filters() noexcept;
3381
3382 /*!
3383 * \brief Set a delivery filter.
3384 *
3385 * \since v.5.5.5
3386 */
3387 void
3389 const mbox_t & mbox,
3390 const std::type_index & msg_type,
3391 delivery_filter_unique_ptr_t filter );
3392
3393 /*!
3394 * \brief Drop a delivery filter.
3395 *
3396 * \since v.5.5.5
3397 */
3398 void
3400 const mbox_t & mbox,
3401 const std::type_index & msg_type ) noexcept;
3402
3403 /*!
3404 * \brief Handler finder for the case when message delivery
3405 * tracing is disabled.
3406 *
3407 * \since v.5.5.9
3408 */
3409 static const impl::event_handler_data_t *
3411 execution_demand_t & demand,
3412 const char * context_marker );
3413
3414 /*!
3415 * \brief Handler finder for the case when message delivery
3416 * tracing is enabled.
3417 *
3418 * \since v.5.5.9
3419 */
3420 static const impl::event_handler_data_t *
3422 execution_demand_t & demand,
3423 const char * context_marker );
3424
3425 /*!
3426 * \brief Actual search for event handler with respect
3427 * to parent-child relationship between agent states.
3428 *
3429 * \since v.5.5.15
3430 */
3431 static const impl::event_handler_data_t *
3433 execution_demand_t & demand );
3434
3435 /*!
3436 * \brief Search for event handler between deadletter handlers.
3437 *
3438 * \return nullptr if event handler is not found.
3439 *
3440 * \since v.5.5.21
3441 */
3442 static const impl::event_handler_data_t *
3444 execution_demand_t & demand );
3445
3446 /*!
3447 * \brief Perform actual operations related to state switch.
3448 *
3449 * It throws if the agent in awaiting_deregistration_state and
3450 * \a state_to_be_set isn't awaiting_deregistration_state.
3451 *
3452 * \note
3453 * This method doesn't check the working context. It's assumed
3454 * that this check has already been performed by caller.
3455 *
3456 * \since v.5.7.3
3457 */
3458 void
3460 //! New state to be set as the current state.
3461 const state_t & state_to_be_set );
3462
3463 /*!
3464 * \brief Actual action for switching agent state.
3465 *
3466 * \since v.5.5.15
3467 */
3468 void
3470 //! New state to be set as the current state.
3471 const state_t & state_to_be_set ) noexcept;
3472
3473 /*!
3474 * \brief Return agent to the default state.
3475 *
3476 * \note This method is called just before invocation of
3477 * so_evt_finish() to return agent to the default state.
3478 * This return will initiate invocation of on_exit handlers
3479 * for all active states of the agent.
3480 *
3481 * \attention State switch is not performed if agent is already
3482 * in default state or if it waits deregistration after unhandled
3483 * exception.
3484 *
3485 * \since v.5.5.15
3486 */
3487 void
3489
3490 /*!
3491 * \brief Is agent already deactivated.
3492 *
3493 * Deactivated agent is in awaiting_deregistration_state.
3494 * This method checks that the current state of the agent
3495 * is awaiting_deregistration_state.
3496 *
3497 * \attention
3498 * This method isn't thread safe and should be used with care.
3499 * A caller should guarantee that it's called from the right
3500 * working thread.
3501 *
3502 * \since v.5.7.3
3503 */
3504 bool
3505 is_agent_deactivated() const noexcept;
3506
3507 /*!
3508 * \brief Initialize data for handling time limit of
3509 * agent's states.
3510 *
3511 * Initializes data for handling time limit if it is not
3512 * defined yet.
3513 *
3514 * \since v.5.8.5
3515 */
3516 void
3518
3519 /*!
3520 * \brief Special event handler to process state time limits.
3521 *
3522 * This event handler is used as deadletter handler for
3523 * state_t::time_limit_t::msg_timeout.
3524 *
3525 * \note
3526 * Subscription for this deadletter handler will be created on
3527 * the first call to state_t::time_limit. If no one calls
3528 * state_t::time_limit then will event handler won't be used
3529 * at all.
3530 *
3531 * \since v.5.8.5
3532 */
3533 void
3536};
3537
3538/*!
3539 * \brief Helper function template for the creation of smart pointer
3540 * to an agent.
3541 *
3542 * This function can be useful if a pointer to an agent should be passed
3543 * somewhere with the guarantee that this pointer will remain valid even
3544 * if the agent will be deregistered.
3545 *
3546 * This could be necessary, for example, if a pointer to an agent is
3547 * passed to some callback (like it is done in Asio):
3548 * \code
3549 * void my_agent::on_some_event(mhood_t<some_msg> cmd) {
3550 * connection_.async_read_some(input_buffer_,
3551 * [self = so_5::make_agent_ref(this)](
3552 * const asio::error_code & ec,
3553 * std::size_t bytes_transferred )
3554 * {
3555 * if(!ec)
3556 * self->handle_incoming_data(bytes_transferred);
3557 * }
3558 * );
3559 * }
3560 * \endcode
3561 *
3562 * \since v.5.7.1
3563 */
3564template< typename Derived >
3565[[nodiscard]]
3566intrusive_ptr_t< Derived >
3567make_agent_ref( Derived * agent )
3568 {
3569 static_assert( std::is_base_of_v< agent_t, Derived >,
3570 "type should be derived from so_5::agent_t" );
3571
3572 return { agent };
3573 }
3574
3575template< typename Lambda >
3576void
3578 const mbox_t & mbox,
3579 Lambda && lambda )
3580 {
3581 using namespace so_5::details::lambda_traits;
3582
3584 using argument_type =
3586
3588
3590 mbox,
3594 std::move( lambda ) )
3595 } );
3596 }
3597
3598template< typename Lambda >
3599void
3601 const mbox_t & mbox,
3602 Lambda && lambda )
3603 {
3604 using namespace so_5::details::lambda_traits;
3605
3607 using argument_type =
3609
3611
3613
3615 mbox,
3619 std::move( lambda ) )
3620 } );
3621 }
3622
3623//
3624// subscription_bind_t implementation
3625//
3626inline
3628 agent_t & agent,
3629 const mbox_t & mbox_ref )
3630 : m_agent( &agent )
3631 , m_mbox_ref( mbox_ref )
3632{
3633}
3634
3635inline subscription_bind_t &
3637 const state_t & state )
3638{
3639 if( !state.is_target( m_agent ) )
3640 {
3642 rc_agent_is_not_the_state_owner,
3643 "agent doesn't own the state" );
3644 }
3645
3646 m_states.push_back( &state );
3647
3648 return *this;
3649}
3650
3651template< typename Method_Pointer >
3652typename std::enable_if<
3672
3673template<typename Lambda>
3674typename std::enable_if<
3678 Lambda && lambda,
3680{
3681 using namespace details::event_subscription_helpers;
3682
3684 m_mbox_ref,
3685 *m_agent,
3686 std::forward<Lambda>(lambda) );
3687
3689 ev.m_msg_type,
3690 ev.m_handler,
3693
3694 return *this;
3695}
3696
3697template< typename Msg >
3700 const state_t & target_state )
3701{
3702 /*
3703 * Note. Since v.5.5.22.1 there is a new implementation of transfer_to_state.
3704 * New implementation protects from loops in transfer_to_state calls.
3705 * For example in the following cases:
3706 *
3707 * \code
3708 class a_simple_case_t final : public so_5::agent_t
3709 {
3710 state_t st_base{ this, "base" };
3711 state_t st_disconnected{ initial_substate_of{st_base}, "disconnected" };
3712 state_t st_connected{ substate_of{st_base}, "connected" };
3713
3714 struct message {};
3715
3716 public :
3717 a_simple_case_t(context_t ctx) : so_5::agent_t{ctx} {
3718 this >>= st_base;
3719
3720 st_base.transfer_to_state<message>(st_disconnected);
3721 }
3722
3723 virtual void so_evt_start() override {
3724 so_5::send<message>(*this);
3725 }
3726 };
3727
3728 class a_two_state_loop_t final : public so_5::agent_t
3729 {
3730 state_t st_one{ this, "one" };
3731 state_t st_two{ this, "two" };
3732
3733 struct message {};
3734
3735 public :
3736 a_two_state_loop_t(context_t ctx) : so_5::agent_t{ctx} {
3737 this >>= st_one;
3738
3739 st_one.transfer_to_state<message>(st_two);
3740 st_two.transfer_to_state<message>(st_one);
3741 }
3742
3743 virtual void so_evt_start() override {
3744 so_5::send<message>(*this);
3745 }
3746 };
3747 * \endcode
3748 *
3749 * For such protection an additional objects with the current state
3750 * of transfer_to_state operation is necessary. There will be a boolean
3751 * flag in that state. When transfer_to_state will be started this
3752 * flag will should be 'false'. But if it is already 'true' then there is
3753 * a loop in transfer_to_state calls.
3754 */
3755
3756 // This is the state of transfer_to_state operation.
3757 struct transfer_op_state_t
3758 {
3759 agent_t * m_agent;
3761 const state_t & m_target_state;
3762 bool m_in_progress;
3763
3765 agent_t * agent,
3768 : m_agent( agent )
3769 , m_mbox_id( mbox_id )
3771 , m_in_progress( false )
3772 {}
3773 };
3774
3775 //NOTE: shared_ptr is used because capture of unique_ptr
3776 //makes std::function non-copyable, but we need to copy
3777 //resulting 'method' object.
3778 //
3781
3782 auto method = [op_state]( message_ref_t & msg )
3783 {
3784 // The current transfer_to_state operation should be inactive.
3785 if( op_state->m_in_progress )
3787 "transfer_to_state loop detected. target_state: " +
3789 ", current_state: " +
3791
3792 // Activate transfer_to_state operation and make sure that it
3793 // will be deactivated on return automatically.
3794 op_state->m_in_progress = true;
3796 op_state->m_in_progress = false;
3797 } );
3798
3799 //
3800 // The main logic of transfer_to_state operation.
3801 //
3803
3806 nullptr, // Message limit is not actual here.
3808 typeid( Msg ),
3809 msg,
3810 // We have very simple choice here: message is an enveloped
3811 // message or just classical message/signal.
3812 // So we should select an appropriate demand handler.
3816 };
3817
3819 };
3820
3822 typeid( Msg ),
3823 method,
3826
3827 return *this;
3828}
3829
3830template< typename Msg >
3833{
3834 // A method with nothing inside.
3835 auto method = []( message_ref_t & ) {};
3836
3838 typeid( Msg ),
3839 method,
3841 // Suppression of a message is a kind of ignoring of the message.
3842 // In the case of enveloped message intermediate_handler receives
3843 // the whole envelope (not the payload) and the whole envelope
3844 // should be ignored. We can't specify final_handler here because
3845 // in that case the payload of an enveloped message will be
3846 // extracted from the envelope and envelope will be informed about
3847 // the handling of message. But message won't be handled, it will
3848 // be ignored.
3850
3851 return *this;
3852}
3853
3854template< typename Msg >
3857 const state_t & target_state )
3858{
3860
3862 {
3864 };
3865
3867 typeid( Msg ),
3868 method,
3870 // Switching to some state is a kind of message processing.
3871 // So if there is an enveloped message then the envelope will be
3872 // informed about the processing of the payload.
3874
3875 return *this;
3876}
3877
3878inline void
3880 const std::type_index & msg_type,
3884{
3885 if( m_states.empty() )
3886 // Agent should be subscribed only in default state.
3888 m_mbox_ref,
3889 msg_type,
3891 method,
3893 handler_kind );
3894 else
3895 for( auto s : m_states )
3897 m_mbox_ref,
3898 msg_type,
3899 *s,
3900 method,
3902 handler_kind );
3903}
3904
3905inline void
3913
3914/*
3915 * Implementation of template methods of state_t class.
3916 */
3917inline bool
3918state_t::is_active() const noexcept
3919{
3920 return m_target_agent->so_is_active_state( *this );
3921}
3922
3923template< typename... Args >
3924const state_t &
3925state_t::event( Args&&... args ) const
3926{
3927 return this->subscribe_message_handler(
3929 std::forward< Args >(args)... );
3930}
3931
3932template< typename... Args >
3933const state_t &
3935{
3936 return this->subscribe_message_handler( from,
3937 std::forward< Args >(args)... );
3938}
3939
3940template< typename Msg >
3941bool
3943{
3944 return m_target_agent->so_has_subscription< Msg >( from, *this );
3945}
3946
3947template< typename Method_Pointer >
3948bool
3950 const mbox_t & from,
3951 Method_Pointer && pfn ) const
3952{
3954 from,
3955 *this,
3957}
3958
3959template< typename Msg >
3960void
3962{
3964}
3965
3966template< typename Method_Pointer >
3967void
3969 const mbox_t & from,
3970 Method_Pointer && pfn ) const
3971{
3973 from,
3974 *this,
3976}
3977
3978template< typename Msg >
3979const state_t &
3981{
3983 .in( *this )
3985
3986 return *this;
3987}
3988
3989template< typename Msg >
3990const state_t &
3992{
3993 return this->transfer_to_state< Msg >(
3995 target_state );
3996}
3997
3998template< typename Msg >
3999const state_t &
4001{
4003 .in( *this )
4005
4006 return *this;
4007}
4008
4009template< typename Msg >
4010const state_t &
4012{
4013 return this->just_switch_to< Msg >(
4015 target_state );
4016}
4017
4018template< typename Msg >
4019const state_t &
4021{
4022 return this->suppress< Msg >( m_target_agent->so_direct_mbox() );
4023}
4024
4025template< typename Msg >
4026const state_t &
4028{
4030 .in( *this )
4031 .suppress< Msg >();
4032
4033 return *this;
4034}
4035
4036template< typename Method_Pointer >
4037typename std::enable_if<
4041 state_t & >::type
4043{
4044 using namespace details::event_subscription_helpers;
4045
4048
4049 // Agent must have right type.
4050 auto cast_result =
4052 typename pfn_traits::agent_type >(
4053 *m_target_agent );
4054
4055 return this->on_enter( [cast_result, pfn]() { (cast_result->*pfn)(); } );
4056}
4057
4058template< typename Method_Pointer >
4059typename std::enable_if<
4063 state_t & >::type
4065{
4066 using namespace details::event_subscription_helpers;
4067
4070
4071 // Agent must have right type.
4072 auto cast_result =
4074 typename pfn_traits::agent_type >(
4075 *m_target_agent );
4076
4077 return this->on_exit( [cast_result, pfn]() { (cast_result->*pfn)(); } );
4078}
4079
4080template< typename... Args >
4081const state_t &
4083 const mbox_t & from,
4084 Args&&... args ) const
4085{
4086 m_target_agent->so_subscribe( from ).in( *this )
4087 .event( std::forward< Args >(args)... );
4088
4089 return *this;
4090}
4091
4092/*!
4093 * \brief A shortcat for switching the agent state.
4094 *
4095 * \par Usage example.
4096 \code
4097 class my_agent : public so_5::agent_t
4098 {
4099 const so_5::state_t st_normal = so_make_state();
4100 const so_5::state_t st_error = so_make_state();
4101 ...
4102 public :
4103 virtual void so_define_agent() override
4104 {
4105 this >>= st_normal;
4106
4107 st_normal.handle( [=]( const msg_failure & evt ) {
4108 this >>= st_error;
4109 ...
4110 });
4111 ...
4112 };
4113 ...
4114 };
4115 \endcode
4116 *
4117 * \since v.5.5.1
4118 */
4119inline void
4121{
4123}
4124
4125} /* namespace so_5 */
4126
4127#if defined( SO_5_MSVC )
4128 #pragma warning(pop)
4129#endif
Interface for message sink.
A context for agent construction and tuning.
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(const agent_t *pointer) noexcept
Initializing constructor for case when agent has no user specified name.
Interface of the agent state listener.
Type for holding information necessary for handling time limits for agent states.
Definition agent.hpp:3019
bool is_defined() const noexcept
Is the data for handling time limits defined?
Definition agent.cpp:692
A base class for agents.
Definition agent.hpp:673
void so_subscribe_deadletter_handler(const so_5::mbox_t &mbox, Event_Handler &&handler, thread_safety_t thread_safety=thread_safety_t::unsafe)
Create a subscription for deadletter handler for a specific message from a specific mbox.
Definition agent.hpp:2125
static demand_handler_pfn_t get_demand_handler_on_start_ptr() noexcept
Definition agent.cpp:1461
impl::state_listener_controller_t m_state_listener_controller
State listeners controller.
Definition agent.hpp:2854
decltype(auto) so_low_level_exec_as_event_handler(Lambda &&lambda) noexcept(noexcept(lambda()))
Helper method that allows to run a block of code as non-thread-safe event handler.
Definition agent.hpp:2675
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:1591
std::unique_ptr< impl::sinks_storage_t > m_message_sinks
Holder of message sinks for that agent.
Definition agent.hpp:2894
const state_t st_default
Definition agent.hpp:2819
void do_change_agent_state(const state_t &state_to_be_set)
Perform actual operations related to state switch.
Definition agent.cpp:1791
void so_drop_delivery_filter(const mbox_t &mbox) noexcept
Drop a delivery filter.
Definition agent.hpp:2576
void so_initiate_agent_definition()
A correct initiation of so_define_agent method call.
Definition agent.cpp:963
const agent_t * self_ptr() const
Get the raw pointer of itself.
Definition agent.hpp:834
void evt_state_time_limit(mhood_t< so_5::details::msg_state_timeout >)
Special event handler to process state time limits.
Definition agent.cpp:1945
so_5::current_thread_id_t m_working_thread_id
Working thread id.
Definition agent.hpp:2950
default_rw_spinlock_t m_event_queue_lock
Event queue operation protector.
Definition agent.hpp:2918
const name_for_agent_t m_name
Optional name for the agent.
Definition agent.hpp:2999
static const impl::event_handler_data_t * find_deadletter_handler(execution_demand_t &demand)
Search for event handler between deadletter handlers.
Definition agent.cpp:1781
static demand_handler_pfn_t get_demand_handler_on_message_ptr() noexcept
Definition agent.cpp:1526
bool is_agent_deactivated() const noexcept
Is agent already deactivated.
Definition agent.cpp:1907
void so_switch_to_awaiting_deregistration_state()
Switching agent to special state in case of unhandled exception.
Definition agent.cpp:881
const state_t & so_current_state() const
Access to the current agent state.
Definition agent.hpp:967
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:1550
agent_ref_t create_ref()
Make an agent reference.
Definition agent.cpp:1150
void so_drop_deadletter_handler(const so_5::mbox_t &mbox)
Drops the subscription for deadletter handler.
Definition agent.hpp:2174
bool so_is_active_state(const state_t &state_to_check) const noexcept
Is a state activated?
Definition agent.cpp:844
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:1639
static constexpr const state_t::history_t deep_history
Short alias for so_5::state_t::history_t::deep.
Definition agent.hpp:737
bool so_was_defined() const
Is method define_agent already called?
Definition agent.cpp:981
agent_status_t
Enumeration of possible agent statuses.
Definition agent.hpp:2834
@ shutdown_with_skipping_pending_demands
Agent was shutdown and all pending demands have to be skipped.
@ 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:1143
void shutdown_agent() noexcept
Agent shutdown deriver.
Definition agent.cpp:1163
void ensure_binding_finished()
Ensures that all agents from cooperation are bound to dispatchers.
Definition agent.cpp:1452
coop_t * m_agent_coop
Agent is belong to this cooperation.
Definition agent.hpp:2953
disp_binder_shptr_t so_this_agent_disp_binder() const
Returns the dispatcher binder that is used for binding this agent.
Definition agent.hpp:2716
agent_status_t m_current_status
Current agent status.
Definition agent.hpp:2851
event_queue_t * m_event_queue
A pointer to event_queue.
Definition agent.hpp:2933
void so_set_delivery_filter(const mbox_t &mbox, Lambda &&lambda)
Set a delivery filter.
Definition agent.hpp:3577
virtual void so_define_agent()
Hook on define agent for SObjectizer.
Definition agent.cpp:975
void drop_all_delivery_filters() noexcept
Drops all delivery filters.
Definition agent.cpp:1664
disp_binder_shptr_t m_disp_binder
Binder for this agent.
Definition agent.hpp:2987
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:1337
void do_state_switch(const state_t &state_to_be_set) noexcept
Actual action for switching agent state.
Definition agent.cpp:1832
std::unique_ptr< impl::delivery_filter_storage_t > m_delivery_filters
Delivery filters for that agents.
Definition agent.hpp:2962
void return_to_default_state_if_possible() noexcept
Return agent to the default state.
Definition agent.cpp:1895
subscription_bind_t so_subscribe_self()
Initiate subscription to agent's direct mbox.
Definition agent.hpp:1461
agent_t(environment_t &env)
Constructor.
Definition agent.cpp:775
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:1674
static void call_push_event(agent_t &agent, const message_limit::control_block_t *limit, mbox_id_t mbox_id, const std::type_index &msg_type, const message_ref_t &message)
Push an event to the agent's event queue.
Definition agent.hpp:1070
mbox_t so_make_new_direct_mbox()
Create a new direct mbox for that agent.
Definition agent.cpp:893
static execution_hint_t so_create_execution_hint(execution_demand_t &demand)
Create execution hint for the specified demand.
Definition agent.cpp:1035
void so_change_state(const state_t &new_state)
Change the current state of the agent.
Definition agent.cpp:936
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:1403
static custom_direct_mbox_factory_t custom_direct_mbox_factory(Lambda &&lambda)
Helper for creation a custom direct mbox factory.
Definition agent.hpp:1168
coop_handle_t so_coop() const
Get a handle of agent's coop.
Definition agent.cpp:994
void so_deregister_agent_coop_normally()
A helper method for deregistering agent's coop in case of normal deregistration.
Definition agent.cpp:1116
void do_drop_delivery_filter(const mbox_t &mbox, const std::type_index &msg_type) noexcept
Drop a delivery filter.
Definition agent.cpp:1702
virtual exception_reaction_t so_exception_reaction() const noexcept
A reaction from SObjectizer to an exception from agent's event.
Definition agent.cpp:871
void so_deactivate_agent()
Deactivate the agent.
Definition agent.cpp:945
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:1122
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:1291
const demands_handling_on_dereg_t m_demands_handling_on_dereg
What to do with pending demands on deregistration.
Definition agent.hpp:3085
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:1509
static constexpr const state_t::history_t shallow_history
Short alias for so_5::state_t::history_t::shallow.
Definition agent.hpp:730
void so_drop_all_subscriptions_and_filters()
Dropping all agents subscriptions and filters.
Definition agent.cpp:954
virtual void so_evt_finish()
Hook of agent finish in SObjectizer.
Definition agent.cpp:838
static demand_handler_pfn_t get_demand_handler_on_finish_ptr() noexcept
Definition agent.cpp:1503
void so_add_nondestroyable_listener(agent_state_listener_t &state_listener)
Add a state listener to the agent.
Definition agent.cpp:853
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:1713
const state_t * m_current_state_ptr
Current agent state.
Definition agent.hpp:2826
const mbox_t m_direct_mbox
A direct mbox for the agent.
Definition agent.hpp:2940
bool so_has_deadletter_handler(const so_5::mbox_t &mbox) const noexcept
Checks the presence of deadletter handler for a message of a specific type from a specific mbox.
Definition agent.hpp:2215
agent_t * self_ptr()
Definition agent.hpp:840
const priority_t m_priority
Priority of the agent.
Definition agent.hpp:2969
agent_identity_t so_agent_name() const noexcept
Get an optional name of the agent.
Definition agent.cpp:1134
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:1726
void bind_to_coop(coop_t &coop)
Bind agent to the cooperation.
Definition agent.cpp:1157
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:1760
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:1425
const mbox_t & so_direct_mbox() const
Get the agent's direct mbox.
Definition agent.cpp:887
impl::subscription_storage_unique_ptr_t m_subscriptions
All agent's subscriptions.
Definition agent.hpp:2881
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:1277
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:1467
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:1347
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:1322
agent_t(context_t ctx)
Constructor which simplifies agent construction with or without agent's tuning options.
Definition agent.cpp:788
static agent_tuning_options_t tuning_options()
Create tuning options object with default values.
Definition agent.hpp:1136
void so_add_destroyable_listener(agent_state_listener_unique_ptr_t state_listener)
Add a state listener to the agent.
Definition agent.cpp:862
environment_t & so_environment() const noexcept
Access to the SObjectizer Environment which this agent is belong.
Definition agent.cpp:987
priority_t so_priority() const noexcept
Get the priority of the agent.
Definition agent.hpp:2600
const state_t & so_default_state() const
Access to the agent's default state.
Definition agent.cpp:900
subscription_bind_t so_subscribe(const mbox_t &mbox_ref)
Initiate subscription.
Definition agent.hpp:1404
virtual ~agent_t()
Definition agent.cpp:824
state_time_limit_handling_data_t m_state_time_limit_handling_data
Data that is necessary for handling time limits of agent's states.
Definition agent.hpp:3077
void so_bind_to_dispatcher(event_queue_t &queue) noexcept
Binding agent to the dispatcher.
Definition agent.cpp:1006
void so_destroy_event_subscription(const mbox_t &mbox, const std::type_index &subscription_type, const state_t &target_state)
Destroy event subscription.
Definition agent.hpp:1506
void so_set_delivery_filter_for_mutable_msg(const mbox_t &mbox, Lambda &&lambda)
Set a delivery filter for a mutable message.
Definition agent.hpp:3600
environment_t & m_env
SObjectizer Environment for which the agent is belong.
Definition agent.hpp:2897
void so_deregister_agent_coop(int dereg_reason)
A helper method for deregistering agent's coop.
Definition agent.cpp:1109
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:1307
agent_t(environment_t &env, agent_tuning_options_t tuning_options)
Constructor which allows specification of agent's tuning options.
Definition agent.cpp:781
handler_finder_t m_handler_finder
Function for searching event handler.
Definition agent.hpp:2874
void define_state_time_limit_handling_data_if_needed()
Initialize data for handling time limit of agent's states.
Definition agent.cpp:1913
virtual void so_evt_start()
Hook on agent start inside SObjectizer.
Definition agent.cpp:832
static demand_handler_pfn_t get_demand_handler_on_enveloped_msg_ptr() noexcept
Definition agent.cpp:1544
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:1221
void so_set_delivery_filter(const mbox_t &mbox, delivery_filter_unique_ptr_t filter)
Set a delivery filter.
Definition agent.hpp:2471
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:1532
A collector for agent tuning options.
so_5::priority_t query_priority() const noexcept
Get priority value.
bool is_user_provided_subscription_storage_factory() const noexcept
Does a user provide a specific subscription_storage_factory?
demands_handling_on_dereg_t demands_handling_on_dereg() const noexcept
The base class for the object with a reference counting.
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
coop_handle_t handle() noexcept
Get handle for this coop.
Definition coop.hpp:432
An implementation of handler_invoker interface.
SObjectizer Environment.
void deregister_coop(coop_handle_t coop, int reason) noexcept
Deregister the cooperation.
An interface of event queue for agent.
A hint for a dispatcher for execution of event for the concrete execution_demand.
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
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.
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.
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.
agent_t::agent_status_t m_previous_status
Definition agent.cpp:910
state_switch_guard_t(agent_t &agent)
Definition agent.cpp:913
Template class for smart reference wrapper on the atomic_refcounted_t.
A base class for agent messages.
Definition message.hpp:47
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
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.
Helper class for simplify iteration on state's path.
Definition state.hpp:1841
state_path_t(const state_t &state) noexcept
Initializing constructor.
Definition state.hpp:1853
Information of time_limit for a state.
Definition agent.cpp:199
void change(duration_t limit, const state_t &state_to_switch) noexcept
Definition agent.cpp:224
const state_t & state_to_switch() const noexcept
Definition agent.cpp:314
void on_state_activation(const agent_t::state_time_limit_handling_data_t &info) noexcept
Definition agent.cpp:243
bool is_limit_exceeded(const steady_clock::time_point current_time) const noexcept
Definition agent.cpp:300
std::optional< activation_data_t > m_activation_data
Definition agent.cpp:355
std::reference_wrapper< const state_t > m_state_to_switch
The target state to switch after the timeout.
Definition agent.cpp:350
void activate(const agent_t::state_time_limit_handling_data_t &info)
Definition agent.cpp:268
time_limit_t(duration_t limit, const state_t &state_to_switch) noexcept
Initializing constructor.
Definition agent.cpp:208
void on_state_deactivation() noexcept
Definition agent.cpp:254
bool is_target(const agent_t *agent) const noexcept
Is agent owner of this state?
Definition agent.cpp:552
state_t & time_limit(duration_t timeout, const state_t &state_to_switch)
Set up a time limit for the state.
Definition agent.cpp:569
std::enable_if< details::is_agent_method_pointer< details::method_arity::nullary, Method_Pointer >::value, state_t & >::type on_enter(Method_Pointer pfn)
Set on enter handler.
Definition agent.hpp:4042
state_t & drop_time_limit()
Drop time limit for the state if defined.
Definition agent.cpp:618
history_t m_state_history
Type of state history.
Definition state.hpp:1643
const state_t * parent_state() const noexcept
Get a parent state if exists.
Definition state.hpp:1719
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:427
state_t(substate_of parent)
Constructor for the case when state is a substate of some parent state.
Definition agent.cpp:447
bool is_active() const noexcept
Is this state or any of its substates activated?
Definition agent.hpp:3918
state_t(state_t &&other)
Move constructor.
Definition agent.cpp:470
state_t(agent_t *target_agent, std::string state_name, state_t *parent_state, std::size_t nested_level, history_t state_history)
Fully initializing constructor.
Definition agent.cpp:362
state_t(agent_t *agent, std::string state_name, history_t state_history)
Definition agent.cpp:409
state_t * m_parent_state
Parent state.
Definition state.hpp:1624
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:458
const state_t * actual_state_to_enter() const
Find actual state to be activated for agent.
Definition agent.cpp:626
state_t(initial_substate_of parent)
Constructor for the case when state is the initial substate of some parent state.
Definition agent.cpp:416
const state_t * m_initial_substate
The initial substate.
Definition state.hpp:1636
bool operator==(const state_t &state) const noexcept
Definition agent.cpp:492
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:421
size_t m_substate_count
Number of substates.
Definition state.hpp:1673
state_t(agent_t *agent, history_t state_history)
Definition agent.cpp:396
bool operator!=(const state_t &state) const noexcept
Definition state.hpp:360
agent_t *const m_target_agent
Owner of this state.
Definition state.hpp:1605
std::string query_name() const
Get textual name of the state.
Definition agent.cpp:498
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:452
const state_t * m_last_active_substate
Last active substate.
Definition state.hpp:1654
void handle_time_limit_on_enter() const
A special handler of time limit to be used on entering into state.
Definition agent.cpp:670
state_t(agent_t *agent, std::string state_name)
Definition agent.cpp:403
void activate() const
Switch agent to that state.
Definition agent.cpp:563
history_t
Type of history for state.
Definition state.hpp:173
@ none
State has no history.
@ deep
State has deep history.
@ shallow
State has shallow history.
void update_history_in_parent_states() const
A helper method which is used during state change for update state with history.
Definition agent.cpp:649
void handle_time_limit_on_exit() const
A special handler of time limit to be used on exiting from state.
Definition agent.cpp:677
state_t(agent_t *agent)
Definition agent.cpp:390
A class for creating a subscription to messages from the mbox.
Definition agent.hpp:174
std::vector< const state_t * > state_vector_t
Type of vector of states.
Definition agent.hpp:414
mbox_t m_mbox_ref
Mbox for messages to subscribe.
Definition agent.hpp:407
subscription_bind_t & just_switch_to(const state_t &target_state)
Define handler which only switches agent to the specified state.
Definition agent.hpp:3856
void create_subscription_for_states(const std::type_index &msg_type, const event_handler_method_t &method, thread_safety_t thread_safety, event_handler_kind_t handler_kind) const
Create subscription of event for all states.
Definition agent.hpp:3879
subscription_bind_t(agent_t &agent, const mbox_t &mbox_ref)
Definition agent.hpp:3627
subscription_bind_t & in(const state_t &state)
Set up a state in which events are allowed be processed.
Definition agent.hpp:3636
subscription_bind_t & suppress()
Suppress processing of event in this state.
Definition agent.hpp:3832
subscription_bind_t & transfer_to_state(const state_t &target_state)
An instruction for switching agent to the specified state and transfering event proceessing to new st...
Definition agent.hpp:3699
state_vector_t m_states
States of agents the event to be subscribed in.
Definition agent.hpp:421
void ensure_handler_can_be_used_with_mbox(const so_5::details::msg_type_and_handler_pair_t &handler) const
Additional check for subscription to a mutable message from MPMC mbox.
Definition agent.hpp:3906
agent_t * m_agent
Agent to which we are subscribing.
Definition agent.hpp:405
An indentificator for the timer.
Definition timers.hpp:82
#define SO_5_TYPE
Definition declspec.hpp:46
#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:1365
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:722
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:759
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.
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:905
All stuff related to message limits.
Definition message.hpp:862
Private part of message limit implementation.
Definition agent.cpp:33
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
@ inherit_exception_reaction
Exception reaction should be inherited from SO Environment.
Definition agent.hpp:81
@ ignore_exception
Exception should be ignored and agent should continue its work.
Definition agent.hpp:75
@ deregister_coop_on_exception
Definition agent.hpp:73
@ shutdown_sobjectizer_on_exception
Definition agent.hpp:70
demands_handling_on_dereg_t
How pending demands should be handled on deregistration.
@ skip
Pending demands have to be skipped.
const thread_safety_t not_thread_safe
Shorthand for thread unsafety indicator.
Definition types.hpp:62
priority_t
Definition of supported priorities.
Definition priority.hpp:28
const thread_safety_t thread_safe
Shorthand for thread safety indicator.
Definition types.hpp:69
thread_safety_t
Thread safety indicator.
Definition types.hpp:50
@ unsafe
Not thread safe.
intrusive_ptr_t< Derived > make_agent_ref(Derived *agent)
Helper function template for the creation of smart pointer to an agent.
Definition agent.hpp:3567
@ user_type_message
Message is an user type message.
SO_5_FUNC void swap(name_for_agent_t &a, name_for_agent_t &b) noexcept
Definition agent.cpp:150
event_handler_kind_t
Kind of an event handler.
Definition types.hpp:154
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
A description of event execution demand.
mbox_id_t m_mbox_id
ID of mbox.
agent_t * m_receiver
Receiver of demand.
const message_limit::control_block_t * m_limit
Optional message limit for that 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_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 void decrement(const control_block_t *limit)
Definition message.hpp:1028
A mixin with message limit definition methods.
Helper type with method to be mixed into agent class.
activation_data_t(timer_id_t timer, steady_clock::time_point expiration_point)
Definition agent.cpp:336
steady_clock::time_point m_expiration_point
Timeout of timeout expiration.
Definition agent.cpp:334
timer_id_t m_timer
ID of delayed timeout signal.
Definition agent.cpp:331
Helper for marking a substate of composite state.
Definition state.hpp:89