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/abort_on_fatal_error.hpp>
25 #include <so_5/details/at_scope_exit.hpp>
26 
27 #include <so_5/fwd.hpp>
28 
29 #include <so_5/agent_ref_fwd.hpp>
30 #include <so_5/agent_context.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 <atomic>
40 #include <map>
41 #include <memory>
42 #include <vector>
43 #include <utility>
44 #include <type_traits>
45 
46 #if defined( SO_5_MSVC )
47 	#pragma warning(push)
48 	#pragma warning(disable: 4251)
49 #endif
50 
51 namespace so_5
52 {
53 
54 //
55 // exception_reaction_t
56 //
57 /*!
58  * \since
59  * v.5.2.3
60  *
61  * \brief A reaction of SObjectizer to an exception from agent event.
62  */
63 enum exception_reaction_t
64 {
65 	//! Execution of application must be aborted immediatelly.
66 	abort_on_exception = 1,
67 	//! Agent must be switched to special state and SObjectizer
68 	//! Environment will be stopped.
69 	shutdown_sobjectizer_on_exception = 2,
70 	//! Agent must be switched to special state and agent's cooperation
71 	//! must be deregistered.
72 	deregister_coop_on_exception = 3,
73 	//! Exception should be ignored and agent should continue its work.
74 	ignore_exception = 4,
75 	/*!
76 	 * \since
77 	 * v.5.3.0
78 	 *
79 	 * \brief Exception reaction should be inherited from SO Environment.
80 	 */
81 	inherit_exception_reaction = 5
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  */
173 class subscription_bind_t
174 {
175 	public:
176 		inline
177 		subscription_bind_t(
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 		 * \since
192 		 * v.5.5.14
193 		 *
194 		 *
195 		 * \note Can be used for message and signal handlers.
196 		 *
197 		 * \par Usage example
198 		 * \code
199 			struct engine_control : public so_5::message_t { ... };
200 			struct check_status : public so_5::signal_t {};
201 			class engine_controller : public so_5::agent_t
202 			{
203 			public :
204 				virtual void so_define_agent() override
205 				{
206 					so_subscribe_self()
207 							.event( &engine_controller::control )
208 							.event( &engine_controller::check_status );
209 							.event( &engine_controller::accelerate );
210 					...
211 				}
212 				...
213 			private :
214 				void control( so_5::mhood_t< engine_control > & cmd ) { ... }
215 				void check_status( so_5::mhood_t< check_status > & cmd ) { ... }
216 				void accelerate( so_5::mhood_t< int > & cmd ) { ... }
217 			};
218 		 * \endcode
219 		 */
220 		template< typename Method_Pointer >
221 		typename std::enable_if<
222 				details::is_agent_method_pointer<
223 						details::method_arity::unary,
224 						Method_Pointer>::value,
225 				subscription_bind_t & >::type
226 		event(
227 			//! Event handling method.
228 			Method_Pointer pfn,
229 			//! Thread safety of the event handler.
230 			thread_safety_t thread_safety = not_thread_safe );
231 
232 		/*!
233 		 * \since
234 		 * v.5.3.0
235 		 *
236 		 * \brief Make subscription to the message by lambda-function.
237 		 *
238 		 * \attention Only lambda-function in the forms:
239 		 * \code
240 			Result (const Message &)
241 			Result (Message)
242 			Result (so_5::mhood_t<Message>)
243 			Result (const so_5::mhood_t<Message> &)
244 		 * \endcode
245 		 * are supported.
246 		 *
247 		 * \par Usage example.
248 		 * \code
249 			enum class engine_control { turn_on, turn_off, slow_down };
250 			struct setup_params : public so_5::message_t { ... };
251 			struct update_settings { ... };
252 
253 			class engine_controller : public so_5::agent_t
254 			{
255 			public :
256 				virtual void so_define_agent() override
257 				{
258 					so_subscribe_self()
259 						.event( [this]( engine_control evt ) {...} )
260 						.event( [this]( const setup_params & evt ) {...} )
261 						.event( [this]( const update_settings & evt ) {...} )
262 					...
263 				}
264 				...
265 			};
266 		 * \endcode
267 		 */
268 		template< class Lambda >
269 		typename std::enable_if<
270 				details::lambda_traits::is_lambda<Lambda>::value,
271 				subscription_bind_t & >::type
272 		event(
273 			//! Event handler code.
274 			Lambda && lambda,
275 			//! Thread safety of the event handler.
276 			thread_safety_t thread_safety = not_thread_safe );
277 
278 		/*!
279 		 * \since
280 		 * v.5.5.15
281 		 *
282 		 * \brief An instruction for switching agent to the specified
283 		 * state and transfering event proceessing to new state.
284 		 *
285 		 * \par Usage example:
286 		 * \code
287 			class device : public so_5::agent_t {
288 				state_t off{ this, "off" };
289 				state_t on{ this, "on" };
290 			public :
291 				virtual void so_define_agent() override {
292 					so_subscribe_self().in( off )
293 						.transfer_to_state< key_on >( on )
294 						.transfer_to_state< key_info >( on );
295 				}
296 				...
297 			};
298 		 * \endcode
299 		 *
300 		 * \note Event will not be postponed or returned to event queue.
301 		 * A search for a handler for this event will be performed immediately
302 		 * after switching to the new state.
303 		 *
304 		 * \note New state can use transfer_to_state for that event too:
305 		 * \code
306 			class device : public so_5::agent_t {
307 				state_t off{ this, "off" };
308 				state_t on{ this, "on" };
309 				state_t status_dialog{ this, "status" };
310 			public :
311 				virtual void so_define_agent() override {
312 					so_subscribe_self().in( off )
313 						.transfer_to_state< key_on >( on )
314 						.transfer_to_state< key_info >( on );
315 
316 					so_subscribe_self().in( on )
317 						.transfer_to_state< key_info >( status_dialog )
318 						...;
319 				}
320 				...
321 			};
322 		 * \endcode
323 		 *
324 		 * \note Since v.5.5.22.1 actual execution of transfer_to_state operation
325 		 * can raise so_5::exception_t with so_5::rc_transfer_to_state_loop
326 		 * error code if a loop in transfer_to_state is detected.
327 		 */
328 		template< typename Msg >
329 		subscription_bind_t &
330 		transfer_to_state(
331 			const state_t & target_state );
332 
333 		/*!
334 		 * \since
335 		 * v.5.5.15
336 		 *
337 		 * \brief Suppress processing of event in this state.
338 		 *
339 		 * \note This method is useful because the event is not passed to
340 		 * event handlers from parent states. For example:
341 		 * \code
342 			class demo : public so_5::agent_t
343 			{
344 				state_t S1{ this, "1" };
345 				state_t S2{ initial_substate_of{ S1 }, "2" };
346 				state_t S3{ initial_substate_of{ S2 }, "3" };
347 			public :
348 				virtual void so_define_agent() override
349 				{
350 					so_subscribe_self().in( S1 )
351 						// Default event handler which will be inherited by states S2 and S3.
352 						.event< msg1 >(...)
353 						.event< msg2 >(...)
354 						.event< msg3 >(...);
355 
356 					so_subscribe_self().in( S2 )
357 						// A special handler for msg1.
358 						// For msg2 and msg3 event handlers from state S1 will be used.
359 						.event< msg1 >(...);
360 
361 					so_subscribe_self().in( S3 )
362 						// Message msg1 will be suppressed. It will be simply ignored.
363 						// No events from states S1 and S2 will be called.
364 						.suppress< msg1 >()
365 						// The same for msg2.
366 						.suppress< msg2 >()
367 						// A special handler for msg3. Overrides handler from state S1.
368 						.event< msg3 >(...);
369 				}
370 			};
371 		 * \endcode
372 		 */
373 		template< typename Msg >
374 		subscription_bind_t &
375 		suppress();
376 
377 		/*!
378 		 * \since
379 		 * v.5.5.15
380 		 *
381 		 * \brief Define handler which only switches agent to the specified
382 		 * state.
383 		 *
384 		 * \note This method differes from transfer_to_state() method:
385 		 * just_switch_to() changes state of the agent, but there will not be a
386 		 * look for event handler for message/signal in the new state.  It means
387 		 * that just_switch_to() is just a shorthand for:
388 		 * \code
389 			virtual void demo::so_define_agent() override
390 			{
391 				so_subscribe_self().in( S1 )
392 					.event< some_signal >( [this]{ this >>= S2; } );
393 			}
394 		 * \endcode
395 		 * With just_switch_to() this code can looks like:
396 		 * \code
397 			virtual void demo::so_define_agent() override
398 			{
399 				so_subscribe_self().in( S1 )
400 					.just_switch_to< some_signal >( S2 );
401 			}
402 		 * \endcode
403 		 */
404 		template< typename Msg >
405 		subscription_bind_t &
406 		just_switch_to(
407 			const state_t & target_state );
408 
409 	private:
410 		//! Agent to which we are subscribing.
411 		agent_t * m_agent;
412 		//! Mbox for messages to subscribe.
413 		mbox_t m_mbox_ref;
414 
415 		/*!
416 		 * \since
417 		 * v.5.3.0
418 		 *
419 		 * \brief Type of vector of states.
420 		 */
421 		typedef std::vector< const state_t * > state_vector_t;
422 
423 		/*!
424 		 * \since
425 		 * v.5.3.0
426 		 *
427 		 * \brief States of agents the event to be subscribed in.
428 		 */
429 		state_vector_t m_states;
430 
431 		/*!
432 		 * \since
433 		 * v.5.3.0
434 		 *
435 		 * \brief Create subscription of event for all states.
436 		 */
437 		void
438 		create_subscription_for_states(
439 			const std::type_index & msg_type,
440 			const event_handler_method_t & method,
441 			thread_safety_t thread_safety,
442 			event_handler_kind_t handler_kind ) const;
443 
444 		/*!
445 		 * \brief Additional check for subscription to a mutable message
446 		 * from MPMC mbox.
447 		 *
448 		 * Such attempt must be disabled because delivery of mutable
449 		 * messages via MPMC mboxes is prohibited.
450 		 *
451 		 * \throw so_5::exception_t if m_mbox_ref is a MPMC mbox and
452 		 * \a handler is for mutable message.
453 		 *
454 		 * \since
455 		 * v.5.5.19
456 		 */
457 		void
458 		ensure_handler_can_be_used_with_mbox(
459 			const so_5::details::msg_type_and_handler_pair_t & handler ) const;
460 };
461 
462 //
463 // agent_t
464 //
465 
466 //! A base class for agents.
467 /*!
468 	An agent in SObjctizer must be derived from the agent_t.
469 
470 	The base class provides various methods whose can be splitted into
471 	the following groups:
472 	\li methods for the interaction with SObjectizer;
473 	\li predefined hook-methods which are called during: cooperation
474 	registration, starting and stopping of an agent;
475 	\li methods for the message subscription and unsubscription;
476 	\li methods for working with an agent state;
477 
478 	<b>Methods for the interaction with SObjectizer</b>
479 
480 	Method so_5::agent_t::so_environment() serves for the access to the
481 	SObjectizer Environment (and, therefore, to all methods of the
482 	SObjectizer Environment).
483 	This method could be called immediatelly after the agent creation.
484 	This is because agent is bound to the SObjectizer Environment during
485 	the creation process.
486 
487 	<b>Hook methods</b>
488 
489 	The base class defines several hook-methods. Its default implementation
490 	do nothing.
491 
492 	The method agent_t::so_define_agent() is called just before agent will
493 	started by SObjectizer as a part of the agent registration process.
494 	It should be reimplemented for the initial subscription of the agent
495 	to messages.
496 
497 	There are two hook-methods related to important agent's lifetime events:
498 	agent_t::so_evt_start() and agent_t::so_evt_finish(). They are called
499 	by SObjectizer in next circumstances:
500 	- method so_evt_start() is called when the agent is starting its work
501 	  inside of SObjectizer. At that moment all agents are defined (all
502 	  their methods agent_t::so_define_agent() have executed);
503 	- method so_evt_finish() is called during the agent's cooperation
504 	  deregistration just after agent processed the last pending event.
505 
506 	Methods so_evt_start() and so_evt_finish() are called by SObjectizer and
507 	user can just reimplement them to implement the agent-specific logic.
508 
509 	<b>Message subscription and unsubscription methods</b>
510 
511 	Any method with one of the following prototypes can be used as an event
512 	handler:
513 	\code
514 		return_type evt_handler( mhood_t< Message > msg );
515 		return_type evt_handler( const mhood_t< Message > & msg );
516 		return_type evt_handler( const Message & msg );
517 		return_type evt_handler( Message msg );
518 		// Since v.5.5.20:
519 		return_type evt_handler( mhood_t< Message > msg ) const;
520 		return_type evt_handler( const mhood_t< Message > & msg ) const;
521 		return_type evt_handler( const Message & msg ) const;
522 		return_type evt_handler( Message msg ) const;
523 	\endcode
524 	Where \c evt_handler is a name of the event handler, \c Message is a
525 	message type.
526 
527 	The class mhood_t is a wrapper on pointer to an instance
528 	of the \c Message. It is very similar to <tt>std::unique_ptr</tt>.
529 	The pointer to \c Message can be a nullptr. It happens in case when
530 	the message has no actual data and servers just a signal about something.
531 
532 	Please note that handlers with the following prototypes can be used
533 	only for messages, not signals:
534 	\code
535 		return_type evt_handler( const Message & msg );
536 		return_type evt_handler( Message msg );
537 		// Since v.5.5.20:
538 		return_type evt_handler( const Message & msg ) const;
539 		return_type evt_handler( Message msg ) const;
540 	\endcode
541 
542 	A subscription to the message is performed by the methods so_subscribe()
543 	and so_subscribe_self().
544 	This method returns an instance of the so_5::subscription_bind_t which
545 	does all actual actions of the subscription process. This instance already
546 	knows agents and message mbox and uses the default agent state for
547 	the event subscription (binding to different state is also possible).
548 
549 	The presence of a subscription can be checked by so_has_subscription()
550 	method.
551 
552 	A subscription can be dropped (removed) by so_drop_subscription() and
553 	so_drop_subscription_for_all_states() methods.
554 
555 	<b>Deadletter handlers subscription and unsubscription</b>
556 
557 	Since v.5.5.21 SObjectizer supports deadletter handlers. Such handlers
558 	are called if there is no any ordinary event handler for a specific
559 	messages from a specific mbox.
560 
561 	Deadletter handler can be implemented by an agent method or by lambda
562 	function. Deadletter handler can have one of the following formats:
563 	\code
564 		void evt_handler( mhood_t< Message > msg );
565 		void return_type evt_handler( mhood_t< Message > msg ) const;
566 		void return_type evt_handler( const mhood_t< Message > & msg );
567 		void return_type evt_handler( const mhood_t< Message > & msg ) const;
568 		void return_type evt_handler( const Message & msg );
569 		void return_type evt_handler( const Message & msg ) const;
570 		void return_type evt_handler( Message msg );
571 		void return_type evt_handler( Message msg ) const;
572 	\endcode
573 
574 	Subscription for a deadletter handler can be created by
575 	so_subscribe_deadletter_handler() method.
576 
577 	The presence of a deadletter handler can be checked by
578 	so_has_deadletter_handler() method.
579 
580 	A deadletter can be dropped (removed) by so_drop_deadletter_handler()
581 	and so_drop_subscription_for_all_states() methods.
582 
583 	<b>Methods for working with an agent state</b>
584 
585 	The agent can change its state by his so_change_state() method.
586 
587 	An attempt to switch an agent to the state which belongs to the another
588 	agent is an error. If state is belong to the same agent there are
589 	no possibility to any run-time errors. In this case changing agent
590 	state is a very safe operation.
591 
592 	In some cases it is necessary to detect agent state switching.
593 	For example for application monitoring purposes. This can be done
594 	by "state listeners".
595 
596 	Any count of state listeners can be set for an agent. There are
597 	two methods for that:
598 	- so_add_nondestroyable_listener() is for listeners whose lifetime
599 	  are controlled by a programmer, not by SObjectizer;
600 	- so_add_destroyable_listener() is for listeners whose lifetime
601 	  must be controlled by agent itself.
602 
603 	<b>Work thread identification</b>
604 
605 	Since v.5.4.0 some operations for agent are enabled only on agent's
606 	work thread. They are:
607 	- subscription management operations (creation or dropping);
608 	- changing agent's state.
609 
610 	Work thread for an agent is defined as follows:
611 	- before invocation of so_define_agent() the work thread is a
612 	  thread on which agent is created (id of that thread is detected in
613 	  agent's constructor);
614 	- during cooperation registration the working thread is a thread on
615 	  which so_environment::register_coop() is working;
616 	- after successful agent registration the work thread for it is
617 	  specified by the dispatcher.
618 
619 	\note Some dispatchers could provide several work threads for
620 	an agent. In such case there would not be work thread id. And
621 	operations like changing agent state or creation of subscription
622 	would be prohibited after agent registration.
623 */
624 class SO_5_TYPE agent_t
625 	:	private atomic_refcounted_t
626 	,	public message_limit::message_limit_methods_mixin_t
627 {
628 		friend class subscription_bind_t;
629 		friend class state_t;
630 
631 		friend class so_5::impl::mpsc_mbox_t;
632 		friend class so_5::impl::state_switch_guard_t;
633 		friend class so_5::impl::internal_agent_iface_t;
634 
635 		friend class so_5::enveloped_msg::impl::agent_demand_handler_invoker_t;
636 
637 		template< typename T >
638 		friend class intrusive_ptr_t;
639 
640 	public:
641 		/*!
642 		 * \since
643 		 * v.5.5.4
644 		 *
645 		 * \brief Short alias for agent_context.
646 		 */
647 		using context_t = so_5::agent_context_t;
648 		/*!
649 		 * \since
650 		 * v.5.5.13
651 		 *
652 		 * \brief Short alias for %so_5::state_t.
653 		 */
654 		using state_t = so_5::state_t;
655 		/*!
656 		 * \since
657 		 * v.5.5.14
658 		 *
659 		 * \brief Short alias for %so_5::mhood_t.
660 		 */
661 		template< typename T >
662 		using mhood_t = so_5::mhood_t< T >;
663 		/*!
664 		 * \since
665 		 * v.5.5.19
666 		 *
667 		 * \brief Short alias for %so_5::mutable_mhood_t.
668 		 */
669 		template< typename T >
670 		using mutable_mhood_t = so_5::mutable_mhood_t< T >;
671 		/*!
672 		 * \since
673 		 * v.5.5.15
674 		 *
675 		 * \brief Short alias for %so_5::initial_substate_of.
676 		 */
677 		using initial_substate_of = so_5::initial_substate_of;
678 		/*!
679 		 * \since
680 		 * v.5.5.15
681 		 *
682 		 * \brief Short alias for %so_5::substate_of.
683 		 */
684 		using substate_of = so_5::substate_of;
685 		/*!
686 		 * \since
687 		 * v.5.5.15
688 		 *
689 		 * \brief Short alias for %so_5::state_t::history_t::shallow.
690 		 */
691 		static constexpr const state_t::history_t shallow_history =
692 				state_t::history_t::shallow;
693 		/*!
694 		 * \since
695 		 * v.5.5.15
696 		 *
697 		 * \brief Short alias for %so_5::state_t::history_t::deep.
698 		 */
699 		static constexpr const state_t::history_t deep_history =
700 				state_t::history_t::deep;
701 
702 		//! Constructor.
703 		/*!
704 			Agent must be bound to the SObjectizer Environment during
705 			its creation. And that binding cannot be changed anymore.
706 		*/
707 		explicit agent_t(
708 			//! The Environment for this agent must exist.
709 			environment_t & env );
710 
711 		/*!
712 		 * \since
713 		 * v.5.5.3
714 		 *
715 		 * \brief Constructor which allows specification of
716 		 * agent's tuning options.
717 		 *
718 		 * \par Usage sample:
719 		 \code
720 		 using namespace so_5;
721 		 class my_agent : public agent_t
722 		 {
723 		 public :
724 		 	my_agent( environment_t & env )
725 				:	agent_t( env, agent_t::tuning_options()
726 						.subscription_storage_factory(
727 								vector_based_subscription_storage_factory() ) )
728 				{...}
729 		 }
730 		 \endcode
731 		 */
732 		agent_t(
733 			environment_t & env,
734 			agent_tuning_options_t tuning_options );
735 
736 		/*!
737 		 * \since
738 		 * v.5.5.4
739 		 *
740 		 * \brief Constructor which simplifies agent construction with
741 		 * or without agent's tuning options.
742 		 *
743 		 * \par Usage sample:
744 		 * \code
745 		 class my_agent : public so_5::agent_t
746 		 {
747 		 public :
748 		 	my_agent( context_t ctx )
749 				:	so_5::agent( ctx + limit_then_drop< get_status >(1) )
750 				{}
751 			...
752 		 };
753 		 class my_more_specific_agent : public my_agent
754 		 {
755 		 public :
756 		 	my_more_specific_agent( context_t ctx )
757 				:	my_agent( ctx + limit_then_drop< reconfigure >(1) )
758 				{}
759 		 };
760 
761 		 // Then somewhere in the code:
762 		 auto coop = env.make_coop();
763 		 auto a = coop->make_agent< my_agent >();
764 		 auto b = coop->make_agent< my_more_specific_agent >();
765 		 * \endcode
766 		 */
767 		agent_t( context_t ctx );
768 
769 		virtual ~agent_t();
770 
771 		//! Get the raw pointer of itself.
772 		/*!
773 			This method is intended for use in the member initialization
774 			list instead 'this' to suppres compiler warnings.
775 			For example for an agent state initialization:
776 			\code
777 			class a_sample_t : public so_5::agent_t
778 			{
779 					typedef so_5::agent_t base_type_t;
780 
781 					// Agent state.
782 					const so_5::state_t m_sample_state;
783 				public:
784 					a_sample_t( so_5::environment_t & env )
785 						:
786 							base_type_t( env ),
787 							m_sample_state( self_ptr() )
788 					{
789 						// ...
790 					}
791 
792 				// ...
793 
794 			};
795 			\endcode
796 		*/
797 		inline const agent_t *
self_ptr() const798 		self_ptr() const
799 		{
800 			return this;
801 		}
802 
803 		inline agent_t *
self_ptr()804 		self_ptr()
805 		{
806 			return this;
807 		}
808 
809 		//! Hook on agent start inside SObjectizer.
810 		/*!
811 			It is guaranteed that this method will be called first
812 			just after end of the cooperation registration process.
813 
814 			During cooperation registration agent is bound to some
815 			working thread. And the first method which is called for
816 			the agent on that working thread context is this method.
817 
818 			\code
819 			class a_sample_t : public so_5::agent_t
820 			{
821 				// ...
822 				virtual void
823 				so_evt_start();
824 				// ...
825 			};
826 
827 			a_sample_t::so_evt_start()
828 			{
829 				std::cout << "first agent action on bound dispatcher" << std::endl;
830 				... // Some application logic actions.
831 			}
832 			\endcode
833 		*/
834 		virtual void
835 		so_evt_start();
836 
837 		//! Hook of agent finish in SObjectizer.
838 		/*!
839 			It is guaranteed that this method will be called last
840 			just before deattaching agent from it's working thread.
841 
842 			This method should be used to perform some cleanup
843 			actions on it's working thread.
844 			\code
845 			class a_sample_t : public so_5::agent_t
846 			{
847 				// ...
848 				virtual void
849 				so_evt_finish();
850 				// ...
851 			};
852 
853 			a_sample_t::so_evt_finish()
854 			{
855 				std::cout << "last agent activity";
856 
857 				if( so_current_state() == m_db_error_happened )
858 				{
859 					// Delete the DB connection on the same thread where
860 					// connection was established and where some
861 					// error happened.
862 					m_db.release();
863 				}
864 			}
865 			\endcode
866 		*/
867 		virtual void
868 		so_evt_finish();
869 
870 		//! Access to the current agent state.
871 		/*!
872 		 * \note
873 		 * There is a change in behaviour of this methon in v.5.5.22.
874 		 * If some on_enter/on_exit handler calls this method during
875 		 * the state change procedure this method will return the state
876 		 * for which this on_enter/on_exit handler is called. For example:
877 		 * \code
878 		 * class demo final : public so_5::agent_t {
879 		 * 	state_t st_1{ this };
880 		 * 	state_t st_1_1{ initial_substate_of{st_1} };
881 		 * 	state_t st_1_2{ substate_of{st_1}};
882 		 * 	...
883 		 * 	virtual void so_define_agent() override {
884 		 * 		st_1.on_enter([this]{
885 		 * 			assert(st_1 == so_current_state());
886 		 * 			...
887 		 * 		});
888 		 * 		st_1_1.on_enter([this]{
889 		 * 			assert(st_1_1 == so_current_state());
890 		 * 			...
891 		 * 		});
892 		 * 		...
893 		 * 	}
894 		 * };
895 		 * \endcode
896 		 */
897 		inline const state_t &
so_current_state() const898 		so_current_state() const
899 		{
900 			return *m_current_state_ptr;
901 		}
902 
903 		/*!
904 		 * \since
905 		 * v.5.5.15
906 		 *
907 		 * \brief Is a state activated?
908 		 *
909 		 * \note Since v.5.5.15 a state can have substates. For example
910 		 * state A can have substates B and C. If B is the current state
911 		 * then so_current_state() will return a reference to B. But
912 		 * state A is active too because it is a superstate for B.
913 		 * Method so_is_active_state(A) will return \a true in that case:
914 		 * \code
915 			class demo : public so_5::agent_t
916 			{
917 				state_t A{ this, "A" };
918 				state_t B{ initial_substate_of{ A }, "B" };
919 				state_t C{ substate_of{ A }, "C" };
920 				...
921 				void some_event()
922 				{
923 					this >>= C;
924 
925 					assert( C == so_current_state() );
926 					assert( !( A == so_current_state() ) );
927 					assert( so_is_active_state(A) );
928 					...
929 				}
930 			};
931 		 * \endcode
932 		 *
933 		 * \attention This method is not thread safe. Be careful calling
934 		 * this method from outside of agent's working thread.
935 		 *
936 		 * \return \a true if state \a state_to_check is the current state
937 		 * or if the current state is a substate of \a state_to_check.
938 		 *
939 		 */
940 		bool
941 		so_is_active_state( const state_t & state_to_check ) const noexcept;
942 
943 		//! Add a state listener to the agent.
944 		/*!
945 		 * A programmer should guarantee that the lifetime of
946 		 * \a state_listener is exceeds lifetime of the agent.
947 		 */
948 		void
949 		so_add_nondestroyable_listener(
950 			agent_state_listener_t & state_listener );
951 
952 		//! Add a state listener to the agent.
953 		/*!
954 		 * Agent takes care of the \a state_listener destruction.
955 		 */
956 		void
957 		so_add_destroyable_listener(
958 			agent_state_listener_unique_ptr_t state_listener );
959 
960 		/*!
961 		 * \since
962 		 * v.5.2.3
963 		 *
964 		 * \brief A reaction from SObjectizer to an exception from
965 		 * agent's event.
966 		 *
967 		 * If an exception is going out from agent's event it will be
968 		 * caught by SObjectizer. Then SObjectizer will call this method
969 		 * and perform some actions in dependence of return value.
970 		 *
971 		 * \note Since v.5.3.0 default implementation calls
972 		 * coop_t::exception_reaction() for agent's cooperation
973 		 * object.
974 		 */
975 		virtual exception_reaction_t
976 		so_exception_reaction() const;
977 
978 		/*!
979 		 * \since 5.2.3
980 		 * \brief Switching agent to special state in case of unhandled
981 		 * exception.
982 		 */
983 		void
984 		so_switch_to_awaiting_deregistration_state();
985 
986 		//! Push an event to the agent's event queue.
987 		/*!
988 			This method is used by SObjectizer for the
989 			agent's event scheduling.
990 		*/
991 		static inline void
call_push_event(agent_t & agent,const message_limit::control_block_t * limit,mbox_id_t mbox_id,std::type_index msg_type,const message_ref_t & message)992 		call_push_event(
993 			agent_t & agent,
994 			const message_limit::control_block_t * limit,
995 			mbox_id_t mbox_id,
996 			std::type_index msg_type,
997 			const message_ref_t & message )
998 		{
999 			agent.push_event( limit, mbox_id, msg_type, message );
1000 		}
1001 
1002 		/*!
1003 		 * \since
1004 		 * v.5.4.0
1005 		 *
1006 		 * \brief Get the agent's direct mbox.
1007 		 */
1008 		const mbox_t &
1009 		so_direct_mbox() const;
1010 
1011 		/*!
1012 		 * \brief Create a new direct mbox for that agent.
1013 		 *
1014 		 * This method creates a new MPSC mbox which is connected
1015 		 * with that agent. Only agent for that so_make_new_direct_mbox()
1016 		 * has been called can make subscriptions for a new mbox.
1017 		 *
1018 		 * Note. The new mbox doesn't replaces the standard direct mbox
1019 		 * for the agent. Old direct mbox is still here and can still be used
1020 		 * for sending messages to the agent. But new mbox is not related
1021 		 * to the old direct mbox: they are different mboxes and can be used
1022 		 * for different subscriptions.
1023 		 * For example:
1024 		 * \code
1025 		 * class my_agent final : public so_5::agent_t {
1026 		 * 	...
1027 		 * 	void so_evt_start() override {
1028 		 * 		so_subscribe_self().event( [](mhood_t<hello>) {
1029 		 * 			std::cout << "hello from the direct mbox" << std::endl;
1030 		 * 		} );
1031 		 *
1032 		 * 		const new_mbox = so_make_new_direct_mbox();
1033 		 * 		so_subscribe( new_mbox ).event( [](mhood_t<hello) {
1034 		 * 			std::cout << "hello from a new mbox" << std::endl;
1035 		 * 		}
1036 		 *
1037 		 * 		so_5::send<hello>(*this);
1038 		 * 		so_5::send<hello>(new_mbox);
1039 		 * 	}
1040 		 * };
1041 		 * \endcode
1042 		 * The output will be:
1043 		 \verbatim
1044 		 hello from the direct mbox
1045 		 hello from a new mbox
1046 		 \endverbatim
1047 		 *
1048 		 * \since
1049 		 * v.5.6.0
1050 		 */
1051 		mbox_t
1052 		so_make_new_direct_mbox();
1053 
1054 		/*!
1055 		 * \since
1056 		 * v.5.5.3
1057 		 *
1058 		 * \brief Create tuning options object with default values.
1059 		 */
1060 		inline static agent_tuning_options_t
tuning_options()1061 		tuning_options()
1062 		{
1063 			return agent_tuning_options_t();
1064 		}
1065 
1066 	protected:
1067 		/*!
1068 		 * \name Methods for working with the agent state.
1069 		 * \{
1070 		 */
1071 
1072 		//! Access to the agent's default state.
1073 		const state_t &
1074 		so_default_state() const;
1075 
1076 	public : /* Note: since v.5.5.1 method so_change_state() is public */
1077 
1078 		//! Method changes state.
1079 		/*!
1080 			Usage sample:
1081 			\code
1082 			void a_sample_t::evt_smth( mhood_t< message_one_t > msg )
1083 			{
1084 				// If something wrong with the message then we should
1085 				// switch to the error_state.
1086 				if( error_in_data( *msg ) )
1087 					so_change_state( m_error_state );
1088 			}
1089 			\endcode
1090 		*/
1091 		void
1092 		so_change_state(
1093 			//! New agent state.
1094 			const state_t & new_state );
1095 		/*!
1096 		 * \}
1097 		 */
1098 
1099 	public : /* Note: since v.5.2.3.2 subscription-related method are
1100 					made public. */
1101 
1102 		/*!
1103 		 * \name Subscription methods.
1104 		 * \{
1105 		 */
1106 
1107 		//! Initiate subscription.
1108 		/*!
1109 			This method starts a subscription procedure by returning
1110 			an instance of subscription_bind_t. The subscription details and
1111 			the completion of a subscription is controlled by this
1112 			subscription_bind_t object.
1113 
1114 			Usage sample:
1115 			\code
1116 			void a_sample_t::so_define_agent()
1117 			{
1118 				// Subscription for state `state_one`
1119 				so_subscribe( mbox_target )
1120 					.in( state_one )
1121 					.event( &a_sample_t::evt_sample_handler );
1122 
1123 				// Subscription for the default state.
1124 				so_subscribe( another_mbox )
1125 					.event( &a_sample_t::evt_another_handler );
1126 
1127 				// Subscription for several event handlers in the default state.
1128 				so_subscribe( yet_another_mbox )
1129 					.event( &a_sample_t::evt_yet_another_handler )
1130 					// Lambda-function can be used as event handler too.
1131 					.event( [this](mhood_t<some_message> cmd) {...} );
1132 
1133 				// Subscription for several event handlers.
1134 				// All of them will be subscribed for states first_state and second_state.
1135 				so_subscribe( some_mbox )
1136 					.in( first_state )
1137 					.in( second_state )
1138 					.event( &a_sample_t::evt_some_handler_1 )
1139 					.event( &a_sample_t::evt_some_handler_2 )
1140 					.event( &a_sample_t::evt_some_handler_3 );
1141 			}
1142 			\endcode
1143 		*/
1144 		inline subscription_bind_t
so_subscribe(const mbox_t & mbox_ref)1145 		so_subscribe(
1146 			//! Mbox for messages to subscribe.
1147 			const mbox_t & mbox_ref )
1148 		{
1149 			return subscription_bind_t( *this, mbox_ref );
1150 		}
1151 
1152 		/*!
1153 		 * \since
1154 		 * v.5.5.1
1155 		 *
1156 		 * \brief Initiate subscription to agent's direct mbox.
1157 		 *
1158 		 * Note that is just a short form of:
1159 		 * \code
1160 		 * void a_sample_t::so_define_agent()
1161 		 * {
1162 		 * 	so_subscribe( so_direct_mbox() )
1163 		 * 		.in( some_state )
1164 		 * 		.in( another_state )
1165 		 * 		.event( some_event_handler )
1166 		 * 		.event( some_another_handler );
1167 		 * }
1168 		 * \endcode
1169 		 * Instead of writting `so_subscribe(so_direct_mbox())` it is possible
1170 		 * to write just `so_subscribe_self()`.
1171 		 *
1172 		 * \par Usage sample:
1173 			\code
1174 			void a_sample_t::so_define_agent()
1175 			{
1176 				// Subscription for state `state_one`
1177 				so_subscribe_self()
1178 					.in( state_one )
1179 					.event( &a_sample_t::evt_sample_handler );
1180 
1181 				// Subscription for the default state.
1182 				so_subscribe_self()
1183 					.event( &a_sample_t::evt_another_handler );
1184 
1185 				// Subscription for several event handlers in the default state.
1186 				so_subscribe_self()
1187 					.event( &a_sample_t::evt_yet_another_handler )
1188 					// Lambda-function can be used as event handler too.
1189 					.event( [this](mhood_t<some_message> cmd) {...} );
1190 
1191 				// Subscription for several event handlers.
1192 				// All of them will be subscribed for states first_state and second_state.
1193 				so_subscribe_self()
1194 					.in( first_state )
1195 					.in( second_state )
1196 					.event( &a_sample_t::evt_some_handler_1 )
1197 					.event( &a_sample_t::evt_some_handler_2 )
1198 					.event( &a_sample_t::evt_some_handler_3 );
1199 			}
1200 			\endcode
1201 		 */
1202 		inline subscription_bind_t
so_subscribe_self()1203 		so_subscribe_self()
1204 		{
1205 			return so_subscribe( so_direct_mbox() );
1206 		}
1207 
1208 		/*!
1209 		 * \brief Create a subscription for an event.
1210 		 *
1211 		 * \note
1212 		 * Before v.5.5.21 it was a private method. Since v.5.5.21
1213 		 * it is a public method with a standard so_-prefix.
1214 		 * It was made public to allow creation of subscriptions
1215 		 * to agent from outside of agent.
1216 		 *
1217 		 * \note
1218 		 * Parameter \a handler_kind was introduced in v.5.7.0.
1219 		 */
1220 		void
1221 		so_create_event_subscription(
1222 			//! Message's mbox.
1223 			const mbox_t & mbox_ref,
1224 			//! Message type.
1225 			std::type_index type_index,
1226 			//! State for event.
1227 			const state_t & target_state,
1228 			//! Event handler caller.
1229 			const event_handler_method_t & method,
1230 			//! Thread safety of the event handler.
1231 			thread_safety_t thread_safety,
1232 			//! Kind of that event handler.
1233 			event_handler_kind_t handler_kind );
1234 
1235 		/*!
1236 		 * \brief Destroy event subscription.
1237 		 *
1238 		 * \note
1239 		 * This method was introduced in v.5.5.21 to allow manipulation
1240 		 * of agent's subscriptions from outside of an agent.
1241 		 *
1242 		 * \note
1243 		 * It is safe to try to destroy nonexistent subscription.
1244 		 *
1245 		 * \since
1246 		 * v.5.5.21
1247 		 */
1248 		void
so_destroy_event_subscription(const mbox_t & mbox,const std::type_index & subscription_type,const state_t & target_state)1249 		so_destroy_event_subscription(
1250 			//! Message's mbox.
1251 			const mbox_t & mbox,
1252 			//! Message's type.
1253 			const std::type_index & subscription_type,
1254 			//! Target state of a subscription.
1255 			const state_t & target_state )
1256 			{
1257 				do_drop_subscription(
1258 						mbox,
1259 						subscription_type,
1260 						target_state );
1261 			}
1262 
1263 		/*!
1264 		 * \since
1265 		 * v.5.2.3
1266 		 *
1267 		 * \brief Drop subscription for the state specified.
1268 		 *
1269 		 * This overload is indended to be used when there is an event-handler in
1270 		 * the form of agent's method. And there is a need to unsubscribe this
1271 		 * event handler.
1272 		 * For example:
1273 		 * \code
1274 		 * class demo : public so_5::agent_t {
1275 		 * 	void on_some_event(mhood_t<some_msg> cmd) {
1276 		 * 		if(cmd->some_condition)
1277 		 * 			// New subscription must be created.
1278 		 * 			so_subscribe(some_mbox).in(some_state)
1279 		 * 				.event(&demo::one_shot_message_handler);
1280 		 * 		...
1281 		 * 	}
1282 		 *
1283 		 * 	void one_shot_message_handler(mhood_t<another_msg> cmd) {
1284 		 * 		... // Some actions.
1285 		 * 		// Subscription is no more needed.
1286 		 * 		so_drop_subscription(some_mbox, some_state,
1287 		 * 			&demo::one_shot_message_handler);
1288 		 * 	}
1289 		 * };
1290 		 * \endcode
1291 		 *
1292 		 * \note Doesn't throw if there is no such subscription.
1293 		 *
1294 		 * \note Subscription is removed even if agent was subscribed
1295 		 * for this message type with different method pointer.
1296 		 * The pointer to event routine is necessary only to
1297 		 * detect MSG type.
1298 		 */
1299 		template< typename Method_Pointer >
1300 		typename std::enable_if<
1301 				details::is_agent_method_pointer<
1302 						details::method_arity::unary,
1303 						Method_Pointer>::value,
1304 				void >::type
so_drop_subscription(const mbox_t & mbox,const state_t & target_state,Method_Pointer)1305 		so_drop_subscription(
1306 			const mbox_t & mbox,
1307 			const state_t & target_state,
1308 			Method_Pointer /*pfn*/ )
1309 		{
1310 			using pfn_traits = details::is_agent_method_pointer<
1311 					details::method_arity::unary, Method_Pointer>;
1312 
1313 			using message_type =
1314 					typename details::message_handler_format_detector<
1315 							typename pfn_traits::argument_type >::type;
1316 
1317 			do_drop_subscription( mbox,
1318 					message_payload_type< message_type >::subscription_type_index(),
1319 					target_state );
1320 		}
1321 
1322 		/*!
1323 		 * \since
1324 		 * v.5.5.3
1325 		 *
1326 		 * \brief Drop subscription for the state specified.
1327 		 *
1328 		 * Usage example:
1329 		 * \code
1330 		 * class demo : public so_5::agent_t {
1331 		 * 	void on_turn_listening_on(mhood_t<turn_on> cmd) {
1332 		 * 		// New subscription must be created.
1333 		 * 		so_subscribe(cmd->listeting_mbox()).in(some_state)
1334 		 * 			.event([this](mhood_t<state_change_notify> cmd) {...});
1335 		 * 		...
1336 		 * 	}
1337 		 *
1338 		 * 	void on_turn_listening_off(mhood_t<turn_off> cmd) {
1339 		 * 		// Subscription is no more needed.
1340 		 * 		so_drop_subscription<state_change_notify>(cmd->listening_mbox(), some_state);
1341 		 * 		...
1342 		 * 	}
1343 		 * };
1344 		 * \endcode
1345 		 *
1346 		 * \note Doesn't throw if there is no such subscription.
1347 		 */
1348 		template< class Message >
1349 		inline void
so_drop_subscription(const mbox_t & mbox,const state_t & target_state)1350 		so_drop_subscription(
1351 			const mbox_t & mbox,
1352 			const state_t & target_state )
1353 		{
1354 			do_drop_subscription(
1355 					mbox,
1356 					message_payload_type< Message >::subscription_type_index(),
1357 					target_state );
1358 		}
1359 
1360 		/*!
1361 		 * \since
1362 		 * v.5.2.3
1363 		 *
1364 		 * \brief Drop subscription for the default agent state.
1365 		 *
1366 		 * This overload is indended to be used when there is an event-handler in
1367 		 * the form of agent's method. And there is a need to unsubscribe this
1368 		 * event handler.
1369 		 * For example:
1370 		 * \code
1371 		 * class demo : public so_5::agent_t {
1372 		 * 	void on_some_event(mhood_t<some_msg> cmd) {
1373 		 * 		if(cmd->some_condition)
1374 		 * 			// New subscription must be created.
1375 		 * 			so_subscribe(some_mbox)
1376 		 * 				.event(&demo::one_shot_message_handler);
1377 		 * 		...
1378 		 * 	}
1379 		 *
1380 		 * 	void one_shot_message_handler(mhood_t<another_msg> cmd) {
1381 		 * 		... // Some actions.
1382 		 * 		// Subscription is no more needed.
1383 		 * 		so_drop_subscription(some_mbox,
1384 		 * 			&demo::one_shot_message_handler);
1385 		 * 	}
1386 		 * };
1387 		 * \endcode
1388 		 *
1389 		 * \note Doesn't throw if there is no such subscription.
1390 		 *
1391 		 * \note Subscription is removed even if agent was subscribed
1392 		 * for this message type with different method pointer.
1393 		 * The pointer to event routine is necessary only to
1394 		 * detect Msg type.
1395 		 */
1396 		template< typename Method_Pointer >
1397 		typename std::enable_if<
1398 				details::is_agent_method_pointer<
1399 						details::method_arity::unary,
1400 						Method_Pointer>::value,
1401 				void >::type
so_drop_subscription(const mbox_t & mbox,Method_Pointer)1402 		so_drop_subscription(
1403 			const mbox_t & mbox,
1404 			Method_Pointer /*pfn*/ )
1405 		{
1406 			using pfn_traits = details::is_agent_method_pointer<
1407 					details::method_arity::unary, Method_Pointer>;
1408 
1409 			using message_type =
1410 					typename details::message_handler_format_detector<
1411 							typename pfn_traits::argument_type >::type;
1412 
1413 			do_drop_subscription(
1414 					mbox,
1415 					message_payload_type< message_type >::subscription_type_index(),
1416 					so_default_state() );
1417 		}
1418 
1419 		/*!
1420 		 * \since
1421 		 * v.5.5.3
1422 		 *
1423 		 * \brief Drop subscription for the default agent state.
1424 		 *
1425 		 * Usage example:
1426 		 * \code
1427 		 * class demo : public so_5::agent_t {
1428 		 * 	void on_turn_listening_on(mhood_t<turn_on> cmd) {
1429 		 * 		// New subscription must be created.
1430 		 * 		so_subscribe(cmd->listening_mbox())
1431 		 * 			.event([this](mhood_t<state_change_notify> cmd) {...});
1432 		 * 		...
1433 		 * 	}
1434 		 *
1435 		 * 	void on_turn_listening_off(mhood_t<turn_off> cmd) {
1436 		 * 		// Subscription is no more needed.
1437 		 * 		so_drop_subscription<state_change_notify>(cmd->listening_mbox());
1438 		 * 		...
1439 		 * 	}
1440 		 * };
1441 		 * \endcode
1442 		 *
1443 		 * \note Doesn't throw if there is no such subscription.
1444 		 */
1445 		template< class Message >
1446 		inline void
so_drop_subscription(const mbox_t & mbox)1447 		so_drop_subscription(
1448 			const mbox_t & mbox )
1449 		{
1450 			do_drop_subscription(
1451 					mbox,
1452 					message_payload_type< Message >::subscription_type_index(),
1453 					so_default_state() );
1454 		}
1455 
1456 		/*!
1457 		 * \since
1458 		 * v.5.2.3
1459 		 *
1460 		 * \brief Drop subscription for all states.
1461 		 *
1462 		 * Usage example:
1463 		 * \code
1464 		 * class demo : public so_5::agent_t {
1465 		 * 	state_t st_working{this}, st_waiting{this}, st_stopping{this};
1466 		 * 	...
1467 		 * 	void on_turn_listening_on(mhood_t<turn_on> cmd) {
1468 		 * 		// Make subscriptions for message of type state_change_notify.
1469 		 * 		st_working.event(cmd->listening_mbox(),
1470 		 * 				&demo::on_state_notify_when_working);
1471 		 * 		st_waiting.event(cmd->listening_mbox(),
1472 		 * 				&demo::on_state_notify_when_waiting);
1473 		 * 		st_waiting.event(cmd->listening_mbox(),
1474 		 * 				&demo::on_state_notify_when_stopping);
1475 		 * 		...
1476 		 * 	}
1477 		 * 	void on_turn_listening_off(mhood_t<turn_off> cmd) {
1478 		 * 		// Subscriptions are no more needed.
1479 		 * 		// All three event handlers for state_change_notify
1480 		 * 		// will be unsubscribed.
1481 		 * 		so_drop_subscription_for_all_states(cmd->listening_mbox(),
1482 		 * 				&demo::on_state_notify_when_working);
1483 		 * 	}
1484 		 * 	...
1485 		 *		void on_state_notify_when_working(mhood_t<state_change_notify> cmd) {...}
1486 		 *		void on_state_notify_when_waiting(mhood_t<state_change_notify> cmd) {...}
1487 		 *		void on_state_notify_when_stopping(mhood_t<state_change_notify> cmd) {...}
1488 		 * };
1489 		 * \endcode
1490 		 *
1491 		 * \note Doesn't throw if there is no any subscription for
1492 		 * that mbox and message type.
1493 		 *
1494 		 * \note Subscription is removed even if agent was subscribed
1495 		 * for this message type with different method pointer.
1496 		 * The pointer to event routine is necessary only to
1497 		 * detect Msg type.
1498 		 *
1499 		 * \note
1500 		 * Since v.5.5.21 this method also drops the subscription
1501 		 * for a deadletter handler for that type of message/signal.
1502 		 */
1503 		template< typename Method_Pointer >
1504 		typename std::enable_if<
1505 				details::is_agent_method_pointer<
1506 						details::method_arity::unary,
1507 						Method_Pointer>::value,
1508 				void >::type
so_drop_subscription_for_all_states(const mbox_t & mbox,Method_Pointer)1509 		so_drop_subscription_for_all_states(
1510 			const mbox_t & mbox,
1511 			Method_Pointer /*pfn*/ )
1512 		{
1513 			using pfn_traits = details::is_agent_method_pointer<
1514 					details::method_arity::unary, Method_Pointer>;
1515 
1516 			using message_type =
1517 					typename details::message_handler_format_detector<
1518 							typename pfn_traits::argument_type >::type;
1519 
1520 			do_drop_subscription_for_all_states(
1521 					mbox,
1522 					message_payload_type< message_type >::subscription_type_index() );
1523 		}
1524 
1525 		/*!
1526 		 * \since
1527 		 * v.5.5.3
1528 		 *
1529 		 * \brief Drop subscription for all states.
1530 		 *
1531 		 * Usage example:
1532 		 * \code
1533 		 * class demo : public so_5::agent_t {
1534 		 * 	state_t st_working{this}, st_waiting{this}, st_stopping{this};
1535 		 * 	...
1536 		 * 	void on_turn_listening_on(mhood_t<turn_on> cmd) {
1537 		 * 		// Make subscriptions for message of type state_change_notify.
1538 		 * 		st_working.event(cmd->listening_mbox(),
1539 		 * 				[this](mhood_t<state_change_notify> cmd) {...});
1540 		 * 		st_waiting.event(cmd->listening_mbox(),
1541 		 * 				[this](mhood_t<state_change_notify> cmd) {...});
1542 		 * 		st_waiting.event(cmd->listening_mbox(),
1543 		 * 				[this](mhood_t<state_change_notify> cmd) {...});
1544 		 * 		...
1545 		 * 	}
1546 		 * 	void on_turn_listening_off(mhood_t<turn_off> cmd) {
1547 		 * 		// Subscriptions are no more needed.
1548 		 * 		// All three event handlers for state_change_notify
1549 		 * 		// will be unsubscribed.
1550 		 * 		so_drop_subscription_for_all_states<state_change_notify>(cmd->listening_mbox());
1551 		 * 	}
1552 		 * 	...
1553 		 * };
1554 		 * \endcode
1555 		 * \note Doesn't throw if there is no any subscription for
1556 		 * that mbox and message type.
1557 		 *
1558 		 * \note
1559 		 * Since v.5.5.21 this method also drops the subscription
1560 		 * for a deadletter handler for that type of message/signal.
1561 		 */
1562 		template< class Message >
1563 		inline void
so_drop_subscription_for_all_states(const mbox_t & mbox)1564 		so_drop_subscription_for_all_states(
1565 			const mbox_t & mbox )
1566 		{
1567 			do_drop_subscription_for_all_states(
1568 					mbox,
1569 					message_payload_type< Message >::subscription_type_index() );
1570 		}
1571 
1572 		/*!
1573 		 * \brief Check the presence of a subscription.
1574 		 *
1575 		 * This method can be used to avoid an exception from so_subscribe()
1576 		 * in the case if the subscription is already present. For example:
1577 		 * \code
1578 		 * void my_agent::evt_create_new_subscription(mhood_t<data_source> cmd)
1579 		 * {
1580 		 * 	// cmd can contain mbox we have already subscribed to.
1581 		 * 	// If we just call so_subscribe() then an exception can be thrown.
1582 		 * 	// Because of that check the presence of subscription first.
1583 		 * 	if(!so_has_subscription<message>(cmd->mbox(), so_default_state()))
1584 		 * 	{
1585 		 * 		// There is no subscription yet. New subscription can be
1586 		 * 		// created.
1587 		 * 		so_subscribe(cmd->mbox()).event(...);
1588 		 * 	}
1589 		 * }
1590 		 * \endcode
1591 		 *
1592 		 * \note
1593 		 * Please do not call this method from outside of working context
1594 		 * of the agent.
1595 		 *
1596 		 * \return true if subscription is present for \a target_state.
1597 		 *
1598 		 * \tparam Message a type of message/signal subscription to which
1599 		 * must be checked.
1600 		 *
1601 		 * \since
1602 		 * v.5.5.19.5
1603 		 */
1604 		template< class Message >
1605 		bool
so_has_subscription(const mbox_t & mbox,const state_t & target_state) const1606 		so_has_subscription(
1607 			//! A mbox from which message/signal of type \a Message is expected.
1608 			const mbox_t & mbox,
1609 			//! A target state for the subscription.
1610 			const state_t & target_state ) const noexcept
1611 		{
1612 			return do_check_subscription_presence(
1613 					mbox,
1614 					message_payload_type< Message >::subscription_type_index(),
1615 					target_state );
1616 		}
1617 
1618 		/*!
1619 		 * \brief Check the presence of a subscription in the default_state.
1620 		 *
1621 		 * This method can be used to avoid an exception from so_subscribe()
1622 		 * in the case if the subscription is already present. For example:
1623 		 * \code
1624 		 * void my_agent::evt_create_new_subscription(mhood_t<data_source> cmd)
1625 		 * {
1626 		 * 	// cmd can contain mbox we have already subscribed to.
1627 		 * 	// If we just call so_subscribe() then an exception can be thrown.
1628 		 * 	// Because of that check the presence of subscription first.
1629 		 * 	if(!so_has_subscription<message>(cmd->mbox()))
1630 		 * 	{
1631 		 * 		// There is no subscription yet. New subscription can be
1632 		 * 		// created.
1633 		 * 		so_subscribe(cmd->mbox()).event(...);
1634 		 * 	}
1635 		 * }
1636 		 * \endcode
1637 		 *
1638 		 * \note
1639 		 * Please do not call this method from outside of working context
1640 		 * of the agent.
1641 		 *
1642 		 * \return true if subscription is present for the default_state.
1643 		 *
1644 		 * \tparam Message a type of message/signal subscription to which
1645 		 * must be checked.
1646 		 *
1647 		 * \since
1648 		 * v.5.5.19.5
1649 		 */
1650 		template< class Message >
1651 		bool
so_has_subscription(const mbox_t & mbox) const1652 		so_has_subscription(
1653 			//! A mbox from which message/signal of type \a Message is expected.
1654 			const mbox_t & mbox ) const noexcept
1655 		{
1656 			return do_check_subscription_presence(
1657 					mbox,
1658 					message_payload_type< Message >::subscription_type_index(),
1659 					so_default_state() );
1660 		}
1661 
1662 		/*!
1663 		 * \brief Check the presence of a subscription.
1664 		 *
1665 		 * Type of message is deducted from event-handler signature.
1666 		 *
1667 		 * Usage example:
1668 		 * \code
1669 		 * void my_agent::evt_create_new_subscription(mhood_t<data_source> cmd)
1670 		 * {
1671 		 * 	// cmd can contain mbox we have already subscribed to.
1672 		 * 	// If we just call so_subscribe() then an exception can be thrown.
1673 		 * 	// Because of that check the presence of subscription first.
1674 		 * 	if(!so_has_subscription(cmd->mbox(), my_state, &my_agent::my_event))
1675 		 * 	{
1676 		 * 		// There is no subscription yet. New subscription can be
1677 		 * 		// created.
1678 		 * 		so_subscribe(cmd->mbox()).event(...);
1679 		 * 	}
1680 		 * }
1681 		 * \endcode
1682 		 *
1683 		 * \note
1684 		 * Please do not call this method from outside of working context
1685 		 * of the agent.
1686 		 *
1687 		 * \return true if subscription is present for \a target_state.
1688 		 *
1689 		 * \since
1690 		 * v.5.5.19.5
1691 		 */
1692 		template< typename Method_Pointer >
1693 		typename std::enable_if<
1694 				details::is_agent_method_pointer<
1695 						details::method_arity::unary,
1696 						Method_Pointer>::value,
1697 				bool >::type
so_has_subscription(const mbox_t & mbox,const state_t & target_state,Method_Pointer) const1698 		so_has_subscription(
1699 			//! A mbox from which message/signal is expected.
1700 			const mbox_t & mbox,
1701 			//! A target state for the subscription.
1702 			const state_t & target_state,
1703 			Method_Pointer /*pfn*/ ) const noexcept
1704 		{
1705 			using pfn_traits = details::is_agent_method_pointer<
1706 					details::method_arity::unary, Method_Pointer>;
1707 
1708 			using message_type =
1709 					typename details::message_handler_format_detector<
1710 							typename pfn_traits::argument_type>::type;
1711 
1712 			return this->so_has_subscription<message_type>(
1713 					mbox, target_state );
1714 		}
1715 
1716 		/*!
1717 		 * \brief Check the presence of a subscription.
1718 		 *
1719 		 * Subscription is checked for the default agent state.
1720 		 *
1721 		 * Type of message is deducted from event-handler signature.
1722 		 *
1723 		 * Usage example:
1724 		 * \code
1725 		 * void my_agent::evt_create_new_subscription(mhood_t<data_source> cmd)
1726 		 * {
1727 		 * 	// cmd can contain mbox we have already subscribed to.
1728 		 * 	// If we just call so_subscribe() then an exception can be thrown.
1729 		 * 	// Because of that check the presence of subscription first.
1730 		 * 	if(!so_has_subscription(cmd->mbox(), &my_agent::my_event))
1731 		 * 	{
1732 		 * 		// There is no subscription yet. New subscription can be
1733 		 * 		// created.
1734 		 * 		so_subscribe(cmd->mbox()).event(...);
1735 		 * 	}
1736 		 * }
1737 		 * \endcode
1738 		 *
1739 		 * \note
1740 		 * Please do not call this method from outside of working context
1741 		 * of the agent.
1742 		 *
1743 		 * \return true if subscription is present for the default state.
1744 		 *
1745 		 * \since
1746 		 * v.5.5.19.5
1747 		 */
1748 		template< typename Method_Pointer >
1749 		typename std::enable_if<
1750 				details::is_agent_method_pointer<
1751 						details::method_arity::unary,
1752 						Method_Pointer>::value,
1753 				bool >::type
so_has_subscription(const mbox_t & mbox,Method_Pointer) const1754 		so_has_subscription(
1755 			//! A mbox from which message/signal is expected.
1756 			const mbox_t & mbox,
1757 			Method_Pointer /*pfn*/ ) const noexcept
1758 		{
1759 			using pfn_traits = details::is_agent_method_pointer<
1760 					details::method_arity::unary, Method_Pointer>;
1761 
1762 			using message_type =
1763 					typename details::message_handler_format_detector<
1764 							typename pfn_traits::argument_type>::type;
1765 
1766 			return this->so_has_subscription<message_type>(
1767 					mbox, so_default_state() );
1768 		}
1769 		/*!
1770 		 * \}
1771 		 */
1772 
1773 		/*!
1774 		 * \name Methods for dealing with deadletter subscriptions.
1775 		 * \{
1776 		 */
1777 		/*!
1778 		 * \brief Create a subscription for a deadletter handler.
1779 		 *
1780 		 * \note
1781 		 * This is low-level method intended to be used by libraries writters.
1782 		 * Do not call it directly if you don't understand its purpose and
1783 		 * what its arguments mean. Use so_subscribe_deadletter_handler()
1784 		 * instead.
1785 		 *
1786 		 * This method actually creates a subscription to deadletter handler
1787 		 * for messages/signal of type \a msg_type from mbox \a mbox.
1788 		 *
1789 		 * \throw so_5::exception_t in the case when the subscription
1790 		 * of a deadletter handler for type \a msg_type from \a mbox is
1791 		 * already exists.
1792 		 *
1793 		 * \since
1794 		 * v.5.5.21
1795 		 */
1796 		void
1797 		so_create_deadletter_subscription(
1798 			//! Message's mbox.
1799 			const mbox_t & mbox,
1800 			//! Message type.
1801 			const std::type_index & msg_type,
1802 			//! Event handler caller.
1803 			const event_handler_method_t & method,
1804 			//! Thread safety of the event handler.
1805 			thread_safety_t thread_safety );
1806 
1807 		/*!
1808 		 * \brief Destroy a subscription for a deadletter handler.
1809 		 *
1810 		 * \note
1811 		 * This is low-level method intended to be used by libraries writters.
1812 		 * Do not call it directly if you don't understand its purpose and
1813 		 * what its arguments mean. Use so_drop_deadletter_handler() instead.
1814 		 *
1815 		 * This method actually destroys a subscription to deadletter handler
1816 		 * for messages/signal of type \a msg_type from mbox \a mbox.
1817 		 *
1818 		 * \note
1819 		 * It is safe to call this method if there is no such
1820 		 * deadletter handler. It will do nothing in such case.
1821 		 *
1822 		 * \since
1823 		 * v.5.5.21
1824 		 */
1825 		void
1826 		so_destroy_deadletter_subscription(
1827 			//! Message's mbox.
1828 			const mbox_t & mbox,
1829 			//! Message type.
1830 			const std::type_index & msg_type );
1831 
1832 		/*!
1833 		 * \brief Create a subscription for deadletter handler for
1834 		 * a specific message from a specific mbox.
1835 		 *
1836 		 * Type of a message for deadletter handler will be detected
1837 		 * automatically from the signature of the \a handler.
1838 		 *
1839 		 * A deadletter handler can be a pointer to method of agent or
1840 		 * lambda-function. The handler should have one of the following
1841 		 * format:
1842 		 * \code
1843 		 * void deadletter_handler(message_type);
1844 		 * void deadletter_handler(const message_type &);
1845 		 * void deadletter_handler(mhood_t<message_type>);
1846 		 * \endcode
1847 		 *
1848 		 * Usage example:
1849 		 * \code
1850 		 * class demo : public so_5::agent_t {
1851 		 * 	void on_some_message(mhood_t<some_message> cmd) {...}
1852 		 * 	...
1853 		 * 	virtual void so_define_agent() override {
1854 		 * 		// Create deadletter handler via pointer to method.
1855 		 * 		// Event handler will be not-thread-safe.
1856 		 * 		so_subscribe_deadletter_handler(
1857 		 * 			so_direct_mbox(),
1858 		 * 			&demo::on_some_message );
1859 		 *
1860 		 * 		// Create deadletter handler via lambda-function.
1861 		 * 		so_subscribe_deadletter_handler(
1862 		 * 			status_mbox(), // Any mbox can be used, not only agent's direct mbox.
1863 		 * 			[](mhood_t<status_request> cmd) {
1864 		 * 				so_5::send<current_status>(cmd->reply_mbox, "workind");
1865 		 * 			},
1866 		 *				// This handler will be thread-safe one.
1867 		 *				so_5::thread_safe );
1868 		 * 	}
1869 		 * };
1870 		 * \endcode
1871 		 *
1872 		 * \throw so_5::exception_t in the case when the subscription
1873 		 * of a deadletter handler for type \a msg_type from \a mbox is
1874 		 * already exists.
1875 		 *
1876 		 * \since
1877 		 * v.5.5.21
1878 		 */
1879 		template< typename Event_Handler >
1880 		void
so_subscribe_deadletter_handler(const so_5::mbox_t & mbox,Event_Handler && handler,thread_safety_t thread_safety=thread_safety_t::unsafe)1881 		so_subscribe_deadletter_handler(
1882 			const so_5::mbox_t & mbox,
1883 			Event_Handler && handler,
1884 			thread_safety_t thread_safety = thread_safety_t::unsafe )
1885 			{
1886 				using namespace details::event_subscription_helpers;
1887 
1888 				const auto ev = preprocess_agent_event_handler(
1889 						mbox,
1890 						*this,
1891 						std::forward<Event_Handler>(handler) );
1892 
1893 				so_create_deadletter_subscription(
1894 						mbox,
1895 						ev.m_msg_type,
1896 						ev.m_handler,
1897 						thread_safety );
1898 			}
1899 
1900 		/*!
1901 		 * \brief Drops the subscription for deadletter handler.
1902 		 *
1903 		 * A message type must be specified explicitely via template
1904 		 * parameter.
1905 		 *
1906 		 * Usage example:
1907 		 * \code
1908 		 * class demo : public so_5::agent_t {
1909 		 * 	void some_deadletter_handler(mhood_t<some_message> cmd) {
1910 		 * 		... // Do some stuff.
1911 		 * 		// There is no need for deadletter handler.
1912 		 * 		so_drop_deadletter_handler<some_message>(some_mbox);
1913 		 * 	}
1914 		 * 	...
1915 		 * };
1916 		 * \endcode
1917 		 *
1918 		 * \note
1919 		 * Is is safe to call this method if there is no a deadletter
1920 		 * handler for message of type \a Message from message box
1921 		 * \a mbox.
1922 		 *
1923 		 * \tparam Message Type of a message or signal for deadletter
1924 		 * handler.
1925 		 *
1926 		 * \since
1927 		 * v.5.5.21
1928 		 */
1929 		template< typename Message >
1930 		void
so_drop_deadletter_handler(const so_5::mbox_t & mbox)1931 		so_drop_deadletter_handler(
1932 			//! A mbox from which the message is expected.
1933 			const so_5::mbox_t & mbox )
1934 			{
1935 				so_destroy_deadletter_subscription(
1936 						mbox,
1937 						message_payload_type< Message >::subscription_type_index() );
1938 			}
1939 
1940 		/*!
1941 		 * \brief Checks the presence of deadletter handler for a message of
1942 		 * a specific type from a specific mbox.
1943 		 *
1944 		 * Message type must be specified explicitely via template
1945 		 * parameter \a Message.
1946 		 *
1947 		 * \return true if a deadletter for a message/signal of type
1948 		 * \a Message from message mbox \a mbox exists.
1949 		 *
1950 		 * Usage example:
1951 		 * \code
1952 		 * class demo : public so_5::agent_t {
1953 		 * 	void on_some_request(mhood_t<request_data> cmd) {
1954 		 * 		if(!so_has_deadletter_handler<some_message>(some_mbox))
1955 		 * 			// There is no deadletter handler yet.
1956 		 * 			// It should be created now.
1957 		 * 			so_subscribe_deadletter_handler(
1958 		 * 				some_mbox,
1959 		 * 				[this](mhood_t<some_message> cmd) {...});
1960 		 * 		...
1961 		 * 	}
1962 		 * };
1963 		 * \endcode
1964 		 *
1965 		 * \tparam Message Type of a message or signal for deadletter
1966 		 * handler.
1967 		 *
1968 		 * \since
1969 		 * v.5.5.21
1970 		 */
1971 		template< typename Message >
1972 		bool
so_has_deadletter_handler(const so_5::mbox_t & mbox) const1973 		so_has_deadletter_handler(
1974 			//! A mbox from which the message is expected.
1975 			const so_5::mbox_t & mbox ) const noexcept
1976 			{
1977 				return do_check_deadletter_presence(
1978 						mbox,
1979 						message_payload_type< Message >::subscription_type_index() );
1980 			}
1981 		/*!
1982 		 * \}
1983 		 */
1984 
1985 	protected :
1986 
1987 		/*!
1988 		 * \name Agent initialization methods.
1989 		 * \{
1990 		 */
1991 		/*!
1992 		 * \since
1993 		 * v.5.4.0
1994 		 *
1995 		 * \brief A correct initiation of so_define_agent method call.
1996 		 *
1997 		 * Before the actual so_define_agent() method it is necessary
1998 		 * to temporary set working thread id. And then drop this id
1999 		 * to non-actual value after so_define_agent() return.
2000 		 *
2001 		 * Because of that this method must be called during cooperation
2002 		 * registration procedure instead of direct call of so_define_agent().
2003 		 */
2004 		void
2005 		so_initiate_agent_definition();
2006 
2007 		//! Hook on define agent for SObjectizer.
2008 		/*!
2009 			This method is called by SObjectizer during the cooperation
2010 			registration process before agent will be bound to its
2011 			working thread.
2012 
2013 			Should be used by the agent to make necessary message subscriptions.
2014 
2015 			Usage sample;
2016 			\code
2017 			class a_sample_t : public so_5::agent_t
2018 			{
2019 				// ...
2020 				virtual void
2021 				so_define_agent();
2022 
2023 				void evt_handler_1( mhood_t< message1 > msg );
2024 				// ...
2025 				void evt_handler_N( mhood_t< messageN > & msg );
2026 
2027 			};
2028 
2029 			void
2030 			a_sample_t::so_define_agent()
2031 			{
2032 				// Make subscriptions...
2033 				so_subscribe( m_mbox1 )
2034 					.in( m_state_1 )
2035 						.event( &a_sample_t::evt_handler_1 );
2036 				// ...
2037 				so_subscribe( m_mboxN )
2038 					.in( m_state_N )
2039 						.event( &a_sample_t::evt_handler_N );
2040 			}
2041 			\endcode
2042 		*/
2043 		virtual void
2044 		so_define_agent();
2045 
2046 		//! Is method define_agent already called?
2047 		/*!
2048 			Usage sample:
2049 			\code
2050 			class a_sample_t : public so_5::agent_t
2051 			{
2052 				// ...
2053 
2054 				public:
2055 					void
2056 					set_target_mbox( const so_5::mbox_t & mbox )
2057 					{
2058 						// mbox cannot be changed after agent registration.
2059 						if( !so_was_defined() )
2060 						{
2061 							m_target_mbox = mbox;
2062 						}
2063 					}
2064 
2065 				private:
2066 					so_5::mbox_t m_target_mbox;
2067 			};
2068 			\endcode
2069 		*/
2070 		bool
2071 		so_was_defined() const;
2072 		/*!
2073 		 * \}
2074 		 */
2075 
2076 	public:
2077 		//! Access to the SObjectizer Environment which this agent is belong.
2078 		/*!
2079 			Usage sample for other cooperation registration:
2080 			\code
2081 			void a_sample_t::evt_on_smth( mhood_t< some_message_t > msg )
2082 			{
2083 				so_5::coop_unique_holder_t coop = so_environment().make_coop();
2084 
2085 				// Filling the cooperation...
2086 				coop->make_agent< a_another_t >( ... );
2087 				// ...
2088 
2089 				// Registering cooperation.
2090 				so_environment().register_coop( std::move(coop) );
2091 			}
2092 			\endcode
2093 
2094 			Usage sample for the SObjectizer shutting down:
2095 			\code
2096 			void a_sample_t::evt_last_event( mhood_t< message_one_t > msg )
2097 			{
2098 				...
2099 				so_environment().stop();
2100 			}
2101 			\endcode
2102 		*/
2103 		environment_t &
2104 		so_environment() const;
2105 
2106 		/*!
2107 		 * \brief Get a handle of agent's coop.
2108 		 *
2109 		 * \note
2110 		 * This method is a replacement for so_coop_name() method
2111 		 * from previous versions of SObjectizer-5.
2112 		 *
2113 		 * \attention
2114 		 * If this method is called when agent is not registered (e.g.
2115 		 * there is no coop for agent) then this method will throw.
2116 		 *
2117 		 * Usage example:
2118 		 * \code
2119 		 * class parent final : public so_5::agent_t {
2120 		 * 	...
2121 		 * 	void so_evt_start() override {
2122 		 * 		// Create a child coop.
2123 		 * 		auto coop = so_environment().make_coop(
2124 		 * 			// We as a parent coop.
2125 		 * 			so_coop() );
2126 		 * 		...; // Fill the coop.
2127 		 * 		so_environment().register_coop( std::move(coop) );
2128 		 * 	}
2129 		 * };
2130 		 * \endcode
2131 		 *
2132 		 * \since
2133 		 * v.5.6.0
2134 		 */
2135 		[[nodiscard]]
2136 		coop_handle_t
2137 		so_coop() const;
2138 
2139 		/*!
2140 		 * \since
2141 		 * v.5.4.0
2142 		 *
2143 		 * \brief Binding agent to the dispatcher.
2144 		 *
2145 		 * This is an actual start of agent's work in SObjectizer.
2146 		 *
2147 		 * \note
2148 		 * This method was a de-facto noexcept in previous versions of
2149 		 * SObjectizer. But didn't marked as noexcept because of need of
2150 		 * support old C++ compilers. Since v.5.6.0 it is officially noexcept.
2151 		 */
2152 		void
2153 		so_bind_to_dispatcher(
2154 			//! Actual event queue for an agent.
2155 			event_queue_t & queue ) noexcept;
2156 
2157 		/*!
2158 		 * \since
2159 		 * v.5.4.0
2160 		 *
2161 		 * \brief Create execution hint for the specified demand.
2162 		 *
2163 		 * The hint returned is intendent for the immediately usage.
2164 		 * It must not be stored for the long time and used sometime in
2165 		 * the future. It is because internal state of the agent
2166 		 * can be changed and some references from hint object to
2167 		 * agent's internals become invalid.
2168 		 */
2169 		static execution_hint_t
2170 		so_create_execution_hint(
2171 			//! Demand for execution of event handler.
2172 			execution_demand_t & demand );
2173 
2174 		/*!
2175 		 * \since
2176 		 * v.5.4.0
2177 		 *
2178 		 * \brief A helper method for deregistering agent's coop.
2179 		 */
2180 		void
2181 		so_deregister_agent_coop( int dereg_reason );
2182 
2183 		/*!
2184 		 * \since
2185 		 * v.5.4.0
2186 		 *
2187 		 * \brief A helper method for deregistering agent's coop
2188 		 * in case of normal deregistration.
2189 		 *
2190 		 * \note It is just a shorthand for:
2191 			\code
2192 			so_deregister_agent_coop( so_5::dereg_reason::normal );
2193 			\endcode
2194 		 */
2195 		void
2196 		so_deregister_agent_coop_normally();
2197 
2198 		/*!
2199 		 * \name Methods for dealing with message delivery filters.
2200 		 * \{
2201 		 */
2202 		/*!
2203 		 * \since
2204 		 * v.5.5.5
2205 		 *
2206 		 * \brief Set a delivery filter.
2207 		 *
2208 		 * \tparam Message type of message to be filtered.
2209 		 */
2210 		template< typename Message >
2211 		void
so_set_delivery_filter(const mbox_t & mbox,delivery_filter_unique_ptr_t filter)2212 		so_set_delivery_filter(
2213 			//! Message box from which message is expected.
2214 			//! This must be MPMC-mbox.
2215 			const mbox_t & mbox,
2216 			//! Delivery filter instance.
2217 			delivery_filter_unique_ptr_t filter )
2218 			{
2219 				ensure_not_signal< Message >();
2220 
2221 				do_set_delivery_filter(
2222 						mbox,
2223 						message_payload_type< Message >::subscription_type_index(),
2224 						std::move(filter) );
2225 			}
2226 
2227 		/*!
2228 		 * \since
2229 		 * v.5.5.5
2230 		 *
2231 		 * \brief Set a delivery filter.
2232 		 *
2233 		 * \tparam Lambda type of lambda-function or functional object which
2234 		 * must be used as message filter.
2235 		 *
2236 		 * \par Usage sample:
2237 		 \code
2238 		 void my_agent::so_define_agent() {
2239 		 	so_set_delivery_filter( temp_sensor,
2240 				[]( const current_temperature & msg ) {
2241 					return !is_normal_temperature( msg );
2242 				} );
2243 			...
2244 		 }
2245 		 \endcode
2246 		 */
2247 		template< typename Lambda >
2248 		void
2249 		so_set_delivery_filter(
2250 			//! Message box from which message is expected.
2251 			//! This must be MPMC-mbox.
2252 			const mbox_t & mbox,
2253 			//! Delivery filter as lambda-function or functional object.
2254 			Lambda && lambda );
2255 
2256 		/*!
2257 		 * \since
2258 		 * v.5.5.5
2259 		 *
2260 		 * \brief Drop a delivery filter.
2261 		 *
2262 		 * \tparam Message type of message filtered.
2263 		 */
2264 		template< typename Message >
2265 		void
so_drop_delivery_filter(const mbox_t & mbox)2266 		so_drop_delivery_filter(
2267 			//! Message box to which delivery filter was set.
2268 			//! This must be MPMC-mbox.
2269 			const mbox_t & mbox ) noexcept
2270 			{
2271 				do_drop_delivery_filter(
2272 						mbox,
2273 						message_payload_type< Message >::subscription_type_index() );
2274 			}
2275 		/*!
2276 		 * \}
2277 		 */
2278 
2279 		/*!
2280 		 * \name Dealing with priority.
2281 		 * \{
2282 		 */
2283 		/*!
2284 		 * \since
2285 		 * v.5.5.8
2286 		 *
2287 		 * \brief Get the priority of the agent.
2288 		 */
2289 		priority_t
so_priority() const2290 		so_priority() const
2291 			{
2292 				return m_priority;
2293 			}
2294 		/*!
2295 		 * \}
2296 		 */
2297 
2298 	private:
2299 		const state_t st_default{ self_ptr(), "<DEFAULT>" };
2300 
2301 		//! Current agent state.
2302 		const state_t * m_current_state_ptr;
2303 
2304 		/*!
2305 		 * \brief Enumeration of possible agent statuses.
2306 		 *
2307 		 * \since
2308 		 * v.5.5.18
2309 		 */
2310 		enum class agent_status_t : char
2311 		{
2312 			//! Agent is not defined yet.
2313 			//! This is an initial agent status.
2314 			not_defined_yet,
2315 			//! Agent is defined.
2316 			defined,
2317 			//! State switch operation is in progress.
2318 			state_switch_in_progress
2319 		};
2320 
2321 		/*!
2322 		 * \brief Current agent status.
2323 		 *
2324 		 * \since
2325 		 * v.5.5.18
2326 		 */
2327 		agent_status_t m_current_status;
2328 
2329 		//! State listeners controller.
2330 		impl::state_listener_controller_t m_state_listener_controller;
2331 
2332 		/*!
2333 		 * \since
2334 		 * v.5.5.9
2335 		 *
2336 		 * \brief Type of function for searching event handler.
2337 		 */
2338 		using handler_finder_t =
2339 			const impl::event_handler_data_t *(*)(
2340 					execution_demand_t & /* demand */,
2341 					const char * /* context_marker */ );
2342 
2343 		/*!
2344 		 * \since
2345 		 * v.5.5.9
2346 		 *
2347 		 * \brief Function for searching event handler.
2348 		 *
2349 		 * \note The value is set only once in the constructor and
2350 		 * doesn't changed anymore.
2351 		 */
2352 		handler_finder_t m_handler_finder;
2353 
2354 		/*!
2355 		 * \since
2356 		 * v.5.4.0
2357 		 *
2358 		 * \brief All agent's subscriptions.
2359 		 */
2360 		impl::subscription_storage_unique_ptr_t m_subscriptions;
2361 
2362 		/*!
2363 		 * \since
2364 		 * v.5.5.4
2365 		 *
2366 		 * \brief Run-time information for message limits.
2367 		 *
2368 		 * Created only of message limits are described in agent's
2369 		 * tuning options.
2370 		 *
2371 		 * \attention This attribute must be initialized before the
2372 		 * \a m_direct_mbox attribute. It is because the value of
2373 		 * \a m_message_limits is used in \a m_direct_mbox creation.
2374 		 * Because of that \a m_message_limits is declared before
2375 		 * \a m_direct_mbox.
2376 		 */
2377 		std::unique_ptr< message_limit::impl::info_storage_t > m_message_limits;
2378 
2379 		//! SObjectizer Environment for which the agent is belong.
2380 		environment_t & m_env;
2381 
2382 		/*!
2383 		 * \since
2384 		 * v.5.5.8
2385 		 *
2386 		 * \brief Event queue operation protector.
2387 		 *
2388 		 * Initially m_event_queue is NULL. It is changed to actual value
2389 		 * in so_bind_to_dispatcher() method. And reset to nullptr again
2390 		 * in shutdown_agent().
2391 		 *
2392 		 * nullptr in m_event_queue means that methods push_event() and
2393 		 * push_service_request() will throw away any new demand.
2394 		 *
2395 		 * It is necessary to provide guarantee that m_event_queue will
2396 		 * be reset to nullptr in shutdown_agent() only if there is no
2397 		 * working push_event()/push_service_request() methods. To do than
2398 		 * default_rw_spinlock_t is used. Methods push_event() and
2399 		 * push_service_request() acquire it in read-mode and shutdown_agent()
2400 		 * acquires it in write-mode. It means that shutdown_agent() cannot
2401 		 * get access to m_event_queue until there is working
2402 		 * push_event()/push_service_request().
2403 		 */
2404 		default_rw_spinlock_t	m_event_queue_lock;
2405 
2406 		/*!
2407 		 * \since
2408 		 * v.5.5.8
2409 		 *
2410 		 * \brief A pointer to event_queue.
2411 		 *
2412 		 * After binding to the dispatcher is it pointed to the actual
2413 		 * event queue.
2414 		 *
2415 		 * After shutdown it is set to nullptr.
2416 		 *
2417 		 * \attention Access to m_event_queue value must be done only
2418 		 * under acquired m_event_queue_lock.
2419 		 */
2420 		event_queue_t * m_event_queue;
2421 
2422 		/*!
2423 		 * \since
2424 		 * v.5.4.0
2425 		 *
2426 		 * \brief A direct mbox for the agent.
2427 		 */
2428 		const mbox_t m_direct_mbox;
2429 
2430 		/*!
2431 		 * \since
2432 		 * v.5.4.0
2433 		 *
2434 		 * \brief Working thread id.
2435 		 *
2436 		 * Some actions like managing subscriptions and changing states
2437 		 * are enabled only on working thread id.
2438 		 */
2439 		so_5::current_thread_id_t m_working_thread_id;
2440 
2441 		//! Agent is belong to this cooperation.
2442 		coop_t * m_agent_coop;
2443 
2444 		/*!
2445 		 * \since
2446 		 * v.5.5.5
2447 		 *
2448 		 * \brief Delivery filters for that agents.
2449 		 *
2450 		 * \note Storage is created only when necessary.
2451 		 */
2452 		std::unique_ptr< impl::delivery_filter_storage_t > m_delivery_filters;
2453 
2454 		/*!
2455 		 * \since
2456 		 * v.5.5.8
2457 		 *
2458 		 * \brief Priority of the agent.
2459 		 */
2460 		const priority_t m_priority;
2461 
2462 		//! Make an agent reference.
2463 		/*!
2464 		 * This is an internal SObjectizer method. It is called when
2465 		 * it is guaranteed that the agent is still necessary and something
2466 		 * has reference to it.
2467 		 */
2468 		agent_ref_t
2469 		create_ref();
2470 
2471 		/*!
2472 		 * \name Embedding agent into the SObjectizer Run-time.
2473 		 * \{
2474 		 */
2475 
2476 		//! Bind agent to the cooperation.
2477 		/*!
2478 		 * Initializes an internal cooperation pointer.
2479 		 */
2480 		void
2481 		bind_to_coop(
2482 			//! Cooperation for that agent.
2483 			coop_t & coop );
2484 
2485 		//! Agent shutdown deriver.
2486 		/*!
2487 		 * \since
2488 		 * v.5.2.3
2489 		 *
2490 		 *
2491 		 * Method destroys all agent subscriptions.
2492 		 */
2493 		void
2494 		shutdown_agent() noexcept;
2495 		/*!
2496 		 * \}
2497 		 */
2498 
2499 		/*!
2500 		 * \name Subscription/unsubscription implementation details.
2501 		 * \{
2502 		 */
2503 
2504 		/*!
2505 		 * \since
2506 		 * v.5.5.4
2507 		 *
2508 		 * \brief Detect limit for that message type.
2509 		 *
2510 		 * \note
2511 		 * Since v.5.7.1 it isn't a const method.
2512 		 *
2513 		 * \return nullptr if message limits are not used.
2514 		 *
2515 		 * \throw exception_t if message limits are used but the limit
2516 		 * for that message type is not found.
2517 		 */
2518 		const message_limit::control_block_t *
2519 		detect_limit_for_message_type(
2520 			const std::type_index & msg_type );
2521 
2522 		/*!
2523 		 * \since
2524 		 * v.5.2.3
2525 		 *
2526 		 * \brief Remove subscription for the state specified.
2527 		 */
2528 		void
2529 		do_drop_subscription(
2530 			//! Message's mbox.
2531 			const mbox_t & mbox,
2532 			//! Message type.
2533 			const std::type_index & msg_type,
2534 			//! State for event.
2535 			const state_t & target_state );
2536 
2537 		/*!
2538 		 * \since
2539 		 * v.5.2.3
2540 		 *
2541 		 * \brief Remove subscription for all states.
2542 		 */
2543 		void
2544 		do_drop_subscription_for_all_states(
2545 			//! Message's mbox.
2546 			const mbox_t & mbox,
2547 			//! Message type.
2548 			const std::type_index & msg_type );
2549 
2550 		/*!
2551 		 * \brief Check the presence of a subscription.
2552 		 *
2553 		 * \since
2554 		 * v.5.5.19.5
2555 		 */
2556 		bool
2557 		do_check_subscription_presence(
2558 			//! Message's mbox.
2559 			const mbox_t & mbox,
2560 			//! Message type.
2561 			const std::type_index & msg_type,
2562 			//! State for the subscription.
2563 			const state_t & target_state ) const noexcept;
2564 
2565 		/*!
2566 		 * \brief Check the presence of a deadletter handler.
2567 		 *
2568 		 * \since
2569 		 * v.5.5.21
2570 		 */
2571 		bool
2572 		do_check_deadletter_presence(
2573 			//! Message's mbox.
2574 			const mbox_t & mbox,
2575 			//! Message type.
2576 			const std::type_index & msg_type ) const noexcept;
2577 		/*!
2578 		 * \}
2579 		 */
2580 
2581 
2582 		/*!
2583 		 * \name Event handling implementation details.
2584 		 * \{
2585 		 */
2586 
2587 		//! Push event into the event queue.
2588 		void
2589 		push_event(
2590 			//! Optional message limit.
2591 			const message_limit::control_block_t * limit,
2592 			//! ID of mbox for this event.
2593 			mbox_id_t mbox_id,
2594 			//! Message type for event.
2595 			std::type_index msg_type,
2596 			//! Event message.
2597 			const message_ref_t & message );
2598 		/*!
2599 		 * \}
2600 		 */
2601 
2602 		// NOTE: demand handlers declared as public to allow
2603 		// access this handlers from unit-tests.
2604 	public :
2605 		/*!
2606 		 * \name Demand handlers.
2607 		 * \{
2608 		 */
2609 		/*!
2610 		 * \since
2611 		 * v.5.2.0
2612 		 *
2613 		 * \brief Calls so_evt_start method for agent.
2614 		 */
2615 		static void
2616 		demand_handler_on_start(
2617 			current_thread_id_t working_thread_id,
2618 			execution_demand_t & d );
2619 
2620 		/*!
2621 		 * \since
2622 		 * v.5.5.8
2623 		 *
2624 		 * \brief Ensures that all agents from cooperation are
2625 		 * bound to dispatchers.
2626 		 */
2627 		void
2628 		ensure_binding_finished();
2629 
2630 		/*!
2631 		 * \since
2632 		 * v.5.4.0
2633 		 *
2634 		 * \note This method is necessary for GCC on Cygwin.
2635 		 */
2636 		static demand_handler_pfn_t
2637 		get_demand_handler_on_start_ptr() noexcept;
2638 
2639 		/*!
2640 		 * \since
2641 		 * v.5.2.0
2642 		 *
2643 		 * \brief Calls so_evt_finish method for agent.
2644 		 */
2645 		static void
2646 		demand_handler_on_finish(
2647 			current_thread_id_t working_thread_id,
2648 			execution_demand_t & d );
2649 
2650 		/*!
2651 		 * \since
2652 		 * v.5.4.0
2653 		 *
2654 		 * \note This method is necessary for GCC on Cygwin.
2655 		 */
2656 		static demand_handler_pfn_t
2657 		get_demand_handler_on_finish_ptr() noexcept;
2658 
2659 		/*!
2660 		 * \since
2661 		 * v.5.2.0
2662 		 *
2663 		 * \brief Calls event handler for message.
2664 		 */
2665 		static void
2666 		demand_handler_on_message(
2667 			current_thread_id_t working_thread_id,
2668 			execution_demand_t & d );
2669 
2670 		/*!
2671 		 * \since
2672 		 * v.5.4.0
2673 		 *
2674 		 * \note This method is necessary for GCC on Cygwin.
2675 		 */
2676 		static demand_handler_pfn_t
2677 		get_demand_handler_on_message_ptr() noexcept;
2678 
2679 		/*!
2680 		 * \since
2681 		 * v.5.5.23
2682 		 *
2683 		 * \brief Handles the enveloped message.
2684 		 */
2685 		static void
2686 		demand_handler_on_enveloped_msg(
2687 			current_thread_id_t working_thread_id,
2688 			execution_demand_t & d );
2689 
2690 		/*!
2691 		 * \since
2692 		 * v.5.5.24
2693 		 */
2694 		static demand_handler_pfn_t
2695 		get_demand_handler_on_enveloped_msg_ptr() noexcept;
2696 		/*!
2697 		 * \}
2698 		 */
2699 
2700 	private :
2701 		/*!
2702 		 * \since
2703 		 * v.5.4.0
2704 		 *
2705 		 * \brief Actual implementation of message handling.
2706 		 *
2707 		 * \note Since v.5.5.17.1 argument \a method is passed as copy.
2708 		 * It prevents deallocation of event_handler_method in the following
2709 		 * case:
2710 		 * \code
2711 		 	auto mbox = so_environment().create_mbox();
2712 			so_subscribe( mbox ).event< some_signal >( [this, mbox] {
2713 				so_drop_subscription< some_signal >( mbox );
2714 				... // Some other actions.
2715 			} );
2716 		 * \endcode
2717 		 */
2718 		static void
2719 		process_message(
2720 			current_thread_id_t working_thread_id,
2721 			execution_demand_t & d,
2722 			event_handler_method_t method );
2723 
2724 		/*!
2725 		 * \brief Actual implementation of enveloped message handling.
2726 		 *
2727 		 * \note
2728 		 * handler_data can be nullptr. It means that an event handler
2729 		 * for that message type if not found and special hook will
2730 		 * be called for the envelope.
2731 		 *
2732 		 * \since
2733 		 * v.5.5.23
2734 		 */
2735 		static void
2736 		process_enveloped_msg(
2737 			current_thread_id_t working_thread_id,
2738 			execution_demand_t & d,
2739 			const impl::event_handler_data_t * handler_data );
2740 
2741 		/*!
2742 		 * \since
2743 		 * v.5.4.0
2744 		 *
2745 		 * \brief Enables operation only if it is performed on agent's
2746 		 * working thread.
2747 		 */
2748 		void
2749 		ensure_operation_is_on_working_thread(
2750 			const char * operation_name ) const;
2751 
2752 		/*!
2753 		 * \since
2754 		 * v.5.5.0
2755 		 *
2756 		 * \brief Drops all delivery filters.
2757 		 */
2758 		void
2759 		drop_all_delivery_filters() noexcept;
2760 
2761 		/*!
2762 		 * \since
2763 		 * v.5.5.5
2764 		 *
2765 		 * \brief Set a delivery filter.
2766 		 */
2767 		void
2768 		do_set_delivery_filter(
2769 			const mbox_t & mbox,
2770 			const std::type_index & msg_type,
2771 			delivery_filter_unique_ptr_t filter );
2772 
2773 		/*!
2774 		 * \since
2775 		 * v.5.5.5
2776 		 *
2777 		 * \brief Drop a delivery filter.
2778 		 */
2779 		void
2780 		do_drop_delivery_filter(
2781 			const mbox_t & mbox,
2782 			const std::type_index & msg_type ) noexcept;
2783 
2784 		/*!
2785 		 * \since
2786 		 * v.5.5.9
2787 		 *
2788 		 * \brief Handler finder for the case when message delivery
2789 		 * tracing is disabled.
2790 		 */
2791 		static const impl::event_handler_data_t *
2792 		handler_finder_msg_tracing_disabled(
2793 			execution_demand_t & demand,
2794 			const char * context_marker );
2795 
2796 		/*!
2797 		 * \since
2798 		 * v.5.5.9
2799 		 *
2800 		 * \brief Handler finder for the case when message delivery
2801 		 * tracing is enabled.
2802 		 */
2803 		static const impl::event_handler_data_t *
2804 		handler_finder_msg_tracing_enabled(
2805 			execution_demand_t & demand,
2806 			const char * context_marker );
2807 
2808 		/*!
2809 		 * \since
2810 		 * v.5.5.15
2811 		 *
2812 		 * \brief Actual search for event handler with respect
2813 		 * to parent-child relationship between agent states.
2814 		 */
2815 		static const impl::event_handler_data_t *
2816 		find_event_handler_for_current_state(
2817 			execution_demand_t & demand );
2818 
2819 		/*!
2820 		 * \brief Search for event handler between deadletter handlers.
2821 		 *
2822 		 * \return nullptr if event handler is not found.
2823 		 *
2824 		 * \since
2825 		 * v.5.5.21
2826 		 */
2827 		static const impl::event_handler_data_t *
2828 		find_deadletter_handler(
2829 			execution_demand_t & demand );
2830 
2831 		/*!
2832 		 * \since
2833 		 * v.5.5.15
2834 		 *
2835 		 * \brief Actual action for switching agent state.
2836 		 */
2837 		void
2838 		do_state_switch(
2839 			//! New state to be set as the current state.
2840 			const state_t & state_to_be_set ) noexcept;
2841 
2842 		/*!
2843 		 * \since
2844 		 * v.5.5.15
2845 		 *
2846 		 * \brief Return agent to the default state.
2847 		 *
2848 		 * \note This method is called just before invocation of
2849 		 * so_evt_finish() to return agent to the default state.
2850 		 * This return will initiate invocation of on_exit handlers
2851 		 * for all active states of the agent.
2852 		 *
2853 		 * \attention State switch is not performed is agent is already
2854 		 * in default state or if it waits deregistration after unhandled
2855 		 * exception.
2856 		 */
2857 		void
2858 		return_to_default_state_if_possible() noexcept;
2859 };
2860 
2861 /*!
2862  * \brief Helper function template for the creation of smart pointer
2863  * to an agent.
2864  *
2865  * This function can be useful if a pointer to an agent should be passed
2866  * somewhere with the guarantee that this pointer will remain valid even
2867  * if the agent will be deregistered.
2868  *
2869  * This could be necessary, for example, if a pointer to an agent is
2870  * passed to some callback (like it is done in Asio):
2871  * \code
2872  * void my_agent::on_some_event(mhood_t<some_msg> cmd) {
2873  * 	connection_.async_read_some(input_buffer_,
2874  * 		[self = so_5::make_agent_ref(this)](
2875  * 			const asio::error_code & ec,
2876  * 			std::size_t bytes_transferred )
2877  * 		{
2878  * 			if(!ec)
2879  * 				self->handle_incoming_data(bytes_transferred);
2880  * 		}
2881  * 	);
2882  * }
2883  * \endcode
2884  *
2885  * \since
2886  * v.5.7.1
2887  */
2888 template< typename Derived >
2889 [[nodiscard]]
2890 intrusive_ptr_t< Derived >
make_agent_ref(Derived * agent)2891 make_agent_ref( Derived * agent )
2892 	{
2893 		static_assert( std::is_base_of_v< agent_t, Derived >,
2894 				"type should be derived from so_5::agent_t" );
2895 
2896 		return { agent };
2897 	}
2898 
2899 /*!
2900  * \since
2901  * v.5.5.5
2902  *
2903  * \brief Template-based implementations of delivery filters.
2904  */
2905 namespace delivery_filter_templates
2906 {
2907 
2908 /*!
2909  * \since
2910  * v.5.5.5
2911  *
2912  * \brief An implementation of delivery filter represented by lambda-function
2913  * like object.
2914  *
2915  * \tparam Lambda type of lambda-function or functional object.
2916  */
2917 template< typename Lambda, typename Message >
2918 class lambda_as_filter_t : public delivery_filter_t
2919 	{
2920 		Lambda m_filter;
2921 
2922 	public :
lambda_as_filter_t(Lambda && filter)2923 		lambda_as_filter_t( Lambda && filter )
2924 			:	m_filter( std::forward< Lambda >( filter ) )
2925 			{}
2926 
2927 		bool
check(const agent_t &,message_t & msg) const2928 		check(
2929 			const agent_t & /*receiver*/,
2930 			message_t & msg ) const noexcept override
2931 			{
2932 				return m_filter(message_payload_type< Message >::payload_reference( msg ));
2933 			}
2934 	};
2935 
2936 } /* namespace delivery_filter_templates */
2937 
2938 template< typename Lambda >
2939 void
so_set_delivery_filter(const mbox_t & mbox,Lambda && lambda)2940 agent_t::so_set_delivery_filter(
2941 	const mbox_t & mbox,
2942 	Lambda && lambda )
2943 	{
2944 		using namespace so_5::details::lambda_traits;
2945 		using namespace delivery_filter_templates;
2946 
2947 		using argument_type = typename argument_type_if_lambda< Lambda >::type;
2948 
2949 		ensure_not_signal< argument_type >();
2950 
2951 		do_set_delivery_filter(
2952 				mbox,
2953 				message_payload_type< argument_type >::subscription_type_index(),
2954 				delivery_filter_unique_ptr_t{
2955 					new lambda_as_filter_t< Lambda, argument_type >(
2956 							std::move( lambda ) )
2957 				} );
2958 	}
2959 
2960 //
2961 // subscription_bind_t implementation
2962 //
2963 inline
subscription_bind_t(agent_t & agent,const mbox_t & mbox_ref)2964 subscription_bind_t::subscription_bind_t(
2965 	agent_t & agent,
2966 	const mbox_t & mbox_ref )
2967 	:	m_agent( &agent )
2968 	,	m_mbox_ref( mbox_ref )
2969 {
2970 }
2971 
2972 inline subscription_bind_t &
in(const state_t & state)2973 subscription_bind_t::in(
2974 	const state_t & state )
2975 {
2976 	if( !state.is_target( m_agent ) )
2977 	{
2978 		SO_5_THROW_EXCEPTION(
2979 			rc_agent_is_not_the_state_owner,
2980 			"agent doesn't own the state" );
2981 	}
2982 
2983 	m_states.push_back( &state );
2984 
2985 	return *this;
2986 }
2987 
2988 template< typename Method_Pointer >
2989 typename std::enable_if<
2990 		details::is_agent_method_pointer<
2991 				details::method_arity::unary,
2992 				Method_Pointer>::value,
2993 		subscription_bind_t & >::type
event(Method_Pointer pfn,thread_safety_t thread_safety)2994 subscription_bind_t::event(
2995 	Method_Pointer pfn,
2996 	thread_safety_t thread_safety )
2997 {
2998 	using namespace details::event_subscription_helpers;
2999 
3000 	const auto ev = preprocess_agent_event_handler( m_mbox_ref, *m_agent, pfn );
3001 	create_subscription_for_states(
3002 			ev.m_msg_type,
3003 			ev.m_handler,
3004 			thread_safety,
3005 			event_handler_kind_t::final_handler );
3006 
3007 	return *this;
3008 }
3009 
3010 template<typename Lambda>
3011 typename std::enable_if<
3012 		details::lambda_traits::is_lambda<Lambda>::value,
3013 		subscription_bind_t & >::type
event(Lambda && lambda,thread_safety_t thread_safety)3014 subscription_bind_t::event(
3015 	Lambda && lambda,
3016 	thread_safety_t thread_safety )
3017 {
3018 	using namespace details::event_subscription_helpers;
3019 
3020 	const auto ev = preprocess_agent_event_handler(
3021 			m_mbox_ref,
3022 			*m_agent,
3023 			std::forward<Lambda>(lambda) );
3024 
3025 	create_subscription_for_states(
3026 			ev.m_msg_type,
3027 			ev.m_handler,
3028 			thread_safety,
3029 			event_handler_kind_t::final_handler );
3030 
3031 	return *this;
3032 }
3033 
3034 template< typename Msg >
3035 subscription_bind_t &
transfer_to_state(const state_t & target_state)3036 subscription_bind_t::transfer_to_state(
3037 	const state_t & target_state )
3038 {
3039 	/*
3040 	 * Note. Since v.5.5.22.1 there is a new implementation of transfer_to_state.
3041 	 * New implementation protects from loops in transfer_to_state calls.
3042 	 * For example in the following cases:
3043 	 *
3044 	 * \code
3045 		class a_simple_case_t final : public so_5::agent_t
3046 		{
3047 			state_t st_base{ this, "base" };
3048 			state_t st_disconnected{ initial_substate_of{st_base}, "disconnected" };
3049 			state_t st_connected{ substate_of{st_base}, "connected" };
3050 
3051 			struct message {};
3052 
3053 		public :
3054 			a_simple_case_t(context_t ctx) : so_5::agent_t{ctx} {
3055 				this >>= st_base;
3056 
3057 				st_base.transfer_to_state<message>(st_disconnected);
3058 			}
3059 
3060 			virtual void so_evt_start() override {
3061 				so_5::send<message>(*this);
3062 			}
3063 		};
3064 
3065 		class a_two_state_loop_t final : public so_5::agent_t
3066 		{
3067 			state_t st_one{ this, "one" };
3068 			state_t st_two{ this, "two" };
3069 
3070 			struct message {};
3071 
3072 		public :
3073 			a_two_state_loop_t(context_t ctx) : so_5::agent_t{ctx} {
3074 				this >>= st_one;
3075 
3076 				st_one.transfer_to_state<message>(st_two);
3077 				st_two.transfer_to_state<message>(st_one);
3078 			}
3079 
3080 			virtual void so_evt_start() override {
3081 				so_5::send<message>(*this);
3082 			}
3083 		};
3084 	 * \endcode
3085 	 *
3086 	 * For such protection an additional objects with the current state
3087 	 * of transfer_to_state operation is necessary. There will be a boolean
3088 	 * flag in that state. When transfer_to_state will be started this
3089 	 * flag will should be 'false'. But if it is already 'true' then there is
3090 	 * a loop in transfer_to_state calls.
3091 	 */
3092 
3093 	// This is the state of transfer_to_state operation.
3094 	struct transfer_op_state_t
3095 	{
3096 		agent_t * m_agent;
3097 		mbox_id_t m_mbox_id;
3098 		const state_t & m_target_state;
3099 		bool m_in_progress;
3100 
3101 		transfer_op_state_t(
3102 			agent_t * agent,
3103 			mbox_id_t mbox_id,
3104 			outliving_reference_t<const state_t> tgt_state)
3105 			:	m_agent( agent )
3106 			,	m_mbox_id( mbox_id )
3107 			,	m_target_state( tgt_state.get() )
3108 			,	m_in_progress( false )
3109 		{}
3110 	};
3111 
3112 	//NOTE: shared_ptr is used because capture of unique_ptr
3113 	//makes std::function non-copyable, but we need to copy
3114 	//resulting 'method' object.
3115 	//
3116 	auto op_state = std::make_shared< transfer_op_state_t >(
3117 			m_agent, m_mbox_ref->id(), outliving_const(target_state) );
3118 
3119 	auto method = [op_state]( message_ref_t & msg )
3120 		{
3121 			// The current transfer_to_state operation should be inactive.
3122 			if( op_state->m_in_progress )
3123 				SO_5_THROW_EXCEPTION( rc_transfer_to_state_loop,
3124 						"transfer_to_state loop detected. target_state: " +
3125 						op_state->m_target_state.query_name() +
3126 						", current_state: " +
3127 						op_state->m_agent->so_current_state().query_name() );
3128 
3129 			// Activate transfer_to_state operation and make sure that it
3130 			// will be deactivated on return automatically.
3131 			op_state->m_in_progress = true;
3132 			auto in_progress_reset = details::at_scope_exit( [&op_state] {
3133 					op_state->m_in_progress = false;
3134 				} );
3135 
3136 			//
3137 			// The main logic of transfer_to_state operation.
3138 			//
3139 			op_state->m_agent->so_change_state( op_state->m_target_state );
3140 
3141 			execution_demand_t demand{
3142 					op_state->m_agent,
3143 					nullptr, // Message limit is not actual here.
3144 					op_state->m_mbox_id,
3145 					typeid( Msg ),
3146 					msg,
3147 					// We have very simple choice here: message is an enveloped
3148 					// message or just classical message/signal.
3149 					// So we should select an appropriate demand handler.
3150 					message_kind_t::enveloped_msg == message_kind( msg ) ?
3151 							agent_t::get_demand_handler_on_enveloped_msg_ptr() :
3152 							agent_t::get_demand_handler_on_message_ptr()
3153 			};
3154 
3155 			demand.call_handler( query_current_thread_id() );
3156 		};
3157 
3158 	create_subscription_for_states(
3159 			typeid( Msg ),
3160 			method,
3161 			thread_safety_t::unsafe,
3162 			event_handler_kind_t::intermediate_handler );
3163 
3164 	return *this;
3165 }
3166 
3167 template< typename Msg >
3168 subscription_bind_t &
suppress()3169 subscription_bind_t::suppress()
3170 {
3171 	// A method with nothing inside.
3172 	auto method = []( message_ref_t & ) {};
3173 
3174 	create_subscription_for_states(
3175 			typeid( Msg ),
3176 			method,
3177 			thread_safety_t::safe,
3178 			// Suppression of a message is a kind of ignoring of the message.
3179 			// In the case of enveloped message intermediate_handler receives
3180 			// the whole envelope (not the payload) and the whole envelope
3181 			// should be ignored. We can't specify final_handler here because
3182 			// in that case the payload of an enveloped message will be
3183 			// extracted from the envelope and envelope will be informed about
3184 			// the handling of message. But message won't be handled, it will
3185 			// be ignored.
3186 			event_handler_kind_t::intermediate_handler );
3187 
3188 	return *this;
3189 }
3190 
3191 template< typename Msg >
3192 subscription_bind_t &
just_switch_to(const state_t & target_state)3193 subscription_bind_t::just_switch_to(
3194 	const state_t & target_state )
3195 {
3196 	agent_t * agent_ptr = m_agent;
3197 
3198 	auto method = [agent_ptr, &target_state]( message_ref_t & )
3199 		{
3200 			agent_ptr->so_change_state( target_state );
3201 		};
3202 
3203 	create_subscription_for_states(
3204 			typeid( Msg ),
3205 			method,
3206 			thread_safety_t::unsafe,
3207 			// Switching to some state is a kind of message processing.
3208 			// So if there is an enveloped message then the envelope will be
3209 			// informed about the processing of the payload.
3210 			event_handler_kind_t::final_handler );
3211 
3212 	return *this;
3213 }
3214 
3215 inline 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) const3216 subscription_bind_t::create_subscription_for_states(
3217 	const std::type_index & msg_type,
3218 	const event_handler_method_t & method,
3219 	thread_safety_t thread_safety,
3220 	event_handler_kind_t handler_kind ) const
3221 {
3222 	if( m_states.empty() )
3223 		// Agent should be subscribed only in default state.
3224 		m_agent->so_create_event_subscription(
3225 			m_mbox_ref,
3226 			msg_type,
3227 			m_agent->so_default_state(),
3228 			method,
3229 			thread_safety,
3230 			handler_kind );
3231 	else
3232 		for( auto s : m_states )
3233 			m_agent->so_create_event_subscription(
3234 					m_mbox_ref,
3235 					msg_type,
3236 					*s,
3237 					method,
3238 					thread_safety,
3239 					handler_kind );
3240 }
3241 
3242 inline void
ensure_handler_can_be_used_with_mbox(const so_5::details::msg_type_and_handler_pair_t & handler) const3243 subscription_bind_t::ensure_handler_can_be_used_with_mbox(
3244 	const so_5::details::msg_type_and_handler_pair_t & handler ) const
3245 {
3246 	::so_5::details::event_subscription_helpers::ensure_handler_can_be_used_with_mbox(
3247 			handler,
3248 			m_mbox_ref );
3249 }
3250 
3251 /*
3252  * Implementation of template methods of state_t class.
3253  */
3254 inline bool
is_active() const3255 state_t::is_active() const noexcept
3256 {
3257 	return m_target_agent->so_is_active_state( *this );
3258 }
3259 
3260 template< typename... Args >
3261 const state_t &
event(Args &&...args) const3262 state_t::event( Args&&... args ) const
3263 {
3264 	return this->subscribe_message_handler(
3265 			m_target_agent->so_direct_mbox(),
3266 			std::forward< Args >(args)... );
3267 }
3268 
3269 template< typename... Args >
3270 const state_t &
event(mbox_t from,Args &&...args) const3271 state_t::event( mbox_t from, Args&&... args ) const
3272 {
3273 	return this->subscribe_message_handler( from,
3274 			std::forward< Args >(args)... );
3275 }
3276 
3277 template< typename Msg >
3278 bool
has_subscription(const mbox_t & from) const3279 state_t::has_subscription( const mbox_t & from ) const
3280 {
3281 	return m_target_agent->so_has_subscription< Msg >( from, *this );
3282 }
3283 
3284 template< typename Method_Pointer >
3285 bool
has_subscription(const mbox_t & from,Method_Pointer && pfn) const3286 state_t::has_subscription(
3287 	const mbox_t & from,
3288 	Method_Pointer && pfn ) const
3289 {
3290 	return m_target_agent->so_has_subscription(
3291 			from,
3292 			*this,
3293 			std::forward<Method_Pointer>(pfn) );
3294 }
3295 
3296 template< typename Msg >
3297 void
drop_subscription(const mbox_t & from) const3298 state_t::drop_subscription( const mbox_t & from ) const
3299 {
3300 	m_target_agent->so_drop_subscription< Msg >( from, *this );
3301 }
3302 
3303 template< typename Method_Pointer >
3304 void
drop_subscription(const mbox_t & from,Method_Pointer && pfn) const3305 state_t::drop_subscription(
3306 	const mbox_t & from,
3307 	Method_Pointer && pfn ) const
3308 {
3309 	m_target_agent->so_drop_subscription(
3310 			from,
3311 			*this,
3312 			std::forward<Method_Pointer>(pfn) );
3313 }
3314 
3315 template< typename Msg >
3316 const state_t &
transfer_to_state(mbox_t from,const state_t & target_state) const3317 state_t::transfer_to_state( mbox_t from, const state_t & target_state ) const
3318 {
3319 	m_target_agent->so_subscribe( from )
3320 			.in( *this )
3321 			.transfer_to_state< Msg >( target_state );
3322 
3323 	return *this;
3324 }
3325 
3326 template< typename Msg >
3327 const state_t &
transfer_to_state(const state_t & target_state) const3328 state_t::transfer_to_state( const state_t & target_state ) const
3329 {
3330 	return this->transfer_to_state< Msg >(
3331 			m_target_agent->so_direct_mbox(),
3332 			target_state );
3333 }
3334 
3335 template< typename Msg >
3336 const state_t &
just_switch_to(mbox_t from,const state_t & target_state) const3337 state_t::just_switch_to( mbox_t from, const state_t & target_state ) const
3338 {
3339 	m_target_agent->so_subscribe( from )
3340 			.in( *this )
3341 			.just_switch_to< Msg >( target_state );
3342 
3343 	return *this;
3344 }
3345 
3346 template< typename Msg >
3347 const state_t &
just_switch_to(const state_t & target_state) const3348 state_t::just_switch_to( const state_t & target_state ) const
3349 {
3350 	return this->just_switch_to< Msg >(
3351 			m_target_agent->so_direct_mbox(),
3352 			target_state );
3353 }
3354 
3355 template< typename Msg >
3356 const state_t &
suppress() const3357 state_t::suppress() const
3358 {
3359 	return this->suppress< Msg >( m_target_agent->so_direct_mbox() );
3360 }
3361 
3362 template< typename Msg >
3363 const state_t &
suppress(mbox_t from) const3364 state_t::suppress( mbox_t from ) const
3365 {
3366 	m_target_agent->so_subscribe( from )
3367 			.in( *this )
3368 			.suppress< Msg >();
3369 
3370 	return *this;
3371 }
3372 
3373 template< typename Method_Pointer >
3374 typename std::enable_if<
3375 		details::is_agent_method_pointer<
3376 				details::method_arity::nullary,
3377 				Method_Pointer>::value,
3378 		state_t & >::type
on_enter(Method_Pointer pfn)3379 state_t::on_enter( Method_Pointer pfn )
3380 {
3381 	using namespace details::event_subscription_helpers;
3382 
3383 	using pfn_traits = details::is_agent_method_pointer<
3384 			details::method_arity::nullary, Method_Pointer>;
3385 
3386 	// Agent must have right type.
3387 	auto cast_result =
3388 			get_actual_agent_pointer<
3389 					typename pfn_traits::agent_type >(
3390 			*m_target_agent );
3391 
3392 	return this->on_enter( [cast_result, pfn]() { (cast_result->*pfn)(); } );
3393 }
3394 
3395 template< typename Method_Pointer >
3396 typename std::enable_if<
3397 		details::is_agent_method_pointer<
3398 				details::method_arity::nullary,
3399 				Method_Pointer>::value,
3400 		state_t & >::type
on_exit(Method_Pointer pfn)3401 state_t::on_exit( Method_Pointer pfn )
3402 {
3403 	using namespace details::event_subscription_helpers;
3404 
3405 	using pfn_traits = details::is_agent_method_pointer<
3406 			details::method_arity::nullary, Method_Pointer>;
3407 
3408 	// Agent must have right type.
3409 	auto cast_result =
3410 			get_actual_agent_pointer<
3411 					typename pfn_traits::agent_type >(
3412 			*m_target_agent );
3413 
3414 	return this->on_exit( [cast_result, pfn]() { (cast_result->*pfn)(); } );
3415 }
3416 
3417 template< typename... Args >
3418 const state_t &
subscribe_message_handler(const mbox_t & from,Args &&...args) const3419 state_t::subscribe_message_handler(
3420 	const mbox_t & from,
3421 	Args&&... args ) const
3422 {
3423 	m_target_agent->so_subscribe( from ).in( *this )
3424 			.event( std::forward< Args >(args)... );
3425 
3426 	return *this;
3427 }
3428 
3429 /*!
3430  * \since
3431  * v.5.5.1
3432  *
3433  * \brief A shortcat for switching the agent state.
3434  *
3435  * \par Usage example.
3436 	\code
3437 	class my_agent : public so_5::agent_t
3438 	{
3439 		const so_5::state_t st_normal = so_make_state();
3440 		const so_5::state_t st_error = so_make_state();
3441 		...
3442 	public :
3443 		virtual void so_define_agent() override
3444 		{
3445 			this >>= st_normal;
3446 
3447 			st_normal.handle( [=]( const msg_failure & evt ) {
3448 				this >>= st_error;
3449 				...
3450 			});
3451 			...
3452 		};
3453 		...
3454 	};
3455 	\endcode
3456  */
3457 inline void
operator >>=(agent_t * agent,const state_t & new_state)3458 operator>>=( agent_t * agent, const state_t & new_state )
3459 {
3460 	agent->so_change_state( new_state );
3461 }
3462 
3463 } /* namespace so_5 */
3464 
3465 #if defined( SO_5_MSVC )
3466 	#pragma warning(pop)
3467 #endif
3468 
3469