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<
222 Method_Pointer>::value,
224 event(
225 //! Event handling method.
226 Method_Pointer pfn,
227 //! Thread safety of the event handler.
228 thread_safety_t thread_safety = not_thread_safe );
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<
267 details::lambda_traits::is_lambda<Lambda>::value,
269 event(
270 //! Event handler code.
271 Lambda && lambda,
272 //! Thread safety of the event handler.
273 thread_safety_t thread_safety = not_thread_safe );
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 {
475 so_5::current_thread_id_t & m_id;
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 }
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 >
712 using mutable_mhood_t = so_5::mutable_mhood_t< 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 * \note
909 * There is a change in behaviour of this methon in v.5.5.22.
910 * If some on_enter/on_exit handler calls this method during
911 * the state change procedure this method will return the state
912 * for which this on_enter/on_exit handler is called. For example:
913 * \code
914 * class demo final : public so_5::agent_t {
915 * state_t st_1{ this };
916 * state_t st_1_1{ initial_substate_of{st_1} };
917 * state_t st_1_2{ substate_of{st_1}};
918 * ...
919 * virtual void so_define_agent() override {
920 * st_1.on_enter([this]{
921 * assert(st_1 == so_current_state());
922 * ...
923 * });
924 * st_1_1.on_enter([this]{
925 * assert(st_1_1 == so_current_state());
926 * ...
927 * });
928 * ...
929 * }
930 * };
931 * \endcode
932 */
933 inline const state_t &
935 {
936 return *m_current_state_ptr;
937 }
938
939 /*!
940 * \brief Is a state activated?
941 *
942 * \note Since v.5.5.15 a state can have substates. For example
943 * state A can have substates B and C. If B is the current state
944 * then so_current_state() will return a reference to B. But
945 * state A is active too because it is a superstate for B.
946 * Method so_is_active_state(A) will return \a true in that case:
947 * \code
948 class demo : public so_5::agent_t
949 {
950 state_t A{ this, "A" };
951 state_t B{ initial_substate_of{ A }, "B" };
952 state_t C{ substate_of{ A }, "C" };
953 ...
954 void some_event()
955 {
956 this >>= C;
957
958 assert( C == so_current_state() );
959 assert( !( A == so_current_state() ) );
960 assert( so_is_active_state(A) );
961 ...
962 }
963 };
964 * \endcode
965 *
966 * \attention This method is not thread safe. Be careful calling
967 * this method from outside of agent's working thread.
968 *
969 * \return \a true if state \a state_to_check is the current state
970 * or if the current state is a substate of \a state_to_check.
971 *
972 * \since v.5.5.15
973 */
974 bool
975 so_is_active_state( const state_t & state_to_check ) const noexcept;
976
977 //! Add a state listener to the agent.
978 /*!
979 * A programmer should guarantee that the lifetime of
980 * \a state_listener is exceeds lifetime of the agent.
981 */
982 void
984 agent_state_listener_t & state_listener );
985
986 //! Add a state listener to the agent.
987 /*!
988 * Agent takes care of the \a state_listener destruction.
989 */
990 void
992 agent_state_listener_unique_ptr_t state_listener );
993
994 /*!
995 * \brief A reaction from SObjectizer to an exception from
996 * agent's event.
997 *
998 * If an exception is going out from agent's event it will be
999 * caught by SObjectizer. Then SObjectizer will call this method
1000 * and perform some actions in dependence of return value.
1001 *
1002 * \note Since v.5.3.0 default implementation calls
1003 * coop_t::exception_reaction() for agent's cooperation
1004 * object.
1005 *
1006 * \note
1007 * This method is noexcept since v.5.8.0.
1008 *
1009 * \since v.5.2.3
1010 */
1011 virtual exception_reaction_t
1012 so_exception_reaction() const noexcept;
1013
1014 /*!
1015 * \brief Switching agent to special state in case of unhandled
1016 * exception.
1017 *
1018 * \note
1019 * Since v.5.7.3 it's implemented via so_deactivate_agent().
1020 *
1021 * \attention
1022 * The method is not noexcept, it can throw an exception. So additional
1023 * care has to be taken when it's called in catch-block and/or in
1024 * noexcept contexts.
1025 *
1026 * \since 5.2.3
1027 */
1028 void
1030
1031 //! Push an event to the agent's event queue.
1032 /*!
1033 This method is used by SObjectizer for the
1034 agent's event scheduling.
1035 */
1036 static inline void
1038 agent_t & agent,
1039 const message_limit::control_block_t * limit,
1040 mbox_id_t mbox_id,
1041 const std::type_index & msg_type,
1042 const message_ref_t & message )
1043 {
1044 agent.push_event( limit, mbox_id, msg_type, message );
1045 }
1046
1047 /*!
1048 * \brief Get the agent's direct mbox.
1049 *
1050 * \since v.5.4.0
1051 */
1052 const mbox_t &
1053 so_direct_mbox() const;
1054
1055 /*!
1056 * \brief Create a new direct mbox for that agent.
1057 *
1058 * This method creates a new MPSC mbox which is connected
1059 * with that agent. Only agent for that so_make_new_direct_mbox()
1060 * has been called can make subscriptions for a new mbox.
1061 *
1062 * Note. The new mbox doesn't replaces the standard direct mbox
1063 * for the agent. Old direct mbox is still here and can still be used
1064 * for sending messages to the agent. But new mbox is not related
1065 * to the old direct mbox: they are different mboxes and can be used
1066 * for different subscriptions.
1067 * For example:
1068 * \code
1069 * class my_agent final : public so_5::agent_t {
1070 * ...
1071 * void so_evt_start() override {
1072 * so_subscribe_self().event( [](mhood_t<hello>) {
1073 * std::cout << "hello from the direct mbox" << std::endl;
1074 * } );
1075 *
1076 * const auto new_mbox = so_make_new_direct_mbox();
1077 * so_subscribe( new_mbox ).event( [](mhood_t<hello) {
1078 * std::cout << "hello from a new mbox" << std::endl;
1079 * }
1080 *
1081 * so_5::send<hello>(*this);
1082 * so_5::send<hello>(new_mbox);
1083 * }
1084 * };
1085 * \endcode
1086 * The output will be:
1087 \verbatim
1088 hello from the direct mbox
1089 hello from a new mbox
1090 \endverbatim
1091 *
1092 * \since v.5.6.0
1093 */
1094 mbox_t
1096
1097 /*!
1098 * \brief Create tuning options object with default values.
1099 *
1100 * \since v.5.5.3
1101 */
1102 inline static agent_tuning_options_t
1104 {
1106 }
1107
1108 /*!
1109 * \brief Helper for creation a custom direct mbox factory.
1110 *
1111 * Usage example:
1112 * \code
1113 * class my_agent : public so_5::agent_t {
1114 * ...
1115 * public:
1116 * my_agent( context_t ctx )
1117 * : so_5::agent_t{ ctx + custom_direct_mbox_factory(
1118 * []( so_5::partially_constructed_agent_ptr_t agent_ptr,
1119 * so_5::mbox_t actual_mbox )
1120 * {
1121 * return so_5::mbox_t{ new my_custom_mbox{ agent_ptr.ptr(), std::move(actual_mbox) } };
1122 * } )
1123 * }
1124 * {...}
1125 *
1126 * ...
1127 * };
1128 * \endcode
1129 *
1130 * \since v.5.7.4
1131 */
1132 template< typename Lambda >
1133 [[nodiscard]]
1134 static custom_direct_mbox_factory_t
1135 custom_direct_mbox_factory( Lambda && lambda )
1136 {
1137 return { std::forward<Lambda>(lambda) };
1138 }
1139
1140 protected:
1141 /*!
1142 * \name Accessing the default state.
1143 * \{
1144 */
1145
1146 //! Access to the agent's default state.
1147 const state_t &
1148 so_default_state() const;
1149 /*!
1150 * \}
1151 */
1152
1153 public : /* Note: since v.5.5.1 method so_change_state() is public */
1154
1155 /*!
1156 * \name Changing agent's state.
1157 * \{
1158 */
1159 //! Change the current state of the agent.
1160 /*!
1161 Usage sample:
1162 \code
1163 void a_sample_t::evt_smth( mhood_t< message_one_t > msg )
1164 {
1165 // If something wrong with the message then we should
1166 // switch to the error_state.
1167 if( error_in_data( *msg ) )
1168 so_change_state( m_error_state );
1169 }
1170 \endcode
1171
1172 \attention
1173 This method has to be called from a worker thread assigned
1174 to the agent by the dispatcher. This method can't be called from
1175 thread_safe event-handlers because so_change_state() modifies
1176 the state of the agent.
1177 */
1178 void
1180 //! New agent state.
1181 const state_t & new_state );
1182
1183 /*!
1184 * \brief Deactivate the agent.
1185 *
1186 * This method deactivates the agent:
1187 *
1188 * - drops all agent's subscriptions (including deadletter handlers) and
1189 * delivery filters;
1190 * - switches the agent to a special state in that the agent does nothing
1191 * and just waits the deregistration.
1192 *
1193 * Sometimes it is necessary to mark an agent as 'failed'. Such an agent
1194 * shouldn't process anything and the only thing that is allowed
1195 * is waiting for the deregistration. For example:
1196 *
1197 * \code
1198 * class some_agent final : public so_5::agent_t
1199 * {
1200 * state_t st_working{ this, "working" };
1201 * state_t st_failed{ this, "failed" };
1202 * ...
1203 * void on_enter_st_failed()
1204 * {
1205 * // Notify some supervisor about the failure.
1206 * // It will deregister the whole cooperation with failed agent.
1207 * so_5::send<msg_failure>( supervisor_mbox(), ... );
1208 * }
1209 * ...
1210 * void so_define_agent() override
1211 * {
1212 * this >>= st_working;
1213 *
1214 * st_failed.on_enter( &some_agent::on_enter_st_failed );
1215 *
1216 * ...
1217 * }
1218 *
1219 * void evt_some_event(mhood_t<some_msg> cmd)
1220 * {
1221 * try
1222 * {
1223 * do_some_processing_of(*cmd);
1224 * }
1225 * catch(...)
1226 * {
1227 * // Processing failed, agent can't continue work normally.
1228 * // Have to switch it to the failed state and wait for
1229 * // the deregistration.
1230 * this >>= st_failed;
1231 * }
1232 * }
1233 * ...
1234 * };
1235 * \endcode
1236 *
1237 * This approach works but has a couple of drawbacks:
1238 *
1239 * - it's necessary to define a separate state for an agent (like
1240 * st_failed shown above);
1241 * - agent still has all its subscriptions. It means that messages will
1242 * be delivered to the agent's event queue and dispatched by the
1243 * agent's dispatcher. They won't be handled because there are no
1244 * subscriptions in a state like st_failed, but message dispatching
1245 * will consume some resources, although the agent is a special
1246 * 'failed' state.
1247 *
1248 * To cope with those drawbacks so_deactivate_agent was introduced in
1249 * v.5.7.3. That method drops all agent's subscriptions (including
1250 * deadletter handlers and delivery filters) and switches the agent to a
1251 * special hidden state in that the agent doesn't handle anything.
1252 *
1253 * The example above now can be rewritten that way:
1254 * \code
1255 * class some_agent final : public so_5::agent_t
1256 * {
1257 * state_t st_working{ this, "working" };
1258 * ...
1259 * void switch_to_failed_state()
1260 * {
1261 * // Notify some supervisor about the failure.
1262 * // It will deregister the whole cooperation with failed agent.
1263 * so_5::send<msg_failure>( supervisor_mbox(), ... );
1264 * // Deactivate the agent.
1265 * so_deactivate_agent();
1266 * }
1267 * ...
1268 * void so_define_agent() override
1269 * {
1270 * this >>= st_working;
1271 * ...
1272 * }
1273 *
1274 * void evt_some_event(mhood_t<some_msg> cmd)
1275 * {
1276 * try
1277 * {
1278 * do_some_processing_of(*cmd);
1279 * }
1280 * catch(...)
1281 * {
1282 * // Processing failed, agent can't continue work normally.
1283 * // Have to switch it to the failed state and wait for
1284 * // the deregistration.
1285 * switch_to_failed_state();
1286 * }
1287 * }
1288 * ...
1289 * };
1290 * \endcode
1291 *
1292 * \note
1293 * The method uses so_change_state(), so it has all requirements of
1294 * so_change_state(). Because the agent state will be changed,
1295 * so_deactivate_state() has to be called on the working thread
1296 * assigned to the agent by the dispatcher, and
1297 * so_deactivate_state() can't be invoked from a thread_safe event
1298 * handler.
1299 *
1300 * \attention
1301 * The method is not noexcept, it can throw an exception. So additional
1302 * care has to be taken when it's called in catch-block and/or in
1303 * noexcept contexts.
1304 *
1305 * \since v.5.7.3
1306 */
1307 void
1309 /*!
1310 * \}
1311 */
1312
1313 public : /* Note: since v.5.2.3.2 subscription-related method are
1314 made public. */
1315
1316 /*!
1317 * \name Subscription methods.
1318 * \{
1319 */
1320
1321 //! Initiate subscription.
1322 /*!
1323 This method starts a subscription procedure by returning
1324 an instance of subscription_bind_t. The subscription details and
1325 the completion of a subscription is controlled by this
1326 subscription_bind_t object.
1327
1328 Usage sample:
1329 \code
1330 void a_sample_t::so_define_agent()
1331 {
1332 // Subscription for state `state_one`
1333 so_subscribe( mbox_target )
1334 .in( state_one )
1335 .event( &a_sample_t::evt_sample_handler );
1336
1337 // Subscription for the default state.
1338 so_subscribe( another_mbox )
1339 .event( &a_sample_t::evt_another_handler );
1340
1341 // Subscription for several event handlers in the default state.
1342 so_subscribe( yet_another_mbox )
1343 .event( &a_sample_t::evt_yet_another_handler )
1344 // Lambda-function can be used as event handler too.
1345 .event( [this](mhood_t<some_message> cmd) {...} );
1346
1347 // Subscription for several event handlers.
1348 // All of them will be subscribed for states first_state and second_state.
1349 so_subscribe( some_mbox )
1350 .in( first_state )
1351 .in( second_state )
1352 .event( &a_sample_t::evt_some_handler_1 )
1353 .event( &a_sample_t::evt_some_handler_2 )
1354 .event( &a_sample_t::evt_some_handler_3 );
1355 }
1356 \endcode
1357 */
1358 inline subscription_bind_t
1360 //! Mbox for messages to subscribe.
1361 const mbox_t & mbox_ref )
1362 {
1363 return subscription_bind_t( *this, mbox_ref );
1364 }
1365
1366 /*!
1367 * \brief Initiate subscription to agent's direct mbox.
1368 *
1369 * Note that is just a short form of:
1370 * \code
1371 * void a_sample_t::so_define_agent()
1372 * {
1373 * so_subscribe( so_direct_mbox() )
1374 * .in( some_state )
1375 * .in( another_state )
1376 * .event( some_event_handler )
1377 * .event( some_another_handler );
1378 * }
1379 * \endcode
1380 * Instead of writting `so_subscribe(so_direct_mbox())` it is possible
1381 * to write just `so_subscribe_self()`.
1382 *
1383 * \par Usage sample:
1384 \code
1385 void a_sample_t::so_define_agent()
1386 {
1387 // Subscription for state `state_one`
1388 so_subscribe_self()
1389 .in( state_one )
1390 .event( &a_sample_t::evt_sample_handler );
1391
1392 // Subscription for the default state.
1393 so_subscribe_self()
1394 .event( &a_sample_t::evt_another_handler );
1395
1396 // Subscription for several event handlers in the default state.
1397 so_subscribe_self()
1398 .event( &a_sample_t::evt_yet_another_handler )
1399 // Lambda-function can be used as event handler too.
1400 .event( [this](mhood_t<some_message> cmd) {...} );
1401
1402 // Subscription for several event handlers.
1403 // All of them will be subscribed for states first_state and second_state.
1404 so_subscribe_self()
1405 .in( first_state )
1406 .in( second_state )
1407 .event( &a_sample_t::evt_some_handler_1 )
1408 .event( &a_sample_t::evt_some_handler_2 )
1409 .event( &a_sample_t::evt_some_handler_3 );
1410 }
1411 \endcode
1412 *
1413 * \since v.5.5.1
1414 */
1415 inline subscription_bind_t
1420
1421 /*!
1422 * \brief Create a subscription for an event.
1423 *
1424 * \note
1425 * Before v.5.5.21 it was a private method. Since v.5.5.21
1426 * it is a public method with a standard so_-prefix.
1427 * It was made public to allow creation of subscriptions
1428 * to agent from outside of agent.
1429 *
1430 * \note
1431 * Parameter \a handler_kind was introduced in v.5.7.0.
1432 */
1433 void
1435 //! Message's mbox.
1436 const mbox_t & mbox_ref,
1437 //! Message type.
1438 std::type_index type_index,
1439 //! State for event.
1440 const state_t & target_state,
1441 //! Event handler caller.
1442 const event_handler_method_t & method,
1443 //! Thread safety of the event handler.
1444 thread_safety_t thread_safety,
1445 //! Kind of that event handler.
1446 event_handler_kind_t handler_kind );
1447
1448 /*!
1449 * \brief Destroy event subscription.
1450 *
1451 * \note
1452 * This method was introduced in v.5.5.21 to allow manipulation
1453 * of agent's subscriptions from outside of an agent.
1454 *
1455 * \note
1456 * It is safe to try to destroy nonexistent subscription.
1457 *
1458 * \since v.5.5.21
1459 */
1460 void
1462 //! Message's mbox.
1463 const mbox_t & mbox,
1464 //! Message's type.
1465 const std::type_index & subscription_type,
1466 //! Target state of a subscription.
1467 const state_t & target_state )
1468 {
1470 mbox,
1471 subscription_type,
1472 target_state );
1473 }
1474
1475 /*!
1476 * \brief Drop subscription for the state specified.
1477 *
1478 * This overload is indended to be used when there is an event-handler in
1479 * the form of agent's method. And there is a need to unsubscribe this
1480 * event handler.
1481 * For example:
1482 * \code
1483 * class demo : public so_5::agent_t {
1484 * void on_some_event(mhood_t<some_msg> cmd) {
1485 * if(cmd->some_condition)
1486 * // New subscription must be created.
1487 * so_subscribe(some_mbox).in(some_state)
1488 * .event(&demo::one_shot_message_handler);
1489 * ...
1490 * }
1491 *
1492 * void one_shot_message_handler(mhood_t<another_msg> cmd) {
1493 * ... // Some actions.
1494 * // Subscription is no more needed.
1495 * so_drop_subscription(some_mbox, some_state,
1496 * &demo::one_shot_message_handler);
1497 * }
1498 * };
1499 * \endcode
1500 *
1501 * \note Doesn't throw if there is no such subscription.
1502 *
1503 * \note Subscription is removed even if agent was subscribed
1504 * for this message type with different method pointer.
1505 * The pointer to event routine is necessary only to
1506 * detect MSG type.
1507 *
1508 * \since v.5.2.3
1509 */
1510 template< typename Method_Pointer >
1511 typename std::enable_if<
1514 Method_Pointer>::value,
1515 void >::type
1517 const mbox_t & mbox,
1518 const state_t & target_state,
1519 Method_Pointer /*pfn*/ )
1520 {
1523
1524 using message_type =
1526 typename pfn_traits::argument_type >::type;
1527
1530 target_state );
1531 }
1532
1533 /*!
1534 * \brief Drop subscription for the state specified.
1535 *
1536 * Usage example:
1537 * \code
1538 * class demo : public so_5::agent_t {
1539 * void on_turn_listening_on(mhood_t<turn_on> cmd) {
1540 * // New subscription must be created.
1541 * so_subscribe(cmd->listeting_mbox()).in(some_state)
1542 * .event([this](mhood_t<state_change_notify> cmd) {...});
1543 * ...
1544 * }
1545 *
1546 * void on_turn_listening_off(mhood_t<turn_off> cmd) {
1547 * // Subscription is no more needed.
1548 * so_drop_subscription<state_change_notify>(cmd->listening_mbox(), some_state);
1549 * ...
1550 * }
1551 * };
1552 * \endcode
1553 *
1554 * \note Doesn't throw if there is no such subscription.
1555 *
1556 * \since v.5.5.3
1557 */
1558 template< class Message >
1559 inline void
1561 const mbox_t & mbox,
1562 const state_t & target_state )
1563 {
1565 mbox,
1566 message_payload_type< Message >::subscription_type_index(),
1567 target_state );
1568 }
1569
1570 /*!
1571 * \brief Drop subscription for the default agent state.
1572 *
1573 * This overload is indended to be used when there is an event-handler in
1574 * the form of agent's method. And there is a need to unsubscribe this
1575 * event handler.
1576 * For example:
1577 * \code
1578 * class demo : public so_5::agent_t {
1579 * void on_some_event(mhood_t<some_msg> cmd) {
1580 * if(cmd->some_condition)
1581 * // New subscription must be created.
1582 * so_subscribe(some_mbox)
1583 * .event(&demo::one_shot_message_handler);
1584 * ...
1585 * }
1586 *
1587 * void one_shot_message_handler(mhood_t<another_msg> cmd) {
1588 * ... // Some actions.
1589 * // Subscription is no more needed.
1590 * so_drop_subscription(some_mbox,
1591 * &demo::one_shot_message_handler);
1592 * }
1593 * };
1594 * \endcode
1595 *
1596 * \note Doesn't throw if there is no such subscription.
1597 *
1598 * \note Subscription is removed even if agent was subscribed
1599 * for this message type with different method pointer.
1600 * The pointer to event routine is necessary only to
1601 * detect Msg type.
1602 *
1603 * \since v.5.2.3
1604 */
1605 template< typename Method_Pointer >
1606 typename std::enable_if<
1609 Method_Pointer>::value,
1610 void >::type
1612 const mbox_t & mbox,
1613 Method_Pointer /*pfn*/ )
1614 {
1617
1618 using message_type =
1620 typename pfn_traits::argument_type >::type;
1621
1623 mbox,
1625 so_default_state() );
1626 }
1627
1628 /*!
1629 * \brief Drop subscription for the default agent state.
1630 *
1631 * Usage example:
1632 * \code
1633 * class demo : public so_5::agent_t {
1634 * void on_turn_listening_on(mhood_t<turn_on> cmd) {
1635 * // New subscription must be created.
1636 * so_subscribe(cmd->listening_mbox())
1637 * .event([this](mhood_t<state_change_notify> cmd) {...});
1638 * ...
1639 * }
1640 *
1641 * void on_turn_listening_off(mhood_t<turn_off> cmd) {
1642 * // Subscription is no more needed.
1643 * so_drop_subscription<state_change_notify>(cmd->listening_mbox());
1644 * ...
1645 * }
1646 * };
1647 * \endcode
1648 *
1649 * \note Doesn't throw if there is no such subscription.
1650 *
1651 * \since v.5.5.3
1652 */
1653 template< class Message >
1654 inline void
1656 const mbox_t & mbox )
1657 {
1659 mbox,
1661 so_default_state() );
1662 }
1663
1664 /*!
1665 * \brief Drop subscription for all states.
1666 *
1667 * Usage example:
1668 * \code
1669 * class demo : public so_5::agent_t {
1670 * state_t st_working{this}, st_waiting{this}, st_stopping{this};
1671 * ...
1672 * void on_turn_listening_on(mhood_t<turn_on> cmd) {
1673 * // Make subscriptions for message of type state_change_notify.
1674 * st_working.event(cmd->listening_mbox(),
1675 * &demo::on_state_notify_when_working);
1676 * st_waiting.event(cmd->listening_mbox(),
1677 * &demo::on_state_notify_when_waiting);
1678 * st_waiting.event(cmd->listening_mbox(),
1679 * &demo::on_state_notify_when_stopping);
1680 * ...
1681 * }
1682 * void on_turn_listening_off(mhood_t<turn_off> cmd) {
1683 * // Subscriptions are no more needed.
1684 * // All three event handlers for state_change_notify
1685 * // will be unsubscribed.
1686 * so_drop_subscription_for_all_states(cmd->listening_mbox(),
1687 * &demo::on_state_notify_when_working);
1688 * }
1689 * ...
1690 * void on_state_notify_when_working(mhood_t<state_change_notify> cmd) {...}
1691 * void on_state_notify_when_waiting(mhood_t<state_change_notify> cmd) {...}
1692 * void on_state_notify_when_stopping(mhood_t<state_change_notify> cmd) {...}
1693 * };
1694 * \endcode
1695 *
1696 * \note Doesn't throw if there is no any subscription for
1697 * that mbox and message type.
1698 *
1699 * \note Subscription is removed even if agent was subscribed
1700 * for this message type with different method pointer.
1701 * The pointer to event routine is necessary only to
1702 * detect Msg type.
1703 *
1704 * \note
1705 * Since v.5.5.21 this method also drops the subscription
1706 * for a deadletter handler for that type of message/signal.
1707 *
1708 * \since v.5.2.3
1709 */
1710 template< typename Method_Pointer >
1711 typename std::enable_if<
1714 Method_Pointer>::value,
1715 void >::type
1717 const mbox_t & mbox,
1718 Method_Pointer /*pfn*/ )
1719 {
1722
1723 using message_type =
1725 typename pfn_traits::argument_type >::type;
1726
1728 mbox,
1730 }
1731
1732 /*!
1733 * \brief Drop subscription for all states.
1734 *
1735 * Usage example:
1736 * \code
1737 * class demo : public so_5::agent_t {
1738 * state_t st_working{this}, st_waiting{this}, st_stopping{this};
1739 * ...
1740 * void on_turn_listening_on(mhood_t<turn_on> cmd) {
1741 * // Make subscriptions for message of type state_change_notify.
1742 * st_working.event(cmd->listening_mbox(),
1743 * [this](mhood_t<state_change_notify> cmd) {...});
1744 * st_waiting.event(cmd->listening_mbox(),
1745 * [this](mhood_t<state_change_notify> cmd) {...});
1746 * st_waiting.event(cmd->listening_mbox(),
1747 * [this](mhood_t<state_change_notify> cmd) {...});
1748 * ...
1749 * }
1750 * void on_turn_listening_off(mhood_t<turn_off> cmd) {
1751 * // Subscriptions are no more needed.
1752 * // All three event handlers for state_change_notify
1753 * // will be unsubscribed.
1754 * so_drop_subscription_for_all_states<state_change_notify>(cmd->listening_mbox());
1755 * }
1756 * ...
1757 * };
1758 * \endcode
1759 * \note Doesn't throw if there is no any subscription for
1760 * that mbox and message type.
1761 *
1762 * \note
1763 * Since v.5.5.21 this method also drops the subscription
1764 * for a deadletter handler for that type of message/signal.
1765 *
1766 * \since v.5.5.3
1767 */
1768 template< class Message >
1769 inline void
1777
1778 /*!
1779 * \brief Check the presence of a subscription.
1780 *
1781 * This method can be used to avoid an exception from so_subscribe()
1782 * in the case if the subscription is already present. For example:
1783 * \code
1784 * void my_agent::evt_create_new_subscription(mhood_t<data_source> cmd)
1785 * {
1786 * // cmd can contain mbox we have already subscribed to.
1787 * // If we just call so_subscribe() then an exception can be thrown.
1788 * // Because of that check the presence of subscription first.
1789 * if(!so_has_subscription<message>(cmd->mbox(), so_default_state()))
1790 * {
1791 * // There is no subscription yet. New subscription can be
1792 * // created.
1793 * so_subscribe(cmd->mbox()).event(...);
1794 * }
1795 * }
1796 * \endcode
1797 *
1798 * \note
1799 * Please do not call this method from outside of working context
1800 * of the agent.
1801 *
1802 * \return true if subscription is present for \a target_state.
1803 *
1804 * \tparam Message a type of message/signal subscription to which
1805 * must be checked.
1806 *
1807 * \since v.5.5.19.5
1808 */
1809 template< class Message >
1810 bool
1812 //! A mbox from which message/signal of type \a Message is expected.
1813 const mbox_t & mbox,
1814 //! A target state for the subscription.
1815 const state_t & target_state ) const noexcept
1816 {
1818 mbox,
1820 target_state );
1821 }
1822
1823 /*!
1824 * \brief Check the presence of a subscription in the default_state.
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()))
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 the default_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 ) const noexcept
1859 {
1861 mbox,
1863 so_default_state() );
1864 }
1865
1866 /*!
1867 * \brief Check the presence of a subscription.
1868 *
1869 * Type of message is deducted from event-handler signature.
1870 *
1871 * Usage example:
1872 * \code
1873 * void my_agent::evt_create_new_subscription(mhood_t<data_source> cmd)
1874 * {
1875 * // cmd can contain mbox we have already subscribed to.
1876 * // If we just call so_subscribe() then an exception can be thrown.
1877 * // Because of that check the presence of subscription first.
1878 * if(!so_has_subscription(cmd->mbox(), my_state, &my_agent::my_event))
1879 * {
1880 * // There is no subscription yet. New subscription can be
1881 * // created.
1882 * so_subscribe(cmd->mbox()).event(...);
1883 * }
1884 * }
1885 * \endcode
1886 *
1887 * \note
1888 * Please do not call this method from outside of working context
1889 * of the agent.
1890 *
1891 * \return true if subscription is present for \a target_state.
1892 *
1893 * \since v.5.5.19.5
1894 */
1895 template< typename Method_Pointer >
1896 typename std::enable_if<
1899 Method_Pointer>::value,
1900 bool >::type
1902 //! A mbox from which message/signal is expected.
1903 const mbox_t & mbox,
1904 //! A target state for the subscription.
1905 const state_t & target_state,
1906 Method_Pointer /*pfn*/ ) const noexcept
1907 {
1910
1911 using message_type =
1913 typename pfn_traits::argument_type>::type;
1914
1915 return this->so_has_subscription<message_type>(
1916 mbox, target_state );
1917 }
1918
1919 /*!
1920 * \brief Check the presence of a subscription.
1921 *
1922 * Subscription is checked for the default agent state.
1923 *
1924 * Type of message is deducted from event-handler signature.
1925 *
1926 * Usage example:
1927 * \code
1928 * void my_agent::evt_create_new_subscription(mhood_t<data_source> cmd)
1929 * {
1930 * // cmd can contain mbox we have already subscribed to.
1931 * // If we just call so_subscribe() then an exception can be thrown.
1932 * // Because of that check the presence of subscription first.
1933 * if(!so_has_subscription(cmd->mbox(), &my_agent::my_event))
1934 * {
1935 * // There is no subscription yet. New subscription can be
1936 * // created.
1937 * so_subscribe(cmd->mbox()).event(...);
1938 * }
1939 * }
1940 * \endcode
1941 *
1942 * \note
1943 * Please do not call this method from outside of working context
1944 * of the agent.
1945 *
1946 * \return true if subscription is present for the default state.
1947 *
1948 * \since v.5.5.19.5
1949 */
1950 template< typename Method_Pointer >
1951 typename std::enable_if<
1954 Method_Pointer>::value,
1955 bool >::type
1957 //! A mbox from which message/signal is expected.
1958 const mbox_t & mbox,
1959 Method_Pointer /*pfn*/ ) const noexcept
1960 {
1963
1964 using message_type =
1966 typename pfn_traits::argument_type>::type;
1967
1968 return this->so_has_subscription<message_type>(
1970 }
1971 /*!
1972 * \}
1973 */
1974
1975 /*!
1976 * \name Methods for dealing with deadletter subscriptions.
1977 * \{
1978 */
1979 /*!
1980 * \brief Create a subscription for a deadletter handler.
1981 *
1982 * \note
1983 * This is low-level method intended to be used by libraries writters.
1984 * Do not call it directly if you don't understand its purpose and
1985 * what its arguments mean. Use so_subscribe_deadletter_handler()
1986 * instead.
1987 *
1988 * This method actually creates a subscription to deadletter handler
1989 * for messages/signal of type \a msg_type from mbox \a mbox.
1990 *
1991 * \throw so_5::exception_t in the case when the subscription
1992 * of a deadletter handler for type \a msg_type from \a mbox is
1993 * already exists.
1994 *
1995 * \since v.5.5.21
1996 */
1997 void
1999 //! Message's mbox.
2000 const mbox_t & mbox,
2001 //! Message type.
2002 const std::type_index & msg_type,
2003 //! Event handler caller.
2004 const event_handler_method_t & method,
2005 //! Thread safety of the event handler.
2006 thread_safety_t thread_safety );
2007
2008 /*!
2009 * \brief Destroy a subscription for a deadletter handler.
2010 *
2011 * \note
2012 * This is low-level method intended to be used by libraries writters.
2013 * Do not call it directly if you don't understand its purpose and
2014 * what its arguments mean. Use so_drop_deadletter_handler() instead.
2015 *
2016 * This method actually destroys a subscription to deadletter handler
2017 * for messages/signal of type \a msg_type from mbox \a mbox.
2018 *
2019 * \note
2020 * It is safe to call this method if there is no such
2021 * deadletter handler. It will do nothing in such case.
2022 *
2023 * \since v.5.5.21
2024 */
2025 void
2027 //! Message's mbox.
2028 const mbox_t & mbox,
2029 //! Message type.
2030 const std::type_index & msg_type );
2031
2032 /*!
2033 * \brief Create a subscription for deadletter handler for
2034 * a specific message from a specific mbox.
2035 *
2036 * Type of a message for deadletter handler will be detected
2037 * automatically from the signature of the \a handler.
2038 *
2039 * A deadletter handler can be a pointer to method of agent or
2040 * lambda-function. The handler should have one of the following
2041 * format:
2042 * \code
2043 * void deadletter_handler(message_type);
2044 * void deadletter_handler(const message_type &);
2045 * void deadletter_handler(mhood_t<message_type>);
2046 * \endcode
2047 *
2048 * Usage example:
2049 * \code
2050 * class demo : public so_5::agent_t {
2051 * void on_some_message(mhood_t<some_message> cmd) {...}
2052 * ...
2053 * virtual void so_define_agent() override {
2054 * // Create deadletter handler via pointer to method.
2055 * // Event handler will be not-thread-safe.
2056 * so_subscribe_deadletter_handler(
2057 * so_direct_mbox(),
2058 * &demo::on_some_message );
2059 *
2060 * // Create deadletter handler via lambda-function.
2061 * so_subscribe_deadletter_handler(
2062 * status_mbox(), // Any mbox can be used, not only agent's direct mbox.
2063 * [](mhood_t<status_request> cmd) {
2064 * so_5::send<current_status>(cmd->reply_mbox, "workind");
2065 * },
2066 * // This handler will be thread-safe one.
2067 * so_5::thread_safe );
2068 * }
2069 * };
2070 * \endcode
2071 *
2072 * \throw so_5::exception_t in the case when the subscription
2073 * of a deadletter handler for type \a msg_type from \a mbox is
2074 * already exists.
2075 *
2076 * \since v.5.5.21
2077 */
2078 template< typename Event_Handler >
2079 void
2081 const so_5::mbox_t & mbox,
2082 Event_Handler && handler,
2084 {
2085 using namespace details::event_subscription_helpers;
2086
2088 mbox,
2089 *this,
2091
2093 mbox,
2094 ev.m_msg_type,
2095 ev.m_handler,
2096 thread_safety );
2097 }
2098
2099 /*!
2100 * \brief Drops the subscription for deadletter handler.
2101 *
2102 * A message type must be specified explicitely via template
2103 * parameter.
2104 *
2105 * Usage example:
2106 * \code
2107 * class demo : public so_5::agent_t {
2108 * void some_deadletter_handler(mhood_t<some_message> cmd) {
2109 * ... // Do some stuff.
2110 * // There is no need for deadletter handler.
2111 * so_drop_deadletter_handler<some_message>(some_mbox);
2112 * }
2113 * ...
2114 * };
2115 * \endcode
2116 *
2117 * \note
2118 * Is is safe to call this method if there is no a deadletter
2119 * handler for message of type \a Message from message box
2120 * \a mbox.
2121 *
2122 * \tparam Message Type of a message or signal for deadletter
2123 * handler.
2124 *
2125 * \since v.5.5.21
2126 */
2127 template< typename Message >
2128 void
2130 //! A mbox from which the message is expected.
2131 const so_5::mbox_t & mbox )
2132 {
2134 mbox,
2136 }
2137
2138 /*!
2139 * \brief Checks the presence of deadletter handler for a message of
2140 * a specific type from a specific mbox.
2141 *
2142 * Message type must be specified explicitely via template
2143 * parameter \a Message.
2144 *
2145 * \return true if a deadletter for a message/signal of type
2146 * \a Message from message mbox \a mbox exists.
2147 *
2148 * Usage example:
2149 * \code
2150 * class demo : public so_5::agent_t {
2151 * void on_some_request(mhood_t<request_data> cmd) {
2152 * if(!so_has_deadletter_handler<some_message>(some_mbox))
2153 * // There is no deadletter handler yet.
2154 * // It should be created now.
2155 * so_subscribe_deadletter_handler(
2156 * some_mbox,
2157 * [this](mhood_t<some_message> cmd) {...});
2158 * ...
2159 * }
2160 * };
2161 * \endcode
2162 *
2163 * \tparam Message Type of a message or signal for deadletter
2164 * handler.
2165 *
2166 * \since v.5.5.21
2167 */
2168 template< typename Message >
2169 bool
2171 //! A mbox from which the message is expected.
2172 const so_5::mbox_t & mbox ) const noexcept
2173 {
2175 mbox,
2177 }
2178 /*!
2179 * \}
2180 */
2181
2182 protected :
2183
2184 /*!
2185 * \name Agent initialization methods.
2186 * \{
2187 */
2188 /*!
2189 * \brief A correct initiation of so_define_agent method call.
2190 *
2191 * Before the actual so_define_agent() method it is necessary
2192 * to temporary set working thread id. And then drop this id
2193 * to non-actual value after so_define_agent() return.
2194 *
2195 * Because of that this method must be called during cooperation
2196 * registration procedure instead of direct call of so_define_agent().
2197 *
2198 * \since v.5.4.0
2199 */
2200 void
2202
2203 //! Hook on define agent for SObjectizer.
2204 /*!
2205 This method is called by SObjectizer during the cooperation
2206 registration process before agent will be bound to its
2207 working thread.
2208
2209 Should be used by the agent to make necessary message subscriptions.
2210
2211 Usage sample;
2212 \code
2213 class a_sample_t : public so_5::agent_t
2214 {
2215 // ...
2216 virtual void
2217 so_define_agent();
2218
2219 void evt_handler_1( mhood_t< message1 > msg );
2220 // ...
2221 void evt_handler_N( mhood_t< messageN > & msg );
2222
2223 };
2224
2225 void
2226 a_sample_t::so_define_agent()
2227 {
2228 // Make subscriptions...
2229 so_subscribe( m_mbox1 )
2230 .in( m_state_1 )
2231 .event( &a_sample_t::evt_handler_1 );
2232 // ...
2233 so_subscribe( m_mboxN )
2234 .in( m_state_N )
2235 .event( &a_sample_t::evt_handler_N );
2236 }
2237 \endcode
2238 */
2239 virtual void
2241
2242 //! Is method define_agent already called?
2243 /*!
2244 Usage sample:
2245 \code
2246 class a_sample_t : public so_5::agent_t
2247 {
2248 // ...
2249
2250 public:
2251 void
2252 set_target_mbox( const so_5::mbox_t & mbox )
2253 {
2254 // mbox cannot be changed after agent registration.
2255 if( !so_was_defined() )
2256 {
2257 m_target_mbox = mbox;
2258 }
2259 }
2260
2261 private:
2262 so_5::mbox_t m_target_mbox;
2263 };
2264 \endcode
2265 */
2266 bool
2267 so_was_defined() const;
2268 /*!
2269 * \}
2270 */
2271
2272 public:
2273 //! Access to the SObjectizer Environment which this agent is belong.
2274 /*!
2275 Usage sample for other cooperation registration:
2276 \code
2277 void a_sample_t::evt_on_smth( mhood_t< some_message_t > msg )
2278 {
2279 so_5::coop_unique_holder_t coop = so_environment().make_coop();
2280
2281 // Filling the cooperation...
2282 coop->make_agent< a_another_t >( ... );
2283 // ...
2284
2285 // Registering cooperation.
2286 so_environment().register_coop( std::move(coop) );
2287 }
2288 \endcode
2289
2290 Usage sample for the SObjectizer shutting down:
2291 \code
2292 void a_sample_t::evt_last_event( mhood_t< message_one_t > msg )
2293 {
2294 ...
2295 so_environment().stop();
2296 }
2297 \endcode
2298 */
2300 so_environment() const noexcept;
2301
2302 /*!
2303 * \brief Get a handle of agent's coop.
2304 *
2305 * \note
2306 * This method is a replacement for so_coop_name() method
2307 * from previous versions of SObjectizer-5.
2308 *
2309 * \attention
2310 * If this method is called when agent is not registered (e.g.
2311 * there is no coop for agent) then this method will throw.
2312 *
2313 * Usage example:
2314 * \code
2315 * class parent final : public so_5::agent_t {
2316 * ...
2317 * void so_evt_start() override {
2318 * // Create a child coop.
2319 * auto coop = so_environment().make_coop(
2320 * // We as a parent coop.
2321 * so_coop() );
2322 * ...; // Fill the coop.
2323 * so_environment().register_coop( std::move(coop) );
2324 * }
2325 * };
2326 * \endcode
2327 *
2328 * \since v.5.6.0
2329 */
2330 [[nodiscard]]
2332 so_coop() const;
2333
2334 /*!
2335 * \brief Binding agent to the dispatcher.
2336 *
2337 * This is an actual start of agent's work in SObjectizer.
2338 *
2339 * \note
2340 * This method was a de-facto noexcept in previous versions of
2341 * SObjectizer. But didn't marked as noexcept because of need of
2342 * support old C++ compilers. Since v.5.6.0 it is officially noexcept.
2343 *
2344 * \since v.5.4.0
2345 */
2346 void
2348 //! Actual event queue for an agent.
2349 event_queue_t & queue ) noexcept;
2350
2351 /*!
2352 * \brief Create execution hint for the specified demand.
2353 *
2354 * The hint returned is intendent for the immediately usage.
2355 * It must not be stored for the long time and used sometime in
2356 * the future. It is because internal state of the agent
2357 * can be changed and some references from hint object to
2358 * agent's internals become invalid.
2359 *
2360 * \since v.5.4.0
2361 */
2362 static execution_hint_t
2364 //! Demand for execution of event handler.
2365 execution_demand_t & demand );
2366
2367 /*!
2368 * \brief A helper method for deregistering agent's coop.
2369 *
2370 * Usage example:
2371 * \code
2372 * class demo : public so_5::agent_t {
2373 * ...
2374 * void on_some_event(mhood_t<some_msg> cmd) {
2375 * try {
2376 * ... // Some processing.
2377 * if(no_more_work_left())
2378 * // Normal deregistration of the coop.
2379 * so_deregister_agent_coop_normally();
2380 * }
2381 * catch(...) {
2382 * // Some error.
2383 * // Deregister the coop with special 'exit code'.
2384 * so_deregister_agent_coop(so_5::dereg_reason::user_defined_reason+10);
2385 * }
2386 * }
2387 * };
2388 * \endcode
2389 *
2390 * \since v.5.4.0
2391 */
2392 void
2393 so_deregister_agent_coop( int dereg_reason );
2394
2395 /*!
2396 * \brief A helper method for deregistering agent's coop
2397 * in case of normal deregistration.
2398 *
2399 * \note It is just a shorthand for:
2400 \code
2401 so_deregister_agent_coop( so_5::dereg_reason::normal );
2402 \endcode
2403 *
2404 * \since v.5.4.0
2405 */
2406 void
2408
2409 /*!
2410 * \name Methods for dealing with message delivery filters.
2411 * \{
2412 */
2413 /*!
2414 * \brief Set a delivery filter.
2415 *
2416 * \note
2417 * Since v.5.7.4 it can be used for mutable messages too (if mbox is MPSC mbox).
2418 * In that case \a Message should be in form `so_5::mutable_msg<Message>`.
2419 *
2420 * \tparam Message type of message to be filtered.
2421 *
2422 * \since v.5.5.5
2423 */
2424 template< typename Message >
2425 void
2427 //! Message box from which message is expected.
2428 //! This must be MPMC-mbox.
2429 const mbox_t & mbox,
2430 //! Delivery filter instance.
2431 delivery_filter_unique_ptr_t filter )
2432 {
2434
2436 mbox,
2438 std::move(filter) );
2439 }
2440
2441 /*!
2442 * \brief Set a delivery filter.
2443 *
2444 * \tparam Lambda type of lambda-function or functional object which
2445 * must be used as message filter.
2446 *
2447 * \par Usage sample:
2448 \code
2449 void my_agent::so_define_agent() {
2450 so_set_delivery_filter( temp_sensor,
2451 []( const current_temperature & msg ) {
2452 return !is_normal_temperature( msg );
2453 } );
2454 ...
2455 }
2456 \endcode
2457 *
2458 * \since v.5.5.5
2459 */
2460 template< typename Lambda >
2461 void
2463 //! Message box from which message is expected.
2464 //! This must be MPMC-mbox.
2465 const mbox_t & mbox,
2466 //! Delivery filter as lambda-function or functional object.
2467 Lambda && lambda );
2468
2469 /*!
2470 * \brief Set a delivery filter for a mutable message.
2471 *
2472 * \tparam Lambda type of lambda-function or functional object which
2473 * must be used as message filter.
2474 *
2475 * \note
2476 * The content of the message will be passed to delivery-filter
2477 * lambda-function by a const reference.
2478 *
2479 * \par Usage sample:
2480 \code
2481 void my_agent::so_define_agent() {
2482 so_set_delivery_filter_for_mutable_msg( temp_sensor,
2483 []( const current_temperature & msg ) {
2484 return !is_normal_temperature( msg );
2485 } );
2486 ...
2487 }
2488 \endcode
2489 *
2490 * \since v.5.7.4
2491 */
2492 template< typename Lambda >
2493 void
2495 //! Message box from which message is expected.
2496 //! This must be MPMC-mbox.
2497 const mbox_t & mbox,
2498 //! Delivery filter as lambda-function or functional object.
2499 Lambda && lambda );
2500
2501 /*!
2502 * \brief Drop a delivery filter.
2503 *
2504 * Usage example:
2505 * \code
2506 * // For a case of an immutable message.
2507 * void some_agent::some_event(mhood_t<my_message> cmd) {
2508 * ... // Some actions.
2509 * // Now we want to drop the subscription and the delivery
2510 * // filter for this message.
2511 * so_drop_subscription_for_all_states<my_message>(source_mbox);
2512 * so_drop_delivery_filter<my_message>(source_mbox);
2513 * }
2514 *
2515 * // For a case of a mutable message.
2516 * void some_agent::some_event(mutable_mhood_t<my_message> cmd) {
2517 * ... // Some actions.
2518 * // Now we want to drop the subscription and the delivery
2519 * // filter for this message.
2520 * so_drop_subscription_for_all_states<so_5::mutable_msg<my_message>>(source_mbox);
2521 * so_drop_delivery_filter<so_5::mutable_msg<my_message>>(source_mbox);
2522 * }
2523 * \endcode
2524 *
2525 * \tparam Message type of message filtered.
2526 *
2527 * \since v.5.5.5
2528 */
2529 template< typename Message >
2530 void
2532 //! Message box to which delivery filter was set.
2533 //! This must be MPMC-mbox.
2534 const mbox_t & mbox ) noexcept
2535 {
2537 mbox,
2539 }
2540 /*!
2541 * \}
2542 */
2543
2544 /*!
2545 * \name Dealing with priority.
2546 * \{
2547 */
2548 /*!
2549 * \brief Get the priority of the agent.
2550 *
2551 * \since v.5.5.8
2552 */
2553 [[nodiscard]]
2555 so_priority() const noexcept
2556 {
2557 return m_priority;
2558 }
2559 /*!
2560 * \}
2561 */
2562
2563 /*!
2564 * \brief Helper method that allows to run a block of code as
2565 * non-thread-safe event handler.
2566 *
2567 * \attention
2568 * This is a low-level method. Using it may destroy all thread-safety
2569 * guarantees provided by SObjectizer. Please use it only when you know
2570 * what your are doing. All responsibility rests with the user.
2571 *
2572 * Use of this method may be necessary when an agent is bound to a
2573 * special dispatcher that runs not only the agent's event-handlers, but
2574 * also other callbacks on the same worker thread.
2575 *
2576 * A good example of such a dispatcher is so5extra's asio_one_thread
2577 * dispatcher. It guarantees that an IO completion handler is called on
2578 * the same worker thread as agent's event-handlers. For example:
2579 * \code
2580 * class agent_that_uses_asio : public so_5::agent_t
2581 * {
2582 * state_t st_not_ready{this};
2583 * state_t st_ready{this};
2584 *
2585 * asio::io_context & io_ctx_;
2586 *
2587 * public:
2588 * agent_that_uses_asio(context_t ctx, asio::io_context & io_ctx)
2589 * : so_5::agent_t{std::move(ctx)}
2590 * , io_ctx_{io_ctx}
2591 * {}
2592 * ...
2593 * void so_define_agent() override
2594 * {
2595 * st_not_ready.activate();
2596 * ...
2597 * }
2598 *
2599 * void so_evt_start() override
2600 * {
2601 * auto resolver = std::make_shared<asio::ip::tcp::resolver>(io_ctx_);
2602 * resolver->async_resolve("some.host.name", "",
2603 * asio::ip::tcp::numeric_service | asio::ip::tcp::address_configured,
2604 * // IO completion handler to be run on agent's worker thread.
2605 * [resolver, this](auto ec, auto results) {
2606 * // It's necessary to wrap the block of code, otherwise
2607 * // modification of the agent's state (or managing of subscriptions)
2608 * // will be prohibited because SObjectizer doesn't see
2609 * // the IO completion handler as event handler.
2610 * so_low_level_exec_as_event_handler( [&]() {
2611 * ...
2612 * st_ready.activate();
2613 * });
2614 * });
2615 * }
2616 * }
2617 * \endcode
2618 *
2619 * \attention
2620 * Using this method inside a running event-handler (non-thread-safe and
2621 * especially thread-safe) is undefined behavior. SObjectizer can't check
2622 * such a case without a significant performance penalty, so there won't
2623 * be any warnings or errors from SObjectizer's side, anything can
2624 * happen.
2625 *
2626 * \since v.5.8.0
2627 */
2628 template< typename Lambda >
2629 decltype(auto)
2631 Lambda && lambda ) noexcept( noexcept(lambda()) )
2632 {
2636 };
2637
2638 return lambda();
2639 }
2640
2641 /*!
2642 * \brief Returns the dispatcher binder that is used for binding this
2643 * agent.
2644 *
2645 * \attention
2646 * It's safe to use this method only while the agent is registered
2647 * in a SObjectizer Environment -- from the start of so_evt_start() until
2648 * the completion so_evt_finish(). The calling of this method when agent
2649 * it not registered (e.g. before the invocation of so_evt_start() or
2650 * after the completion of so_evt_finish()) may lead to undefined behavior.
2651 *
2652 * This method is intended to simplify creation of children cooperations:
2653 * \code
2654 * void parent_agent::evt_some_command(mhood_t<msg_command> cmd) {
2655 * ...
2656 * // A new child coop has to be created and bound to the same
2657 * // dispatcher as the parent agent.
2658 * so_5::introduce_child_coop( *this,
2659 * // Get the binder of the parent.
2660 * so_this_agent_disp_binder(),
2661 * [&](so_5::coop_t & coop) {
2662 * ... // Creation of children agents.
2663 * } );
2664 * }
2665 * \endcode
2666 *
2667 * \since v.5.8.1
2668 */
2669 [[nodiscard]]
2670 disp_binder_shptr_t
2672 {
2673 return m_disp_binder;
2674 }
2675
2676 /*!
2677 * \brief Returns the dispatcher binder that is used as the default
2678 * binder for the agent's coop.
2679 *
2680 * \attention
2681 * It's safe to use this method only while the agent is registered
2682 * in a SObjectizer Environment -- from the start of so_evt_start() until
2683 * the completion so_evt_finish(). The calling of this method when agent
2684 * it not registered (e.g. before the invocation of so_evt_start() or
2685 * after the completion of so_evt_finish()) may lead to undefined behavior.
2686 *
2687 * This method is intended to simplify creation of children cooperations:
2688 * \code
2689 * void parent_agent::evt_some_command(mhood_t<msg_command> cmd) {
2690 * ...
2691 * // A new child coop has to be created and bound to the same
2692 * // dispatcher as the parent agent.
2693 * so_5::introduce_child_coop( *this,
2694 * // Get the binder of the parent's coop.
2695 * so_this_coop_disp_binder(),
2696 * [&](so_5::coop_t & coop) {
2697 * ... // Creation of children agents.
2698 * } );
2699 * }
2700 * \endcode
2701 *
2702 * \note
2703 * This method may return a different binder that so_this_agent_disp_binder()
2704 * in a case when the agent was bound by a separate dispatcher. For example:
2705 * \code
2706 * // The parent coop will use thread_pool dispatcher as
2707 * // the default dispatcher.
2708 * env.introduce_coop(
2709 * so_5::disp::thread_pool::make_dispatcher( env, 8u )
2710 * .binder( []( auto & params ) {
2711 * // Every agent will have a separate event queue.
2712 * params.fifo( so_5::disp::thread_pool::fifo_t::individual );
2713 * } ),
2714 * [&]( so_5::coop_t & coop ) {
2715 * // The parent agent itself will use a separate dispatcher.
2716 * coop.make_agent_with_binder< parent_agent >(
2717 * so_5::disp::one_thread::make_dispatcher( env ).binder(),
2718 * ... );
2719 *
2720 * ... // Creation of other agents.
2721 * } );
2722 * \endcode
2723 * In that case use of so_this_agent_disp_binder() instead of
2724 * so_this_coop_disp_binder() will bind children agents to the
2725 * parent's one_thread dispatcher instead of coop's thread_pool
2726 * dispatcher.
2727 *
2728 * \since v.5.8.1
2729 */
2730 [[nodiscard]]
2731 disp_binder_shptr_t
2733
2734 /*!
2735 * \brief Get an optional name of the agent.
2736 *
2737 * If agent has the name then a reference to this name will be returned.
2738 * Otherwise a small object with a pointer to agent will be returned.
2739 *
2740 * The result can be printed to std::ostream or converted into a string:
2741 * \code
2742 * class my_agent final : public so_5::agent_t
2743 * {
2744 * ...
2745 * void so_evt_start() override
2746 * {
2747 * std::cout << so_agent_name() << ": started" << std::endl;
2748 * ...
2749 * so_5::send<std::string>(some_mbox, so_agent_name().to_string());
2750 * }
2751 * void so_evt_finished() override
2752 * {
2753 * std::cout << so_agent_name() << ": stopped" << std::endl;
2754 * }
2755 * ...
2756 * }
2757 * \endcode
2758 *
2759 * \attention
2760 * This method returns a lightweight object that just holds a reference
2761 * to the agent's name (or a pointer to the agent). This object should
2762 * not be stored for the long time, because the references/pointers it
2763 * holds may become invalid. If you have to store the agent name for
2764 * a long time please convert the returned value into std::string and
2765 * store the resulting std::string object.
2766 *
2767 * \since v.5.8.2
2768 */
2769 [[nodiscard]]
2771 so_agent_name() const noexcept;
2772
2773 private:
2774 const state_t st_default{ self_ptr(), "<DEFAULT>" };
2775
2776 //! Current agent state.
2777 const state_t * m_current_state_ptr;
2778
2779 /*!
2780 * \brief Enumeration of possible agent statuses.
2781 *
2782 * \since v.5.5.18
2783 */
2784 enum class agent_status_t : char
2785 {
2786 //! Agent is not defined yet.
2787 //! This is an initial agent status.
2789 //! Agent is defined.
2790 defined,
2791 //! State switch operation is in progress.
2793 };
2794
2795 /*!
2796 * \brief Current agent status.
2797 *
2798 * \since v.5.5.18
2799 */
2801
2802 //! State listeners controller.
2803 impl::state_listener_controller_t m_state_listener_controller;
2804
2805 /*!
2806 * \brief Type of function for searching event handler.
2807 *
2808 * \since v.5.5.9
2809 */
2810 using handler_finder_t =
2811 const impl::event_handler_data_t *(*)(
2812 execution_demand_t & /* demand */,
2813 const char * /* context_marker */ );
2814
2815 /*!
2816 * \brief Function for searching event handler.
2817 *
2818 * \note The value is set only once in the constructor and
2819 * doesn't changed anymore.
2820 *
2821 * \since v.5.5.9
2822 */
2823 handler_finder_t m_handler_finder;
2824
2825 /*!
2826 * \brief All agent's subscriptions.
2827 *
2828 * \since v.5.4.0
2829 */
2830 impl::subscription_storage_unique_ptr_t m_subscriptions;
2831
2832 /*!
2833 * \brief Holder of message sinks for that agent.
2834 *
2835 * If message limits are defined for the agent it will be an actual
2836 * storage with separate sinks for every (message_type, message_limit).
2837 *
2838 * If message limits are not defined then it will be a special storage
2839 * with just one message sink (that sink will be used for all subscriptions).
2840 *
2841 * \since v.5.8.0
2842 */
2844
2845 //! SObjectizer Environment for which the agent is belong.
2847
2848 /*!
2849 * \brief Event queue operation protector.
2850 *
2851 * Initially m_event_queue is NULL. It is changed to actual value
2852 * in so_bind_to_dispatcher() method. And reset to nullptr again
2853 * in shutdown_agent().
2854 *
2855 * nullptr in m_event_queue means that methods push_event() will throw
2856 * away any new demand.
2857 *
2858 * It is necessary to provide guarantee that m_event_queue will be reset
2859 * to nullptr in shutdown_agent() only if there is no working
2860 * push_event() methods. To do that default_rw_spinlock_t is used. Method
2861 * push_event() acquire it in read-mode and shutdown_agent() acquires it
2862 * in write-mode. It means that shutdown_agent() cannot get access to
2863 * m_event_queue until there is working push_event().
2864 *
2865 * \since v.5.5.8
2866 */
2867 default_rw_spinlock_t m_event_queue_lock;
2868
2869 /*!
2870 * \brief A pointer to event_queue.
2871 *
2872 * After binding to the dispatcher is it pointed to the actual
2873 * event queue.
2874 *
2875 * After shutdown it is set to nullptr.
2876 *
2877 * \attention Access to m_event_queue value must be done only
2878 * under acquired m_event_queue_lock.
2879 *
2880 * \since v.5.5.8
2881 */
2883
2884 /*!
2885 * \brief A direct mbox for the agent.
2886 *
2887 * \since v.5.4.0
2888 */
2889 const mbox_t m_direct_mbox;
2890
2891 /*!
2892 * \brief Working thread id.
2893 *
2894 * Some actions like managing subscriptions and changing states
2895 * are enabled only on working thread id.
2896 *
2897 * \since v.5.4.0
2898 */
2899 so_5::current_thread_id_t m_working_thread_id;
2900
2901 //! Agent is belong to this cooperation.
2903
2904 /*!
2905 * \brief Delivery filters for that agents.
2906 *
2907 * \note Storage is created only when necessary.
2908 *
2909 * \since v.5.5.5
2910 */
2912
2913 /*!
2914 * \brief Priority of the agent.
2915 *
2916 * \since v.5.5.8
2917 */
2919
2920 /*!
2921 * \brief Binder for this agent.
2922 *
2923 * Since v.5.7.5 disp_binder for the agent is stored inside the agent.
2924 * It guarantees that disp_binder will be deleted after destruction
2925 * of the agent (if there is no circular references between the agent
2926 * and the disp_binder).
2927 *
2928 * This value will be set by coop_t when agent is being add to the
2929 * coop.
2930 *
2931 * \note
2932 * Access to that field provided by so_5::impl::internal_agent_iface_t.
2933 *
2934 * \since v.5.7.5
2935 */
2936 disp_binder_shptr_t m_disp_binder;
2937
2938 /*!
2939 * \brief Optional name for the agent.
2940 *
2941 * This value can be set in the constructor only and can't be changed
2942 * later.
2943 *
2944 * Empty value means that the name for the agent wasn't specified.
2945 *
2946 * \since v.5.8.2
2947 */
2949
2950 //! Destroy all agent's subscriptions.
2951 /*!
2952 * \note
2953 * This method is intended to be used in the destructor and
2954 * methods like so_deactivate_agent().
2955 *
2956 * \attention
2957 * It's noexcept method because there is no way to recover in case
2958 * when deletion of subscriptions throws.
2959 *
2960 * \since v.5.7.3
2961 */
2962 void
2964
2965 //! Make an agent reference.
2966 /*!
2967 * This is an internal SObjectizer method. It is called when
2968 * it is guaranteed that the agent is still necessary and something
2969 * has reference to it.
2970 */
2971 agent_ref_t
2972 create_ref();
2973
2974 /*!
2975 * \name Embedding agent into the SObjectizer Run-time.
2976 * \{
2977 */
2978
2979 //! Bind agent to the cooperation.
2980 /*!
2981 * Initializes an internal cooperation pointer.
2982 */
2983 void
2985 //! Cooperation for that agent.
2986 coop_t & coop );
2987
2988 //! Agent shutdown deriver.
2989 /*!
2990 * Method destroys all agent subscriptions.
2991 *
2992 * \since v.5.2.3
2993 */
2994 void
2995 shutdown_agent() noexcept;
2996 /*!
2997 * \}
2998 */
2999
3000 /*!
3001 * \name Subscription/unsubscription implementation details.
3002 * \{
3003 */
3004
3005 /*!
3006 * \brief Helper function that returns a message sink to be used
3007 * for subscriptions for specified message type.
3008 *
3009 * \since v.5.8.0
3010 */
3011 [[nodiscard]]
3014 const std::type_index & msg_type );
3015
3016 /*!
3017 * \brief Remove subscription for the state specified.
3018 *
3019 * \since v.5.2.3
3020 */
3021 void
3023 //! Message's mbox.
3024 const mbox_t & mbox,
3025 //! Message type.
3026 const std::type_index & msg_type,
3027 //! State for event.
3028 const state_t & target_state );
3029
3030 /*!
3031 * \brief Remove subscription for all states.
3032 *
3033 * \since v.5.2.3
3034 */
3035 void
3037 //! Message's mbox.
3038 const mbox_t & mbox,
3039 //! Message type.
3040 const std::type_index & msg_type );
3041
3042 /*!
3043 * \brief Check the presence of a subscription.
3044 *
3045 * \since v.5.5.19.5
3046 */
3047 bool
3049 //! Message's mbox.
3050 const mbox_t & mbox,
3051 //! Message type.
3052 const std::type_index & msg_type,
3053 //! State for the subscription.
3054 const state_t & target_state ) const noexcept;
3055
3056 /*!
3057 * \brief Check the presence of a deadletter handler.
3058 *
3059 * \since v.5.5.21
3060 */
3061 bool
3063 //! Message's mbox.
3064 const mbox_t & mbox,
3065 //! Message type.
3066 const std::type_index & msg_type ) const noexcept;
3067 /*!
3068 * \}
3069 */
3070
3071 /*!
3072 * \name Event handling implementation details.
3073 * \{
3074 */
3075
3076 //! Push event into the event queue.
3077 void
3078 push_event(
3079 //! Optional message limit.
3080 const message_limit::control_block_t * limit,
3081 //! ID of mbox for this event.
3082 mbox_id_t mbox_id,
3083 //! Message type for event.
3084 const std::type_index & msg_type,
3085 //! Event message.
3086 const message_ref_t & message );
3087 /*!
3088 * \}
3089 */
3090
3091 // NOTE: demand handlers declared as public to allow
3092 // access this handlers from unit-tests.
3093 public :
3094 /*!
3095 * \name Demand handlers.
3096 * \{
3097 */
3098 /*!
3099 * \brief Calls so_evt_start method for agent.
3100 *
3101 * \since v.5.2.0
3102 */
3103 static void
3105 current_thread_id_t working_thread_id,
3106 execution_demand_t & d );
3107
3108 /*!
3109 * \brief Ensures that all agents from cooperation are
3110 * bound to dispatchers.
3111 *
3112 * \since v.5.5.8
3113 */
3114 void
3116
3117 /*!
3118 * \note This method is necessary for GCC on Cygwin.
3119 *
3120 * \since v.5.4.0
3121 */
3122 static demand_handler_pfn_t
3124
3125 /*!
3126 * \brief Calls so_evt_finish method for agent.
3127 *
3128 * \since v.5.2.0
3129 */
3130 static void
3132 current_thread_id_t working_thread_id,
3133 execution_demand_t & d );
3134
3135 /*!
3136 * \note This method is necessary for GCC on Cygwin.
3137 *
3138 * \since v.5.4.0
3139 */
3140 static demand_handler_pfn_t
3142
3143 /*!
3144 * \brief Calls event handler for message.
3145 *
3146 * \since v.5.2.0
3147 */
3148 static void
3150 current_thread_id_t working_thread_id,
3151 execution_demand_t & d );
3152
3153 /*!
3154 * \note This method is necessary for GCC on Cygwin.
3155 *
3156 * \since v.5.4.0
3157 */
3158 static demand_handler_pfn_t
3160
3161 /*!
3162 * \brief Handles the enveloped message.
3163 *
3164 * \since v.5.5.23
3165 */
3166 static void
3168 current_thread_id_t working_thread_id,
3169 execution_demand_t & d );
3170
3171 /*!
3172 * \since v.5.5.24
3173 */
3174 static demand_handler_pfn_t
3176 /*!
3177 * \}
3178 */
3179
3180 private :
3181 /*!
3182 * \brief Actual implementation of message handling.
3183 *
3184 * \note Since v.5.5.17.1 argument \a method is passed as copy.
3185 * It prevents deallocation of event_handler_method in the following
3186 * case:
3187 * \code
3188 auto mbox = so_environment().create_mbox();
3189 so_subscribe( mbox ).event< some_signal >( [this, mbox] {
3190 so_drop_subscription< some_signal >( mbox );
3191 ... // Some other actions.
3192 } );
3193 * \endcode
3194 *
3195 * \attention
3196 * Implementation notes: it's important that \a method is passed
3197 * by value. It's because subscription can be deleted during
3198 * the work of process_message (due to unsubscription inside the
3199 * event handler) and if pass \a method by a reference then
3200 * that reference can become invalid.
3201 *
3202 * \since v.5.4.0
3203 */
3204 static void
3206 current_thread_id_t working_thread_id,
3208 thread_safety_t thread_safety,
3209 event_handler_method_t method );
3210
3211 /*!
3212 * \brief Actual implementation of enveloped message handling.
3213 *
3214 * \note
3215 * handler_data can be nullptr. It means that an event handler
3216 * for that message type if not found and special hook will
3217 * be called for the envelope.
3218 *
3219 * \since v.5.5.23
3220 */
3221 static void
3223 current_thread_id_t working_thread_id,
3225 const impl::event_handler_data_t * handler_data );
3226
3227 /*!
3228 * \brief Enables operation only if it is performed on agent's
3229 * working thread.
3230 *
3231 * \since v.5.4.0
3232 */
3233 void
3235 const char * operation_name ) const;
3236
3237 /*!
3238 * \brief Drops all delivery filters.
3239 *
3240 * \since v.5.5.0
3241 */
3242 void
3243 drop_all_delivery_filters() noexcept;
3244
3245 /*!
3246 * \brief Set a delivery filter.
3247 *
3248 * \since v.5.5.5
3249 */
3250 void
3252 const mbox_t & mbox,
3253 const std::type_index & msg_type,
3254 delivery_filter_unique_ptr_t filter );
3255
3256 /*!
3257 * \brief Drop a delivery filter.
3258 *
3259 * \since v.5.5.5
3260 */
3261 void
3263 const mbox_t & mbox,
3264 const std::type_index & msg_type ) noexcept;
3265
3266 /*!
3267 * \brief Handler finder for the case when message delivery
3268 * tracing is disabled.
3269 *
3270 * \since v.5.5.9
3271 */
3272 static const impl::event_handler_data_t *
3274 execution_demand_t & demand,
3275 const char * context_marker );
3276
3277 /*!
3278 * \brief Handler finder for the case when message delivery
3279 * tracing is enabled.
3280 *
3281 * \since v.5.5.9
3282 */
3283 static const impl::event_handler_data_t *
3285 execution_demand_t & demand,
3286 const char * context_marker );
3287
3288 /*!
3289 * \brief Actual search for event handler with respect
3290 * to parent-child relationship between agent states.
3291 *
3292 * \since v.5.5.15
3293 */
3294 static const impl::event_handler_data_t *
3296 execution_demand_t & demand );
3297
3298 /*!
3299 * \brief Search for event handler between deadletter handlers.
3300 *
3301 * \return nullptr if event handler is not found.
3302 *
3303 * \since v.5.5.21
3304 */
3305 static const impl::event_handler_data_t *
3307 execution_demand_t & demand );
3308
3309 /*!
3310 * \brief Perform actual operations related to state switch.
3311 *
3312 * It throws if the agent in awaiting_deregistration_state and
3313 * \a state_to_be_set isn't awaiting_deregistration_state.
3314 *
3315 * \note
3316 * This method doesn't check the working context. It's assumed
3317 * that this check has already been performed by caller.
3318 *
3319 * \since v.5.7.3
3320 */
3321 void
3323 //! New state to be set as the current state.
3324 const state_t & state_to_be_set );
3325
3326 /*!
3327 * \brief Actual action for switching agent state.
3328 *
3329 * \since v.5.5.15
3330 */
3331 void
3333 //! New state to be set as the current state.
3334 const state_t & state_to_be_set ) noexcept;
3335
3336 /*!
3337 * \brief Return agent to the default state.
3338 *
3339 * \note This method is called just before invocation of
3340 * so_evt_finish() to return agent to the default state.
3341 * This return will initiate invocation of on_exit handlers
3342 * for all active states of the agent.
3343 *
3344 * \attention State switch is not performed if agent is already
3345 * in default state or if it waits deregistration after unhandled
3346 * exception.
3347 *
3348 * \since v.5.5.15
3349 */
3350 void
3352
3353 /*!
3354 * \brief Is agent already deactivated.
3355 *
3356 * Deactivated agent is in awaiting_deregistration_state.
3357 * This method checks that the current state of the agent
3358 * is awaiting_deregistration_state.
3359 *
3360 * \attention
3361 * This method isn't thread safe and should be used with care.
3362 * A caller should guarantee that it's called from the right
3363 * working thread.
3364 *
3365 * \since v.5.7.3
3366 */
3367 bool
3368 is_agent_deactivated() const noexcept;
3369};
3370
3371/*!
3372 * \brief Helper function template for the creation of smart pointer
3373 * to an agent.
3374 *
3375 * This function can be useful if a pointer to an agent should be passed
3376 * somewhere with the guarantee that this pointer will remain valid even
3377 * if the agent will be deregistered.
3378 *
3379 * This could be necessary, for example, if a pointer to an agent is
3380 * passed to some callback (like it is done in Asio):
3381 * \code
3382 * void my_agent::on_some_event(mhood_t<some_msg> cmd) {
3383 * connection_.async_read_some(input_buffer_,
3384 * [self = so_5::make_agent_ref(this)](
3385 * const asio::error_code & ec,
3386 * std::size_t bytes_transferred )
3387 * {
3388 * if(!ec)
3389 * self->handle_incoming_data(bytes_transferred);
3390 * }
3391 * );
3392 * }
3393 * \endcode
3394 *
3395 * \since v.5.7.1
3396 */
3397template< typename Derived >
3398[[nodiscard]]
3399intrusive_ptr_t< Derived >
3400make_agent_ref( Derived * agent )
3401 {
3402 static_assert( std::is_base_of_v< agent_t, Derived >,
3403 "type should be derived from so_5::agent_t" );
3404
3405 return { agent };
3406 }
3407
3408template< typename Lambda >
3409void
3411 const mbox_t & mbox,
3412 Lambda && lambda )
3413 {
3414 using namespace so_5::details::lambda_traits;
3415
3417 using argument_type =
3419
3421
3423 mbox,
3427 std::move( lambda ) )
3428 } );
3429 }
3430
3431template< typename Lambda >
3432void
3434 const mbox_t & mbox,
3435 Lambda && lambda )
3436 {
3437 using namespace so_5::details::lambda_traits;
3438
3440 using argument_type =
3442
3444
3446
3448 mbox,
3452 std::move( lambda ) )
3453 } );
3454 }
3455
3456//
3457// subscription_bind_t implementation
3458//
3459inline
3461 agent_t & agent,
3462 const mbox_t & mbox_ref )
3463 : m_agent( &agent )
3464 , m_mbox_ref( mbox_ref )
3465{
3466}
3467
3468inline subscription_bind_t &
3470 const state_t & state )
3471{
3472 if( !state.is_target( m_agent ) )
3473 {
3476 "agent doesn't own the state" );
3477 }
3478
3479 m_states.push_back( &state );
3480
3481 return *this;
3482}
3483
3484template< typename Method_Pointer >
3485typename std::enable_if<
3488 Method_Pointer>::value,
3491 Method_Pointer pfn,
3492 thread_safety_t thread_safety )
3493{
3494 using namespace details::event_subscription_helpers;
3495
3498 ev.m_msg_type,
3499 ev.m_handler,
3502
3503 return *this;
3504}
3505
3506template<typename Lambda>
3507typename std::enable_if<
3508 details::lambda_traits::is_lambda<Lambda>::value,
3511 Lambda && lambda,
3512 thread_safety_t thread_safety )
3513{
3514 using namespace details::event_subscription_helpers;
3515
3516 const auto ev = preprocess_agent_event_handler(
3517 m_mbox_ref,
3518 *m_agent,
3519 std::forward<Lambda>(lambda) );
3520
3522 ev.m_msg_type,
3523 ev.m_handler,
3524 thread_safety,
3526
3527 return *this;
3528}
3529
3530template< typename Msg >
3533 const state_t & target_state )
3534{
3535 /*
3536 * Note. Since v.5.5.22.1 there is a new implementation of transfer_to_state.
3537 * New implementation protects from loops in transfer_to_state calls.
3538 * For example in the following cases:
3539 *
3540 * \code
3541 class a_simple_case_t final : public so_5::agent_t
3542 {
3543 state_t st_base{ this, "base" };
3544 state_t st_disconnected{ initial_substate_of{st_base}, "disconnected" };
3545 state_t st_connected{ substate_of{st_base}, "connected" };
3546
3547 struct message {};
3548
3549 public :
3550 a_simple_case_t(context_t ctx) : so_5::agent_t{ctx} {
3551 this >>= st_base;
3552
3553 st_base.transfer_to_state<message>(st_disconnected);
3554 }
3555
3556 virtual void so_evt_start() override {
3557 so_5::send<message>(*this);
3558 }
3559 };
3560
3561 class a_two_state_loop_t final : public so_5::agent_t
3562 {
3563 state_t st_one{ this, "one" };
3564 state_t st_two{ this, "two" };
3565
3566 struct message {};
3567
3568 public :
3569 a_two_state_loop_t(context_t ctx) : so_5::agent_t{ctx} {
3570 this >>= st_one;
3571
3572 st_one.transfer_to_state<message>(st_two);
3573 st_two.transfer_to_state<message>(st_one);
3574 }
3575
3576 virtual void so_evt_start() override {
3577 so_5::send<message>(*this);
3578 }
3579 };
3580 * \endcode
3581 *
3582 * For such protection an additional objects with the current state
3583 * of transfer_to_state operation is necessary. There will be a boolean
3584 * flag in that state. When transfer_to_state will be started this
3585 * flag will should be 'false'. But if it is already 'true' then there is
3586 * a loop in transfer_to_state calls.
3587 */
3588
3589 // This is the state of transfer_to_state operation.
3590 struct transfer_op_state_t
3591 {
3592 agent_t * m_agent;
3594 const state_t & m_target_state;
3595 bool m_in_progress;
3596
3598 agent_t * agent,
3601 : m_agent( agent )
3602 , m_mbox_id( mbox_id )
3604 , m_in_progress( false )
3605 {}
3606 };
3607
3608 //NOTE: shared_ptr is used because capture of unique_ptr
3609 //makes std::function non-copyable, but we need to copy
3610 //resulting 'method' object.
3611 //
3614
3615 auto method = [op_state]( message_ref_t & msg )
3616 {
3617 // The current transfer_to_state operation should be inactive.
3618 if( op_state->m_in_progress )
3620 "transfer_to_state loop detected. target_state: " +
3622 ", current_state: " +
3624
3625 // Activate transfer_to_state operation and make sure that it
3626 // will be deactivated on return automatically.
3627 op_state->m_in_progress = true;
3629 op_state->m_in_progress = false;
3630 } );
3631
3632 //
3633 // The main logic of transfer_to_state operation.
3634 //
3636
3639 nullptr, // Message limit is not actual here.
3641 typeid( Msg ),
3642 msg,
3643 // We have very simple choice here: message is an enveloped
3644 // message or just classical message/signal.
3645 // So we should select an appropriate demand handler.
3649 };
3650
3652 };
3653
3655 typeid( Msg ),
3656 method,
3659
3660 return *this;
3661}
3662
3663template< typename Msg >
3666{
3667 // A method with nothing inside.
3668 auto method = []( message_ref_t & ) {};
3669
3671 typeid( Msg ),
3672 method,
3674 // Suppression of a message is a kind of ignoring of the message.
3675 // In the case of enveloped message intermediate_handler receives
3676 // the whole envelope (not the payload) and the whole envelope
3677 // should be ignored. We can't specify final_handler here because
3678 // in that case the payload of an enveloped message will be
3679 // extracted from the envelope and envelope will be informed about
3680 // the handling of message. But message won't be handled, it will
3681 // be ignored.
3683
3684 return *this;
3685}
3686
3687template< typename Msg >
3690 const state_t & target_state )
3691{
3693
3695 {
3697 };
3698
3700 typeid( Msg ),
3701 method,
3703 // Switching to some state is a kind of message processing.
3704 // So if there is an enveloped message then the envelope will be
3705 // informed about the processing of the payload.
3707
3708 return *this;
3709}
3710
3711inline void
3713 const std::type_index & msg_type,
3714 const event_handler_method_t & method,
3715 thread_safety_t thread_safety,
3716 event_handler_kind_t handler_kind ) const
3717{
3718 if( m_states.empty() )
3719 // Agent should be subscribed only in default state.
3722 msg_type,
3724 method,
3725 thread_safety,
3726 handler_kind );
3727 else
3728 for( auto s : m_states )
3731 msg_type,
3732 *s,
3733 method,
3734 thread_safety,
3735 handler_kind );
3736}
3737
3738inline void
3746
3747/*
3748 * Implementation of template methods of state_t class.
3749 */
3750inline bool
3751state_t::is_active() const noexcept
3752{
3754}
3755
3756template< typename... Args >
3757const state_t &
3758state_t::event( Args&&... args ) const
3759{
3760 return this->subscribe_message_handler(
3762 std::forward< Args >(args)... );
3763}
3764
3765template< typename... Args >
3766const state_t &
3767state_t::event( mbox_t from, Args&&... args ) const
3768{
3769 return this->subscribe_message_handler( from,
3770 std::forward< Args >(args)... );
3771}
3772
3773template< typename Msg >
3774bool
3775state_t::has_subscription( const mbox_t & from ) const
3776{
3777 return m_target_agent->so_has_subscription< Msg >( from, *this );
3778}
3779
3780template< typename Method_Pointer >
3781bool
3783 const mbox_t & from,
3784 Method_Pointer && pfn ) const
3785{
3787 from,
3788 *this,
3790}
3791
3792template< typename Msg >
3793void
3794state_t::drop_subscription( const mbox_t & from ) const
3795{
3797}
3798
3799template< typename Method_Pointer >
3800void
3802 const mbox_t & from,
3803 Method_Pointer && pfn ) const
3804{
3806 from,
3807 *this,
3809}
3810
3811template< typename Msg >
3812const state_t &
3813state_t::transfer_to_state( mbox_t from, const state_t & target_state ) const
3814{
3816 .in( *this )
3818
3819 return *this;
3820}
3821
3822template< typename Msg >
3823const state_t &
3824state_t::transfer_to_state( const state_t & target_state ) const
3825{
3826 return this->transfer_to_state< Msg >(
3828 target_state );
3829}
3830
3831template< typename Msg >
3832const state_t &
3833state_t::just_switch_to( mbox_t from, const state_t & target_state ) const
3834{
3836 .in( *this )
3838
3839 return *this;
3840}
3841
3842template< typename Msg >
3843const state_t &
3844state_t::just_switch_to( const state_t & target_state ) const
3845{
3846 return this->just_switch_to< Msg >(
3848 target_state );
3849}
3850
3851template< typename Msg >
3852const state_t &
3853state_t::suppress() const
3854{
3855 return this->suppress< Msg >( m_target_agent->so_direct_mbox() );
3856}
3857
3858template< typename Msg >
3859const state_t &
3860state_t::suppress( mbox_t from ) const
3861{
3863 .in( *this )
3864 .suppress< Msg >();
3865
3866 return *this;
3867}
3868
3869template< typename Method_Pointer >
3870typename std::enable_if<
3873 Method_Pointer>::value,
3874 state_t & >::type
3875state_t::on_enter( Method_Pointer pfn )
3876{
3877 using namespace details::event_subscription_helpers;
3878
3881
3882 // Agent must have right type.
3883 auto cast_result =
3885 typename pfn_traits::agent_type >(
3886 *m_target_agent );
3887
3888 return this->on_enter( [cast_result, pfn]() { (cast_result->*pfn)(); } );
3889}
3890
3891template< typename Method_Pointer >
3892typename std::enable_if<
3895 Method_Pointer>::value,
3896 state_t & >::type
3897state_t::on_exit( Method_Pointer pfn )
3898{
3899 using namespace details::event_subscription_helpers;
3900
3903
3904 // Agent must have right type.
3905 auto cast_result =
3907 typename pfn_traits::agent_type >(
3908 *m_target_agent );
3909
3910 return this->on_exit( [cast_result, pfn]() { (cast_result->*pfn)(); } );
3911}
3912
3913template< typename... Args >
3914const state_t &
3916 const mbox_t & from,
3917 Args&&... args ) const
3918{
3919 m_target_agent->so_subscribe( from ).in( *this )
3920 .event( std::forward< Args >(args)... );
3921
3922 return *this;
3923}
3924
3925/*!
3926 * \brief A shortcat for switching the agent state.
3927 *
3928 * \par Usage example.
3929 \code
3930 class my_agent : public so_5::agent_t
3931 {
3932 const so_5::state_t st_normal = so_make_state();
3933 const so_5::state_t st_error = so_make_state();
3934 ...
3935 public :
3936 virtual void so_define_agent() override
3937 {
3938 this >>= st_normal;
3939
3940 st_normal.handle( [=]( const msg_failure & evt ) {
3941 this >>= st_error;
3942 ...
3943 });
3944 ...
3945 };
3946 ...
3947 };
3948 \endcode
3949 *
3950 * \since v.5.5.1
3951 */
3952inline void
3953operator>>=( agent_t * agent, const state_t & new_state )
3954{
3955 agent->so_change_state( new_state );
3956}
3957
3958} /* namespace so_5 */
3959
3960#if defined( SO_5_MSVC )
3961 #pragma warning(pop)
3962#endif
virtual mbox_id_t id() const =0
Unique ID of this mbox.
virtual mbox_type_t type() const =0
Get the type of message box.
Interface for message sink.
A context for agent construction and tuning.
agent_context_t(environment_t &env, agent_tuning_options_t options)
agent_tuning_options_t & options()
Read-Write access to agent options.
environment_t & env() const
Access to SObjectizer Environment.
Helper class for holding agent's identity (name or pointer).
agent_identity_t(std::string_view name) noexcept
Initializing constructor for case when agent has a user specified name.
agent_identity_t(const agent_t *pointer) noexcept
Initializing constructor for case when agent has no user specified name.
Interface of the agent state listener.
A base class for agents.
Definition agent.hpp:673
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:2080
static demand_handler_pfn_t get_demand_handler_on_start_ptr() noexcept
Definition agent.cpp:1323
impl::state_listener_controller_t m_state_listener_controller
State listeners controller.
Definition agent.hpp:2803
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:2630
static void process_enveloped_msg(current_thread_id_t working_thread_id, execution_demand_t &d, const impl::event_handler_data_t *handler_data)
Actual implementation of enveloped message handling.
Definition agent.cpp:1445
std::unique_ptr< impl::sinks_storage_t > m_message_sinks
Holder of message sinks for that agent.
Definition agent.hpp:2843
const state_t st_default
Definition agent.hpp:2774
bool so_has_subscription(const mbox_t &mbox) const noexcept
Check the presence of a subscription in the default_state.
Definition agent.hpp:1856
void do_change_agent_state(const state_t &state_to_be_set)
Perform actual operations related to state switch.
Definition agent.cpp:1637
void so_drop_delivery_filter(const mbox_t &mbox) noexcept
Drop a delivery filter.
Definition agent.hpp:2531
void so_initiate_agent_definition()
A correct initiation of so_define_agent method call.
Definition agent.cpp:829
const agent_t * self_ptr() const
Get the raw pointer of itself.
Definition agent.hpp:834
so_5::current_thread_id_t m_working_thread_id
Working thread id.
Definition agent.hpp:2899
default_rw_spinlock_t m_event_queue_lock
Event queue operation protector.
Definition agent.hpp:2867
bool so_has_subscription(const mbox_t &mbox, const state_t &target_state) const noexcept
Check the presence of a subscription.
Definition agent.hpp:1811
std::enable_if< details::is_agent_method_pointer< details::method_arity::unary, Method_Pointer >::value, bool >::type so_has_subscription(const mbox_t &mbox, Method_Pointer) const noexcept
Check the presence of a subscription.
Definition agent.hpp:1956
const name_for_agent_t m_name
Optional name for the agent.
Definition agent.hpp:2948
static const impl::event_handler_data_t * find_deadletter_handler(execution_demand_t &demand)
Search for event handler between deadletter handlers.
Definition agent.cpp:1627
static demand_handler_pfn_t get_demand_handler_on_message_ptr() noexcept
Definition agent.cpp:1388
bool is_agent_deactivated() const noexcept
Is agent already deactivated.
Definition agent.cpp:1753
void so_switch_to_awaiting_deregistration_state()
Switching agent to special state in case of unhandled exception.
Definition agent.cpp:756
const state_t & so_current_state() const
Access to the current agent state.
Definition agent.hpp:934
static void process_message(current_thread_id_t working_thread_id, execution_demand_t &d, thread_safety_t thread_safety, event_handler_method_t method)
Actual implementation of message handling.
Definition agent.cpp:1412
agent_ref_t create_ref()
Make an agent reference.
Definition agent.cpp:1016
void so_drop_deadletter_handler(const so_5::mbox_t &mbox)
Drops the subscription for deadletter handler.
Definition agent.hpp:2129
bool so_is_active_state(const state_t &state_to_check) const noexcept
Is a state activated?
Definition agent.cpp:713
void ensure_operation_is_on_working_thread(const char *operation_name) const
Enables operation only if it is performed on agent's working thread.
Definition agent.cpp:1485
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:847
agent_status_t
Enumeration of possible agent statuses.
Definition agent.hpp:2785
@ defined
Agent is defined.
@ state_switch_in_progress
State switch operation is in progress.
void destroy_all_subscriptions_and_filters() noexcept
Destroy all agent's subscriptions.
Definition agent.cpp:1009
void so_create_deadletter_subscription(const mbox_t &mbox, const std::type_index &msg_type, const event_handler_method_t &method, thread_safety_t thread_safety)
Create a subscription for a deadletter handler.
Definition agent.cpp:1114
void shutdown_agent() noexcept
Agent shutdown deriver.
Definition agent.cpp:1029
void ensure_binding_finished()
Ensures that all agents from cooperation are bound to dispatchers.
Definition agent.cpp:1314
coop_t * m_agent_coop
Agent is belong to this cooperation.
Definition agent.hpp:2902
disp_binder_shptr_t so_this_agent_disp_binder() const
Returns the dispatcher binder that is used for binding this agent.
Definition agent.hpp:2671
void so_drop_subscription(const mbox_t &mbox, const state_t &target_state)
Drop subscription for the state specified.
Definition agent.hpp:1560
agent_status_t m_current_status
Current agent status.
Definition agent.hpp:2800
event_queue_t * m_event_queue
A pointer to event_queue.
Definition agent.hpp:2882
void so_set_delivery_filter(const mbox_t &mbox, Lambda &&lambda)
Set a delivery filter.
Definition agent.hpp:3410
std::enable_if< details::is_agent_method_pointer< details::method_arity::unary, Method_Pointer >::value, void >::type so_drop_subscription_for_all_states(const mbox_t &mbox, Method_Pointer)
Drop subscription for all states.
Definition agent.hpp:1716
virtual void so_define_agent()
Hook on define agent for SObjectizer.
Definition agent.cpp:841
void drop_all_delivery_filters() noexcept
Drops all delivery filters.
Definition agent.cpp:1510
disp_binder_shptr_t m_disp_binder
Binder for this agent.
Definition agent.hpp:2936
bool do_check_subscription_presence(const mbox_t &mbox, const std::type_index &msg_type, const state_t &target_state) const noexcept
Check the presence of a subscription.
Definition agent.cpp:1199
void do_state_switch(const state_t &state_to_be_set) noexcept
Actual action for switching agent state.
Definition agent.cpp:1678
std::enable_if< details::is_agent_method_pointer< details::method_arity::unary, Method_Pointer >::value, bool >::type so_has_subscription(const mbox_t &mbox, const state_t &target_state, Method_Pointer) const noexcept
Check the presence of a subscription.
Definition agent.hpp:1901
std::unique_ptr< impl::delivery_filter_storage_t > m_delivery_filters
Delivery filters for that agents.
Definition agent.hpp:2911
void so_drop_subscription_for_all_states(const mbox_t &mbox)
Drop subscription for all states.
Definition agent.hpp:1770
void return_to_default_state_if_possible() noexcept
Return agent to the default state.
Definition agent.cpp:1741
subscription_bind_t so_subscribe_self()
Initiate subscription to agent's direct mbox.
Definition agent.hpp:1416
agent_t(environment_t &env)
Constructor.
Definition agent.cpp:646
void do_set_delivery_filter(const mbox_t &mbox, const std::type_index &msg_type, delivery_filter_unique_ptr_t filter)
Set a delivery filter.
Definition agent.cpp:1520
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:1037
mbox_t so_make_new_direct_mbox()
Create a new direct mbox for that agent.
Definition agent.cpp:768
static execution_hint_t so_create_execution_hint(execution_demand_t &demand)
Create execution hint for the specified demand.
Definition agent.cpp:901
std::enable_if< details::is_agent_method_pointer< details::method_arity::unary, Method_Pointer >::value, void >::type so_drop_subscription(const mbox_t &mbox, Method_Pointer)
Drop subscription for the default agent state.
Definition agent.hpp:1611
void so_change_state(const state_t &new_state)
Change the current state of the agent.
Definition agent.cpp:811
void push_event(const message_limit::control_block_t *limit, mbox_id_t mbox_id, const std::type_index &msg_type, const message_ref_t &message)
Push event into the event queue.
Definition agent.cpp:1265
static custom_direct_mbox_factory_t custom_direct_mbox_factory(Lambda &&lambda)
Helper for creation a custom direct mbox factory.
Definition agent.hpp:1135
coop_handle_t so_coop() const
Get a handle of agent's coop.
Definition agent.cpp:860
void so_deregister_agent_coop_normally()
A helper method for deregistering agent's coop in case of normal deregistration.
Definition agent.cpp:982
void do_drop_delivery_filter(const mbox_t &mbox, const std::type_index &msg_type) noexcept
Drop a delivery filter.
Definition agent.cpp:1548
virtual exception_reaction_t so_exception_reaction() const noexcept
A reaction from SObjectizer to an exception from agent's event.
Definition agent.cpp:746
void so_deactivate_agent()
Deactivate the agent.
Definition agent.cpp:820
disp_binder_shptr_t so_this_coop_disp_binder() const
Returns the dispatcher binder that is used as the default binder for the agent's coop.
Definition agent.cpp:988
abstract_message_sink_t & detect_sink_for_message_type(const std::type_index &msg_type)
Helper function that returns a message sink to be used for subscriptions for specified message type.
Definition agent.cpp:1153
static void demand_handler_on_message(current_thread_id_t working_thread_id, execution_demand_t &d)
Calls event handler for message.
Definition agent.cpp:1371
static constexpr const state_t::history_t shallow_history
Short alias for so_5::state_t::history_t::shallow.
Definition agent.hpp:730
virtual void so_evt_finish()
Hook of agent finish in SObjectizer.
Definition agent.cpp:707
static demand_handler_pfn_t get_demand_handler_on_finish_ptr() noexcept
Definition agent.cpp:1365
void so_add_nondestroyable_listener(agent_state_listener_t &state_listener)
Add a state listener to the agent.
Definition agent.cpp:728
static const impl::event_handler_data_t * handler_finder_msg_tracing_disabled(execution_demand_t &demand, const char *context_marker)
Handler finder for the case when message delivery tracing is disabled.
Definition agent.cpp:1559
const state_t * m_current_state_ptr
Current agent state.
Definition agent.hpp:2777
const mbox_t m_direct_mbox
A direct mbox for the agent.
Definition agent.hpp:2889
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:2170
agent_t * self_ptr()
Definition agent.hpp:840
const priority_t m_priority
Priority of the agent.
Definition agent.hpp:2918
agent_identity_t so_agent_name() const noexcept
Get an optional name of the agent.
Definition agent.cpp:1000
static const impl::event_handler_data_t * handler_finder_msg_tracing_enabled(execution_demand_t &demand, const char *context_marker)
Handler finder for the case when message delivery tracing is enabled.
Definition agent.cpp:1572
void bind_to_coop(coop_t &coop)
Bind agent to the cooperation.
Definition agent.cpp:1023
static const impl::event_handler_data_t * find_event_handler_for_current_state(execution_demand_t &demand)
Actual search for event handler with respect to parent-child relationship between agent states.
Definition agent.cpp:1606
static void demand_handler_on_start(current_thread_id_t working_thread_id, execution_demand_t &d)
Calls so_evt_start method for agent.
Definition agent.cpp:1287
const mbox_t & so_direct_mbox() const
Get the agent's direct mbox.
Definition agent.cpp:762
impl::subscription_storage_unique_ptr_t m_subscriptions
All agent's subscriptions.
Definition agent.hpp:2830
void so_destroy_deadletter_subscription(const mbox_t &mbox, const std::type_index &msg_type)
Destroy a subscription for a deadletter handler.
Definition agent.cpp:1139
static void demand_handler_on_finish(current_thread_id_t working_thread_id, execution_demand_t &d)
Calls so_evt_finish method for agent.
Definition agent.cpp:1329
bool do_check_deadletter_presence(const mbox_t &mbox, const std::type_index &msg_type) const noexcept
Check the presence of a deadletter handler.
Definition agent.cpp:1209
void do_drop_subscription_for_all_states(const mbox_t &mbox, const std::type_index &msg_type)
Remove subscription for all states.
Definition agent.cpp:1184
agent_t(context_t ctx)
Constructor which simplifies agent construction with or without agent's tuning options.
Definition agent.cpp:659
static agent_tuning_options_t tuning_options()
Create tuning options object with default values.
Definition agent.hpp:1103
void so_add_destroyable_listener(agent_state_listener_unique_ptr_t state_listener)
Add a state listener to the agent.
Definition agent.cpp:737
environment_t & so_environment() const noexcept
Access to the SObjectizer Environment which this agent is belong.
Definition agent.cpp:853
std::enable_if< details::is_agent_method_pointer< details::method_arity::unary, Method_Pointer >::value, void >::type so_drop_subscription(const mbox_t &mbox, const state_t &target_state, Method_Pointer)
Drop subscription for the state specified.
Definition agent.hpp:1516
priority_t so_priority() const noexcept
Get the priority of the agent.
Definition agent.hpp:2555
const state_t & so_default_state() const
Access to the agent's default state.
Definition agent.cpp:775
subscription_bind_t so_subscribe(const mbox_t &mbox_ref)
Initiate subscription.
Definition agent.hpp:1359
virtual ~agent_t()
Definition agent.cpp:693
void so_bind_to_dispatcher(event_queue_t &queue) noexcept
Binding agent to the dispatcher.
Definition agent.cpp:872
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:1461
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:3433
environment_t & m_env
SObjectizer Environment for which the agent is belong.
Definition agent.hpp:2846
void so_drop_subscription(const mbox_t &mbox)
Drop subscription for the default agent state.
Definition agent.hpp:1655
void so_deregister_agent_coop(int dereg_reason)
A helper method for deregistering agent's coop.
Definition agent.cpp:975
void do_drop_subscription(const mbox_t &mbox, const std::type_index &msg_type, const state_t &target_state)
Remove subscription for the state specified.
Definition agent.cpp:1169
agent_t(environment_t &env, agent_tuning_options_t tuning_options)
Constructor which allows specification of agent's tuning options.
Definition agent.cpp:652
handler_finder_t m_handler_finder
Function for searching event handler.
Definition agent.hpp:2823
virtual void so_evt_start()
Hook on agent start inside SObjectizer.
Definition agent.cpp:701
static demand_handler_pfn_t get_demand_handler_on_enveloped_msg_ptr() noexcept
Definition agent.cpp:1406
void so_create_event_subscription(const mbox_t &mbox_ref, std::type_index type_index, const state_t &target_state, const event_handler_method_t &method, thread_safety_t thread_safety, event_handler_kind_t handler_kind)
Create a subscription for an event.
Definition agent.cpp:1083
void so_set_delivery_filter(const mbox_t &mbox, delivery_filter_unique_ptr_t filter)
Set a delivery filter.
Definition agent.hpp:2426
static void demand_handler_on_enveloped_msg(current_thread_id_t working_thread_id, execution_demand_t &d)
Handles the enveloped message.
Definition agent.cpp:1394
A collector for agent tuning options.
so_5::priority_t query_priority() const noexcept
Get priority value.
name_for_agent_t giveout_agent_name() noexcept
Gives away the name for an agent.
const custom_direct_mbox_factory_t & query_custom_direct_mbox_factory() const noexcept
Get a reference to custom direct mbox factory.
bool is_user_provided_subscription_storage_factory() const noexcept
Does a user provide a specific subscription_storage_factory?
const subscription_storage_factory_t & query_subscription_storage_factory() const noexcept
message_limit::description_container_t giveout_message_limits()
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
std::mutex m_lock
A lock for synchonization of some operations on coop.
Definition coop.hpp:1077
disp_binder_shptr_t coop_disp_binder() const noexcept(noexcept(disp_binder_shptr_t{ this->m_coop_disp_binder }))
Return the default dispatcher binder for the coop.
Definition coop.hpp:1316
coop_handle_t handle() noexcept
Get handle for this coop.
Definition coop.hpp:432
virtual void access_hook(access_context_t context, handler_invoker_t &invoker) noexcept=0
An implementation of handler_invoker interface.
agent_demand_handler_invoker_t(current_thread_id_t work_thread_id, execution_demand_t &demand, const so_5::impl::event_handler_data_t &handler_data)
Initializing constructor.
SObjectizer Environment.
void deregister_coop(coop_handle_t coop, int reason) noexcept
Deregister the cooperation.
An interface of event queue for agent.
virtual void push(execution_demand_t demand)=0
Enqueue new event to the queue.
virtual void push_evt_finish(execution_demand_t demand) noexcept=0
Enqueue a demand for evt_finish event.
virtual void push_evt_start(execution_demand_t demand)=0
Enqueue a demand for evt_start event.
A hint for a dispatcher for execution of event for the concrete execution_demand.
execution_hint_t(execution_demand_t &demand, direct_func_t direct_func, thread_safety_t thread_safety)
Initializing constructor.
static execution_hint_t create_empty_execution_hint(execution_demand_t &demand)
A special class for accessing private members of agent_coop.
static void decrement_usage_count(coop_t &coop)
static void increment_usage_count(coop_t &coop) noexcept
Storage for message delivery filters.
void drop_all() noexcept
Drop all defined filters.
void set_delivery_filter(const mbox_t &mbox, const std::type_index &msg_type, delivery_filter_unique_ptr_t filter, so_5::outliving_reference_t< abstract_message_sink_t > owner)
Set a delivery filter.
void drop_delivery_filter(const mbox_t &mbox, const std::type_index &msg_type) noexcept
Remove delivery filter.
A helper class for accessing the functionality of environment-class which is specific for SObjectizer...
event_queue_t * event_queue_on_bind(agent_t *agent, event_queue_t *original_queue) noexcept
Call the event_queue_hook when an agent is being bound to a particular event_queue.
subscription_storage_factory_t default_subscription_storage_factory() const noexcept(noexcept(subscription_storage_factory_t{}=subscription_storage_factory_t{}))
Get the default storage subscription factory.
void event_queue_on_unbind(agent_t *agent, event_queue_t *queue) noexcept
Call the event_queue_hook when an agent is being unbound from its event_queue.
mbox_t create_ordinary_mpsc_mbox(agent_t &single_consumer)
Create multi-producer/single-consumer mbox that handles message limits.
bool is_msg_tracing_enabled() const
Is message delivery tracing enabled?
internal_env_iface_t(environment_t &env)
Initializing constructor.
mbox_t create_limitless_mpsc_mbox(agent_t &single_consumer)
Create multi-producer/single-consumer mbox that ignores message limits.
An interface for storage of message_sinks for one agent.
virtual abstract_message_sink_t * find_or_create(const std::type_index &msg_type)=0
void add(internal_state_listener_unique_ptr_t listener)
Add a new listener.
static auto wrap_nondestroyable(agent_state_listener_t &listener)
void changed(agent_t &agent, const state_t &state) noexcept
Handle state change.
static auto wrap_destroyable(agent_state_listener_unique_ptr_t listener)
agent_t::agent_status_t m_previous_status
Definition agent.cpp:785
state_switch_guard_t(agent_t &agent)
Definition agent.cpp:788
virtual const event_handler_data_t * find_handler(mbox_id_t mbox_id, const std::type_index &msg_type, const state_t &current_state) const noexcept=0
virtual void drop_subscription(const mbox_t &mbox, const std::type_index &msg_type, const state_t &target_state) noexcept=0
virtual void drop_subscription_for_all_states(const mbox_t &mbox, const std::type_index &msg_type) noexcept=0
virtual void create_event_subscription(const mbox_t &mbox, const std::type_index &msg_type, abstract_message_sink_t &message_sink, const state_t &target_state, const event_handler_method_t &method, thread_safety_t thread_safety, event_handler_kind_t handler_kind)=0
virtual void drop_all_subscriptions() noexcept=0
Drop all subscriptions.
Template class for smart reference wrapper on the atomic_refcounted_t.
intrusive_ptr_t(T *obj) noexcept
Constructor for a raw pointer.
intrusive_ptr_t(intrusive_ptr_t &&o) noexcept
Move constructor.
T * operator->() const noexcept
intrusive_ptr_t(const intrusive_ptr_t &o) noexcept
Copy constructor.
intrusive_ptr_t & operator=(intrusive_ptr_t &&o) noexcept
Move operator.
T & operator*() const noexcept
intrusive_ptr_t() noexcept
Default constructor.
A base class for agent messages.
Definition message.hpp:47
friend message_kind_t message_kind(const message_t &what)
Helper method for quering kind of the message.
Definition message.hpp:174
A message wrapped to be used as type of argument for event handlers.
Definition mhood.hpp:570
Type for holding agent name.
unsigned int m_length
Name length.
name_for_agent_t & operator=(name_for_agent_t &&other) noexcept
Definition agent.cpp:142
name_for_agent_t(const name_for_agent_t &)
Definition agent.cpp:115
name_for_agent_t(name_for_agent_t &&other) noexcept
Definition agent.cpp:136
std::unique_ptr< char[] > m_value
Name storage.
bool has_value() const noexcept
Does this object have a value?
Definition agent.cpp:169
name_for_agent_t & operator=(const name_for_agent_t &)
Definition agent.cpp:129
std::string_view as_string_view() const
Get the value as a string_view.
Definition agent.cpp:160
name_for_agent_t()
Default constructor makes an null value.
Definition agent.cpp:104
name_for_agent_t(std::string_view value)
Initializing constructor.
Definition agent.cpp:108
Wrapper around a pointer to partially constructed agent.
Scoped guard for shared locks.
bool is_target(const agent_t *agent) const noexcept
Is agent owner of this state?
Definition agent.cpp:463
state_t & time_limit(duration_t timeout, const state_t &state_to_switch)
Set up a time limit for the state.
Definition agent.cpp:480
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:3875
bool has_subscription(const mbox_t &from, Method_Pointer &&pfn) const
Check the presence of a subscription.
Definition agent.hpp:3782
const state_t & just_switch_to(mbox_t from, const state_t &target_state) const
Define handler which only switches agent to the specified state.
Definition agent.hpp:3833
state_t & drop_time_limit()
Drop time limit for the state if defined.
Definition agent.cpp:514
on_exit_handler_t m_on_exit
Handler for the exit from the state.
Definition state.hpp:1713
const state_t & suppress(mbox_t from) const
Suppress processing of event in this state.
Definition agent.hpp:3860
history_t m_state_history
Type of state history.
Definition state.hpp:1664
const state_t * parent_state() const noexcept
Get a parent state if exists.
Definition state.hpp:1748
void fill_path(path_t &path) const noexcept
A helper method for building a path from top-level state to this state.
Definition state.hpp:1793
state_t(initial_substate_of parent, std::string state_name, history_t state_history)
Constructor for the case when state is the initial substate of some parent state.
Definition agent.cpp:338
state_t(substate_of parent)
Constructor for the case when state is a substate of some parent state.
Definition agent.cpp:358
std::size_t nested_level() const noexcept
Query nested level for the state.
Definition state.hpp:1780
bool is_active() const noexcept
Is this state or any of its substates activated?
Definition agent.hpp:3751
state_t(state_t &&other)
Move constructor.
Definition agent.cpp:381
state_t(agent_t *target_agent, std::string state_name, state_t *parent_state, std::size_t nested_level, history_t state_history)
Fully initialized constructor.
Definition agent.cpp:273
const state_t & subscribe_message_handler(const mbox_t &from, Args &&... args) const
A helper for handle-methods implementation.
Definition agent.hpp:3915
const state_t & event(Args &&... args) const
Helper for subscription of event handler in this state.
Definition agent.hpp:3758
std::enable_if< details::is_agent_method_pointer< details::method_arity::nullary, Method_Pointer >::value, state_t & >::type on_exit(Method_Pointer pfn)
Set on exit handler.
Definition agent.hpp:3897
const state_t & suppress() const
Suppress processing of event in this state.
Definition agent.hpp:3853
std::string m_state_name
State name.
Definition state.hpp:1629
state_t(agent_t *agent, std::string state_name, history_t state_history)
Definition agent.cpp:320
bool has_subscription(const mbox_t &from) const
Check the presence of a subscription.
Definition agent.hpp:3775
state_t * m_parent_state
Parent state.
Definition state.hpp:1643
state_t(substate_of parent, std::string state_name, history_t state_history)
Constructor for the case when state is a substate of some parent state.
Definition agent.cpp:369
const state_t * actual_state_to_enter() const
Find actual state to be activated for agent.
Definition agent.cpp:526
void drop_subscription(const mbox_t &from, Method_Pointer &&pfn) const
Drop subscription.
Definition agent.hpp:3801
state_t(initial_substate_of parent)
Constructor for the case when state is the initial substate of some parent state.
Definition agent.cpp:327
const state_t * m_initial_substate
The initial substate.
Definition state.hpp:1656
bool operator==(const state_t &state) const noexcept
Definition agent.cpp:403
state_t(initial_substate_of parent, std::string state_name)
Constructor for the case when state is the initial substate of some parent state.
Definition agent.cpp:332
const state_t & just_switch_to(const state_t &target_state) const
Define handler which only switches agent to the specified state.
Definition agent.hpp:3844
size_t m_substate_count
Number of substates.
Definition state.hpp:1697
state_t(agent_t *agent, history_t state_history)
Definition agent.cpp:307
void call_on_enter() const noexcept
Call for on enter handler if defined.
Definition state.hpp:1837
bool operator!=(const state_t &state) const noexcept
Definition state.hpp:372
void call_on_exit() const noexcept
Call for on exit handler if defined.
Definition state.hpp:1850
agent_t *const m_target_agent
Owner of this state.
Definition state.hpp:1623
const state_t & transfer_to_state(mbox_t from, const state_t &target_state) const
An instruction for switching agent to the specified state and transfering event proceessing to new st...
Definition agent.hpp:3813
std::string query_name() const
Get textual name of the state.
Definition agent.cpp:409
const state_t & transfer_to_state(const state_t &target_state) const
An instruction for switching agent to the specified state and transfering event proceessing to new st...
Definition agent.hpp:3824
static constexpr const std::size_t max_deep
Max deep of nested states.
Definition state.hpp:163
state_t(substate_of parent, std::string state_name)
Constructor for the case when state is a substate of some parent state.
Definition agent.cpp:363
const state_t * m_last_active_substate
Last active substate.
Definition state.hpp:1676
void handle_time_limit_on_enter() const
A special handler of time limit to be used on entering into state.
Definition agent.cpp:570
state_t(agent_t *agent, std::string state_name)
Definition agent.cpp:314
const state_t & event(mbox_t from, Args &&... args) const
Helper for subscription of event handler in this state.
Definition agent.hpp:3767
void activate() const
Switch agent to that state.
Definition agent.cpp:474
void drop_subscription(const mbox_t &from) const
Drop subscription.
Definition agent.hpp:3794
history_t
Type of history for state.
Definition state.hpp:172
@ none
State has no history.
@ deep
State has deep history.
@ shallow
State has shallow history.
on_enter_handler_t m_on_enter
Handler for the enter to the state.
Definition state.hpp:1705
std::unique_ptr< time_limit_t > m_time_limit
A definition of time limit for the state.
Definition state.hpp:1723
std::size_t m_nested_level
Nesting level for state.
Definition state.hpp:1686
void update_history_in_parent_states() const
A helper method which is used during state change for update state with history.
Definition agent.cpp:549
void handle_time_limit_on_exit() const
A special handler of time limit to be used on exiting from state.
Definition agent.cpp:576
state_t(agent_t *agent)
Definition agent.cpp:301
A class for creating a subscription to messages from the mbox.
Definition agent.hpp:174
std::enable_if< details::lambda_traits::is_lambda< Lambda >::value, subscription_bind_t & >::type event(Lambda &&lambda, thread_safety_t thread_safety=not_thread_safe)
Make subscription to the message by lambda-function.
Definition agent.hpp:3510
std::enable_if< details::is_agent_method_pointer< details::method_arity::unary, Method_Pointer >::value, subscription_bind_t & >::type event(Method_Pointer pfn, thread_safety_t thread_safety=not_thread_safe)
Make subscription to the message.
Definition agent.hpp:3490
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:3689
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:3712
subscription_bind_t(agent_t &agent, const mbox_t &mbox_ref)
Definition agent.hpp:3460
subscription_bind_t & in(const state_t &state)
Set up a state in which events are allowed be processed.
Definition agent.hpp:3469
subscription_bind_t & suppress()
Suppress processing of event in this state.
Definition agent.hpp:3665
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:3532
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:3739
agent_t * m_agent
Agent to which we are subscribing.
Definition agent.hpp:405
An indentificator for the timer.
Definition timers.hpp:73
void release() noexcept
Release the timer event.
Definition timers.hpp:99
#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:1227
mbox_t make_direct_mbox_with_respect_to_custom_factory(partially_constructed_agent_ptr_t agent_ptr, const agent_tuning_options_t &tuning_options, mbox_t standard_mbox)
Helper for creation of the direct mbox for an agent.
Definition agent.cpp:593
unsigned int ensure_valid_agent_name_length(std::size_t length)
Definition agent.cpp:83
std::string create_anonymous_state_name(const agent_t *agent, const state_t *st)
Definition agent.cpp:183
const state_t deadletter_state(nullptr, "<DEADLETTER_STATE>")
A special object to be used as state for make subscriptions for deadletter handlers.
subscription_storage_factory_t detect_subscription_storage_factory_to_use(environment_t &env, const agent_tuning_options_t &tuning_options)
Helper for selection of subscription storage factory.
Definition agent.cpp:630
const state_t awaiting_deregistration_state(nullptr, "<AWAITING_DEREGISTRATION_AFTER_UNHANDLED_EXCEPTION>")
A special object for the state in which agent is awaiting for deregistration after unhandled exceptio...
Enumeration of cooperation deregistration reasons.
Definition coop.hpp:39
const int normal
Normal deregistration.
Definition coop.hpp:46
Various helpers for event subscription.
void ensure_handler_can_be_used_with_mbox(const ::so_5::details::msg_type_and_handler_pair_t &handler, const ::so_5::mbox_t &target_mbox)
Ensure that mutability of message is compatible with mutability of target mbox.
Helper templates for detection of lambda-type traits.
Some reusable and low-level classes/functions which can be used in public header files.
void abort_on_fatal_error(L logging_lambda) noexcept
auto do_with_rollback_on_exception(Main_Action main_action, Rollback_Action rollback_action) -> decltype(main_action())
Helper function for do some action with rollback in the case of an exception.
auto invoke_noexcept_code(L lambda) noexcept -> decltype(lambda())
method_arity
A special enumeration to specify arity of lambda-function or method.
@ nullary
Method or function has no arguments.
@ unary
Method or function has just one argument.
envelope_t & message_to_envelope(const message_ref_t &src_msg)
A helper function for casting message instance to envelope instance.
access_context_t
Information about context on that enveloped message is handled.
Internal namespace with details of agent_t implementation.
Definition agent.hpp:462
Various helpers for message delivery tracing mechanism.
void safe_trace_state_leaving(const agent_t &state_owner, const state_t &state)
Helper for tracing the fact of leaving a state.
void safe_trace_state_entering(const agent_t &state_owner, const state_t &state)
Helper for tracing the fact of entering into a state.
void trace_deadletter_handler_search_result(const execution_demand_t &demand, const char *context_marker, const event_handler_data_t *search_result)
Helper for tracing the result of search of deadletter handler.
void trace_event_handler_search_result(const execution_demand_t &demand, const char *context_marker, const event_handler_data_t *search_result)
Helper for tracing the result of event handler search.
Details of SObjectizer run-time implementations.
Definition agent.cpp:780
void process_unhandled_unknown_exception(current_thread_id_t working_thread_id, agent_t &a_exception_producer) noexcept
Processor of unhandled exception of unknown type from agent's event handler.
void process_unhandled_exception(current_thread_id_t working_thread_id, const std::exception &ex, agent_t &a_exception_producer) noexcept
Processor of unhandled exception from agent's event handler.
static std::unique_ptr< sinks_storage_t > create_sinks_storage_if_necessary(partially_constructed_agent_ptr_t owner_ptr, so_5::message_limit::description_container_t &&descriptions)
Create info_storage object if there are some message limits.
All stuff related to message limits.
Definition message.hpp:862
Private part of message limit implementation.
Definition agent.cpp:33
const int rc_state_nesting_is_too_deep
Nesting of agent states is too deep.
Definition ret_code.hpp:59
const int rc_initial_substate_already_defined
Initial substate for a composite state is already defined.
Definition ret_code.hpp:66
mbox_type_t
Type of the message box.
Definition mbox.hpp:163
void operator>>=(agent_t *agent, const state_t &new_state)
A shortcat for switching the agent state.
Definition agent.hpp:3953
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
current_thread_id_t null_current_thread_id()
Get NULL thread id.
const thread_safety_t not_thread_safe
Shorthand for thread unsafety indicator.
Definition types.hpp:62
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
const int rc_operation_enabled_only_on_agent_working_thread
An attempt to perform an operation which is enabled only on agent's working thread.
Definition ret_code.hpp:44
const int rc_agent_is_not_the_state_owner
Agent doesn't own this state.
Definition ret_code.hpp:108
const int rc_agent_unknown_state
Trying to switch to the unknown state.
Definition ret_code.hpp:30
const int rc_invalid_time_limit_for_state
Invalid value of time limit for an agent's state.
Definition ret_code.hpp:539
thread_safety_t
Thread safety indicator.
Definition types.hpp:50
@ 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:3400
@ user_type_message
Message is an user type message.
@ enveloped_msg
Message is an envelope with some other message inside.
const int rc_empty_agent_name
Name for an agent can't be empty.
Definition ret_code.hpp:512
const int rc_agent_deactivated
Agent can't change state because the agent is already deactivated.
Definition ret_code.hpp:432
SO_5_FUNC void swap(name_for_agent_t &a, name_for_agent_t &b) noexcept
Definition agent.cpp:150
timer_id_t send_periodic(Target &&target, std::chrono::steady_clock::duration pause, std::chrono::steady_clock::duration period, Args &&... args)
A utility function for creating and delivering a periodic message to the specified destination.
const int rc_another_state_switch_in_progress
An attempt to switch agent state when another switch operation is in progress.
Definition ret_code.hpp:216
const int rc_no_initial_substate
An attempt to change agent state to a new composite state which have no initial state defined.
Definition ret_code.hpp:52
const int rc_agent_has_no_cooperation
Agent is not bound to a cooperation.
Definition ret_code.hpp:33
event_handler_kind_t
Kind of an event handler.
Definition types.hpp:154
current_thread_id_t query_current_thread_id()
Get the ID of the current thread.
const int rc_message_has_no_limit_defined
An attempt to create subscription to message without predefined limit for that message type.
Definition ret_code.hpp:123
outliving_reference_t< T > outliving_mutable(T &r)
Make outliving_reference wrapper for mutable reference.
const int rc_mpsc_mbox_expected
An instance of MPSC mbox is expected as custom direct mbox.
Definition ret_code.hpp:443
Type for case when agent has no user-provided name.
SO_5_FUNC std::array< char, c_string_size > make_c_string() const noexcept
Make a c-string with text representation of a value.
Definition agent.cpp:39
static constexpr std::string_view c_string_prefix
Prefix to be used for string representation.
static constexpr std::size_t c_string_size
static constexpr std::string_view c_string_suffix
Suffix to be used for string representation.
Check whether T is a non-static member function pointer.
A detector that type is a lambda or functional object.
A description of event execution demand.
mbox_id_t m_mbox_id
ID of mbox.
void call_handler(current_thread_id_t thread_id)
Helper method to simplify demand execution.
demand_handler_pfn_t m_demand_handler
Demand handler.
agent_t * m_receiver
Receiver of demand.
message_ref_t m_message_ref
Event incident.
const message_limit::control_block_t * m_limit
Optional message limit for that message.
execution_demand_t(agent_t *receiver, const message_limit::control_block_t *limit, mbox_id_t mbox_id, std::type_index msg_type, message_ref_t message_ref, demand_handler_pfn_t demand_handler) noexcept
std::type_index m_msg_type
Type of the message.
A helper class for temporary setting and then dropping the ID of the current working thread.
Definition agent.hpp:474
working_thread_id_sentinel_t(so_5::current_thread_id_t &id_var, so_5::current_thread_id_t value_to_set)
Definition agent.hpp:477
Information about event_handler and its properties.
thread_safety_t m_thread_safety
Is event handler thread safe or not.
event_handler_method_t m_method
Method for handling event.
event_handler_kind_t m_kind
Kind of this event handler.
Helper for marking initial substate of composite state.
Definition state.hpp:57
A control block for one message limit.
Definition message.hpp:976
static const control_block_t * none()
A special indicator about absence of control_block.
Definition message.hpp:1023
static void decrement(const control_block_t *limit)
Definition message.hpp:1028
A mixin with message limit definition methods.
A helper class for detection of payload type of message.
Definition message.hpp:783
Helper type with method to be mixed into agent class.
time_limit_t(duration_t limit, const state_t &state_to_switch)
Definition agent.cpp:207
void set_up_limit_for_agent(agent_t &agent, const state_t &current_state) noexcept
Definition agent.cpp:215
const state_t & m_state_to_switch
Definition agent.cpp:202
void drop_limit_for_agent(agent_t &agent, const state_t &current_state) noexcept
Definition agent.cpp:247
Helper for marking a substate of composite state.
Definition state.hpp:89
state_t * m_parent_state
Definition state.hpp:90