1 /*
2 	SObjectizer 5.
3 */
4 
5 /*!
6 	\file
7 	\brief SObjectizer Environment definition.
8 */
9 
10 #pragma once
11 
12 #include <functional>
13 #include <chrono>
14 #include <memory>
15 #include <type_traits>
16 
17 #include <so_5/compiler_features.hpp>
18 #include <so_5/declspec.hpp>
19 #include <so_5/exception.hpp>
20 #include <so_5/error_logger.hpp>
21 #include <so_5/compiler_features.hpp>
22 #include <so_5/msg_tracing.hpp>
23 
24 #include <so_5/custom_mbox.hpp>
25 
26 #include <so_5/stop_guard.hpp>
27 
28 #include <so_5/nonempty_name.hpp>
29 #include <so_5/mbox.hpp>
30 #include <so_5/mchain.hpp>
31 #include <so_5/message.hpp>
32 #include <so_5/coop.hpp>
33 #include <so_5/disp_binder.hpp>
34 #include <so_5/so_layer.hpp>
35 #include <so_5/coop_listener.hpp>
36 #include <so_5/event_exception_logger.hpp>
37 #include <so_5/event_queue_hook.hpp>
38 
39 #include <so_5/timers.hpp>
40 
41 #include <so_5/stats/controller.hpp>
42 #include <so_5/stats/repository.hpp>
43 
44 #include <so_5/queue_locks_defaults_manager.hpp>
45 
46 #include <so_5/environment_infrastructure.hpp>
47 
48 #include <so_5/disp/one_thread/params.hpp>
49 
50 
51 #if defined( SO_5_MSVC )
52 	#pragma warning(push)
53 	#pragma warning(disable: 4251)
54 #endif
55 
56 namespace so_5
57 {
58 
59 namespace low_level_api
60 {
61 
62 struct schedule_timer_params_t
63 	{
64 		//! Message type.
65 		const std::type_index & m_msg_type;
66 		//! Message to be sent after timeout.
67 		const message_ref_t & m_msg;
68 		//! Mbox to which message will be delivered.
69 		const mbox_t & m_mbox;
70 		//! Timeout before the first delivery.
71 		std::chrono::steady_clock::duration m_pause;
72 		//! Period of the delivery repetition for periodic messages.
73 		std::chrono::steady_clock::duration m_period;
74 	};
75 
76 struct single_timer_params_t
77 	{
78 		//! Message type.
79 		const std::type_index & m_msg_type;
80 		//! Message to be sent after timeout.
81 		const message_ref_t & m_msg;
82 		//! Mbox to which message will be delivered.
83 		const mbox_t & m_mbox;
84 		//! Timeout before the delivery.
85 		std::chrono::steady_clock::duration m_pause;
86 	};
87 
88 } /* namespace low_level_api */
89 
90 //
91 // environment_params_t
92 //
93 
94 //! Parameters for the SObjectizer Environment initialization.
95 /*!
96  * This class is used for setting SObjectizer Parameters.
97  *
98  * \see http://www.parashift.com/c++-faq/named-parameter-idiom.html
99  */
100 class SO_5_TYPE environment_params_t
101 {
102 	public:
103 		/*!
104 		 * \brief Constructor.
105 		 *
106 		 * Sets default values for parameters.
107 		 */
108 		environment_params_t();
109 		/*!
110 		 * \brief Move constructor.
111 		 *
112 		 * \since
113 		 * v.5.2.3
114 		 */
115 		environment_params_t( environment_params_t && other );
116 		~environment_params_t();
117 
118 		/*!
119 		 * \brief Move operator.
120 		 *
121 		 * \since
122 		 * v.5.2.3
123 		 */
124 		environment_params_t &
125 		operator=( environment_params_t && other );
126 
127 		/*!
128 		 * \brief Swap operation.
129 		 *
130 		 * \since
131 		 * v.5.2.3
132 		 */
133 		friend SO_5_FUNC void
134 		swap( environment_params_t & a, environment_params_t & b );
135 
136 		//! Set the timer_thread factory.
137 		/*!
138 		 * If \a factory is a null then the default timer thread
139 		 * will be used.
140 		 */
141 		environment_params_t &
142 		timer_thread(
143 			//! timer_thread factory to be used.
144 			so_5::timer_thread_factory_t factory );
145 
146 		//! Add an additional layer to the SObjectizer Environment.
147 		/*!
148 		 * If this layer is already added it will be replaced by \a layer_ptr.
149 		 *
150 		 * The method distinguishes layers from each other by the type SO_LAYER.
151 		*/
152 		template< class SO_Layer >
153 		environment_params_t &
add_layer(std::unique_ptr<SO_Layer> layer_ptr)154 		add_layer(
155 			//! A layer to be added.
156 			std::unique_ptr< SO_Layer > layer_ptr )
157 		{
158 			if( layer_ptr.get() )
159 			{
160 				layer_unique_ptr_t ptr( layer_ptr.release() );
161 
162 				add_layer(
163 					std::type_index( typeid( SO_Layer ) ),
164 					std::move( ptr ) );
165 			}
166 
167 			return *this;
168 		}
169 
170 		//! Set cooperation listener object.
171 		environment_params_t &
172 		coop_listener(
173 			coop_listener_unique_ptr_t coop_listener );
174 
175 		//! Set exception logger object.
176 		environment_params_t &
177 		event_exception_logger(
178 			event_exception_logger_unique_ptr_t logger );
179 
180 		/*!
181 		 * \name Exception reaction flag management methods.
182 		 * \{
183 		 */
184 		/*!
185 		 * \since
186 		 * v.5.3.0
187 		 * \brief Get exception reaction flag value.
188 		 */
189 		inline exception_reaction_t
exception_reaction() const190 		exception_reaction() const
191 		{
192 			return m_exception_reaction;
193 		}
194 
195 		/*!
196 		 * \since
197 		 * v.5.3.0
198 		 * \brief Set exception reaction flag value.
199 		 */
200 		environment_params_t &
exception_reaction(exception_reaction_t value)201 		exception_reaction( exception_reaction_t value )
202 		{
203 			m_exception_reaction = value;
204 			return *this;
205 		}
206 		/*!
207 		 * \}
208 		 */
209 
210 		/*!
211 		 * \since
212 		 * v.5.4.0
213 		 * \brief Do not shutdown SO Environment when it is becomes empty.
214 		 *
215 		 * \par Description
216 		 * Since v.5.4.0 SO Environment checks count of live cooperations
217 		 * after every cooperation deregistration. If there is no more
218 		 * live cooperations then SO Environment will be shutted down.
219 		 * If it is not appropriate then this method must be called.
220 		 * It disables autoshutdown of SO Environment. Event if there is
221 		 * no more live cooperations SO Environment will work until
222 		 * explisit call to environment_t::stop() method.
223 		 */
224 		environment_params_t &
disable_autoshutdown()225 		disable_autoshutdown()
226 		{
227 			m_autoshutdown_disabled = true;
228 			return *this;
229 		}
230 
231 		/*!
232 		 * \since
233 		 * v.5.4.0
234 		 * \brief Is autoshutdown disabled?
235 		 *
236 		 * \see disable_autoshutdown()
237 		 */
238 		[[nodiscard]]
239 		bool
autoshutdown_disabled() const240 		autoshutdown_disabled() const
241 		{
242 			return m_autoshutdown_disabled;
243 		}
244 
245 		/*!
246 		 * \since
247 		 * v.5.5.0
248 		 * \brief Set error logger for the environment.
249 		 */
250 		environment_params_t &
error_logger(error_logger_shptr_t logger)251 		error_logger( error_logger_shptr_t logger )
252 		{
253 			m_error_logger = std::move(logger);
254 			return *this;
255 		}
256 
257 		/*!
258 		 * \brief Set message delivery tracer for the environment.
259 		 *
260 		 * Usage example:
261 		 * \code
262 		 * so_5::launch( [](so_5::environment_t & env) { ... },
263 		 * 	[](so_5::environment_params_t & params) {
264 		 * 		params.message_delivery_tracer( so_5::msg_tracing::std_cout_tracer() );
265 		 * 		...
266 		 * 	} );
267 		 * \endcode
268 		 *
269 		 * \since
270 		 * v.5.5.9
271 		 */
272 		environment_params_t &
message_delivery_tracer(so_5::msg_tracing::tracer_unique_ptr_t tracer)273 		message_delivery_tracer( so_5::msg_tracing::tracer_unique_ptr_t tracer )
274 		{
275 			m_message_delivery_tracer = std::move( tracer );
276 			return *this;
277 		}
278 
279 		/*!
280 		 * \brief Set message tracer filter for the environment.
281 		 *
282 		 * \since
283 		 * v.5.5.22
284 		 */
285 		environment_params_t &
message_delivery_tracer_filter(so_5::msg_tracing::filter_shptr_t filter)286 		message_delivery_tracer_filter(
287 			so_5::msg_tracing::filter_shptr_t filter )
288 		{
289 			m_message_delivery_tracer_filter = std::move( filter );
290 			return *this;
291 		}
292 
293 		/*!
294 		 * \since
295 		 * v.5.5.10
296 		 * \brief Set parameters for the default dispatcher.
297 		 *
298 		 * \par Usage example:
299 			\code
300 			so_5::launch( []( so_5::environment_t & env ) { ... },
301 				[]( so_5::environment_params_t & env_params ) {
302 					using namespace so_5::disp::one_thread;
303 					// Event queue for the default dispatcher must use mutex as lock.
304 					env_params.default_disp_params( disp_params_t{}.tune_queue_params(
305 						[]( queue_traits::queue_params_t & queue_params ) {
306 							queue_params.lock_factory( queue_traits::simple_lock_factory() );
307 						} ) );
308 				} );
309 			\endcode
310 		 */
311 		environment_params_t &
default_disp_params(so_5::disp::one_thread::disp_params_t params)312 		default_disp_params( so_5::disp::one_thread::disp_params_t params )
313 		{
314 			m_default_disp_params = std::move(params);
315 			return *this;
316 		}
317 
318 		/*!
319 		 * \since
320 		 * v.5.5.10
321 		 * \brief Get the parameters for the default dispatcher.
322 		 */
323 		const so_5::disp::one_thread::disp_params_t &
default_disp_params() const324 		default_disp_params() const
325 		{
326 			return m_default_disp_params;
327 		}
328 
329 		/*!
330 		 * \brief Set activity tracking flag for the whole SObjectizer Environment.
331 		 * \since
332 		 * v.5.5.18
333 		 */
334 		environment_params_t &
work_thread_activity_tracking(work_thread_activity_tracking_t flag)335 		work_thread_activity_tracking(
336 			work_thread_activity_tracking_t flag )
337 		{
338 			m_work_thread_activity_tracking = flag;
339 			return *this;
340 		}
341 
342 		/*!
343 		 * \brief Get activity tracking flag for the whole SObjectizer Environment.
344 		 *
345 		 * \since
346 		 * v.5.5.18
347 		 */
348 		work_thread_activity_tracking_t
work_thread_activity_tracking() const349 		work_thread_activity_tracking() const
350 		{
351 			return m_work_thread_activity_tracking;
352 		}
353 
354 		//! Helper for turning work thread activity tracking on.
355 		/*!
356 		 * \since
357 		 * v.5.5.18
358 		 */
359 		environment_params_t &
turn_work_thread_activity_tracking_on()360 		turn_work_thread_activity_tracking_on()
361 			{
362 				return work_thread_activity_tracking(
363 						work_thread_activity_tracking_t::on );
364 			}
365 
366 		//! Helper for turning work thread activity tracking off.
367 		/*!
368 		 * \since
369 		 * v.5.5.18
370 		 */
371 		environment_params_t &
turn_work_thread_activity_tracking_off()372 		turn_work_thread_activity_tracking_off()
373 			{
374 				return work_thread_activity_tracking(
375 						work_thread_activity_tracking_t::off );
376 			}
377 
378 		//! Set manager for queue locks defaults.
379 		/*!
380 		 * \since
381 		 * v.5.5.18
382 		 */
383 		environment_params_t &
queue_locks_defaults_manager(queue_locks_defaults_manager_unique_ptr_t manager)384 		queue_locks_defaults_manager(
385 			queue_locks_defaults_manager_unique_ptr_t manager )
386 			{
387 				m_queue_locks_defaults_manager = std::move(manager);
388 				return *this;
389 			}
390 
391 		//! Get the current environment infrastructure factory.
392 		/*!
393 		 * \since
394 		 * v.5.5.19
395 		 */
396 		const environment_infrastructure_factory_t &
infrastructure_factory() const397 		infrastructure_factory() const
398 			{
399 				return m_infrastructure_factory;
400 			}
401 
402 		//! Set new environment infrastructure factory.
403 		/*!
404 		 * \since
405 		 * v.5.5.19
406 		 */
407 		environment_params_t &
infrastructure_factory(environment_infrastructure_factory_t factory)408 		infrastructure_factory(
409 			environment_infrastructure_factory_t factory )
410 			{
411 				m_infrastructure_factory = std::move(factory);
412 				return *this;
413 			}
414 
415 		/*!
416 		 * \brief Set event_queue_hook object.
417 		 *
418 		 * Since v.5.5.24 it is possible to use special event_queue_hook
419 		 * object. If it is used it should be set for SObjectizer
420 		 * Environment before the Environment will be started. This method
421 		 * allows to specify event_queue_hook object for a new Environment
422 		 * object.
423 		 *
424 		 * Usage example:
425 		 * \code
426 		 * so_5::launch(
427 		 * 	[](so_5::environment_t & env) {...}, // Some stating actions.
428 		 * 	[](so_5::environment_params_t & params) {
429 		 * 		// Set my own event_queue hook object.
430 		 * 		so_5::make_event_queue_hook<my_hook>(
431 		 * 			// Object is created dynamically and should be
432 		 * 			// destroyed the normal way.
433 		 * 			so_5::event_queue_hook_t::default_deleter,
434 		 * 			arg1, arg3, arg3 // and all other arguments for my_hook's constructor.
435 		 * 		);
436 		 * 	});
437 		 * \endcode
438 		 *
439 		 * \note
440 		 * The previous event_queue_hook object (if it was set earlier)
441 		 * will just be dropped.
442 		 *
443 		 * \since
444 		 * v.5.5.24
445 		 */
446 		void
event_queue_hook(event_queue_hook_unique_ptr_t hook)447 		event_queue_hook(
448 			event_queue_hook_unique_ptr_t hook )
449 			{
450 				m_event_queue_hook = std::move(hook);
451 			}
452 
453 		/*!
454 		 * \name Methods for internal use only.
455 		 * \{
456 		 */
457 		//! Get map of default SObjectizer's layers.
458 		const layer_map_t &
so5__layers_map() const459 		so5__layers_map() const
460 		{
461 			return m_so_layers;
462 		}
463 
464 		//! Get cooperation listener.
465 		coop_listener_unique_ptr_t
so5__giveout_coop_listener()466 		so5__giveout_coop_listener()
467 		{
468 			return std::move( m_coop_listener );
469 		}
470 
471 		//! Get exception logger.
472 		event_exception_logger_unique_ptr_t
so5__giveout_event_exception_logger()473 		so5__giveout_event_exception_logger()
474 		{
475 			return std::move( m_event_exception_logger );
476 		}
477 
478 		//! Get the timer_thread factory.
479 		so_5::timer_thread_factory_t
so5__giveout_timer_thread_factory()480 		so5__giveout_timer_thread_factory()
481 		{
482 			return std::move( m_timer_thread_factory );
483 		}
484 
485 		//! Get error logger for the environment.
486 		const error_logger_shptr_t &
so5__error_logger() const487 		so5__error_logger() const
488 		{
489 			return m_error_logger;
490 		}
491 
492 		/*!
493 		 * \brief Get message delivery tracer for the environment.
494 		 *
495 		 * \since
496 		 * v.5.5.9
497 		 */
498 		so_5::msg_tracing::tracer_unique_ptr_t
so5__giveout_message_delivery_tracer()499 		so5__giveout_message_delivery_tracer()
500 		{
501 			return std::move( m_message_delivery_tracer );
502 		}
503 
504 		/*!
505 		 * \brief Get message delivery tracer filter for the environment.
506 		 *
507 		 * \since
508 		 * v.5.5.22
509 		 */
510 		so_5::msg_tracing::filter_shptr_t
so5__giveout_message_delivery_tracer_filter()511 		so5__giveout_message_delivery_tracer_filter()
512 		{
513 			return std::move( m_message_delivery_tracer_filter );
514 		}
515 
516 		//! Take out queue locks defaults manager.
517 		/*!
518 		 * \since
519 		 * v.5.5.18
520 		 */
521 		queue_locks_defaults_manager_unique_ptr_t
so5__giveout_queue_locks_defaults_manager()522 		so5__giveout_queue_locks_defaults_manager()
523 			{
524 				return std::move( m_queue_locks_defaults_manager );
525 			}
526 
527 		//! Take out event_queue_hook object.
528 		/*!
529 		 * \since
530 		 * v.5.5.24
531 		 */
532 		event_queue_hook_unique_ptr_t
so5__giveout_event_queue_hook()533 		so5__giveout_event_queue_hook()
534 			{
535 				return std::move( m_event_queue_hook );
536 			}
537 		/*!
538 		 * \}
539 		 */
540 
541 	private:
542 		//! Add an additional layer.
543 		/*!
544 		 * If this layer is already added it will be replaced by \a layer_ptr.
545 		 *
546 		 * The method distinguishes layers from each other by the type SO_LAYER.
547 		 */
548 		void
549 		add_layer(
550 			//! Type identification for layer.
551 			const std::type_index & type,
552 			//! A layer to be added.
553 			layer_unique_ptr_t layer_ptr );
554 
555 		//! Timer thread factory.
556 		so_5::timer_thread_factory_t m_timer_thread_factory;
557 
558 		//! Additional layers.
559 		layer_map_t m_so_layers;
560 
561 		//! Cooperation listener.
562 		coop_listener_unique_ptr_t m_coop_listener;
563 
564 		//! Exception logger.
565 		event_exception_logger_unique_ptr_t m_event_exception_logger;
566 
567 		/*!
568 		 * \brief Exception reaction flag for the whole SO Environment.
569 		 *
570 		 * \since
571 		 * v.5.3.0
572 		 */
573 		exception_reaction_t m_exception_reaction;
574 
575 		/*!
576 		 * \brief Is autoshutdown when there is no more cooperation disabled?
577 		 *
578 		 * \see disable_autoshutdown()
579 		 *
580 		 * \since
581 		 * v.5.4.0
582 		 */
583 		bool m_autoshutdown_disabled;
584 
585 		/*!
586 		 * \brief Error logger for the environment.
587 		 * \since
588 		 * v.5.5.0
589 		 */
590 		error_logger_shptr_t m_error_logger;
591 
592 		/*!
593 		 * \brief Tracer for message delivery.
594 		 * \since
595 		 * v.5.5.9
596 		 */
597 		so_5::msg_tracing::tracer_unique_ptr_t m_message_delivery_tracer;
598 
599 		/*!
600 		 * \brief Message delivery tracer filter to be used with environment.
601 		 * \since
602 		 * v.5.5.22
603 		 */
604 		so_5::msg_tracing::filter_shptr_t m_message_delivery_tracer_filter;
605 
606 		/*!
607 		 * \brief Parameters for the default dispatcher.
608 		 * \since
609 		 * v.5.5.10
610 		 */
611 		so_5::disp::one_thread::disp_params_t m_default_disp_params;
612 
613 		/*!
614 		 * \brief Work thread activity tracking for the whole Environment.
615 		 * \since
616 		 * v.5.5.18
617 		 */
618 		work_thread_activity_tracking_t m_work_thread_activity_tracking;
619 
620 		/*!
621 		 * \brief Manager for defaults of queue locks.
622 		 *
623 		 * \since
624 		 * v.5.5.18
625 		 */
626 		queue_locks_defaults_manager_unique_ptr_t m_queue_locks_defaults_manager;
627 
628 		/*!
629 		 * \brief A factory for environment infrastructure entity.
630 		 *
631 		 * \since
632 		 * v.5.5.19
633 		 */
634 		environment_infrastructure_factory_t m_infrastructure_factory;
635 
636 		/*!
637 		 * \brief An event_queue_hook object.
638 		 *
639 		 * \note
640 		 * It can be a nullptr. It means that no event_queue_hook should
641 		 * be used.
642 		 *
643 		 * \since
644 		 * v.5.5.24
645 		 */
646 		event_queue_hook_unique_ptr_t m_event_queue_hook;
647 };
648 
649 //
650 // environment_t
651 //
652 
653 //! SObjectizer Environment.
654 /*!
655  * \section so_env__intro Basic information
656  *
657  * The SObjectizer Environment provides a basic infrastructure for
658  * the SObjectizer Run-Time execution.
659  *
660  * The main method of starting SObjectizer Environment creates a
661  * class derived from the environment_t and reimplementing the
662  * environment_t::init() method.
663  * This method should be used to define starting actions of
664  * application. For example first application cooperations can
665  * be registered here and starting messages can be sent to them.
666  *
667  * The SObjectizer Environment calls the environment_t::init() when
668  * the SObjectizer Run-Time is successfully started.
669  * If something happened during the Run-Time startup then
670  * the method init() will not be called.
671  *
672  * The SObjectizer Run-Time is started by the environment_t::run().
673  * This method blocks the caller thread until SObjectizer completely
674  * finished its work.
675  *
676  * The SObjectizer Run-Time is finished by the environment_t::stop().
677  * This method doesn't block the caller thread. Instead it sends a special
678  * shutdown signal to the Run-Time. The SObjectizer Run-Time then
679  * informs agents about this and waits finish of agents work.
680  * The SObjectizer Run-Time finishes if all agents are stopped and
681  * all cooperations are deregistered.
682  *
683  * Methods of the SObjectizer Environment can be splitted into the
684  * following groups:
685  * - working with mboxes;
686  * - working with dispatchers, exception loggers and handlers;
687  * - working with cooperations;
688  * - working with delayed and periodic messages;
689  * - working with additional layers;
690  * - initializing/running/stopping/waiting of the Run-Time.
691  *
692  * \section so_env__mbox_methods Methods for working with mboxes.
693  *
694  * SObjectizer Environment allows creation of named and anonymous mboxes.
695  * Syncronization objects for these mboxes can be obtained from
696  * common pools or assigned by a user during mbox creation.
697  *
698  * Mboxes are created by environment_t::create_mbox() methods.
699  * All these methods return the mbox_t which is a smart reference
700  * to the mbox.
701  *
702  * An anonymous mbox is automatically destroyed when the last reference to it is
703  * destroyed. So, to save the anonymous mbox, the mbox_ref from
704  * the create_mbox() should be stored somewhere.
705  *
706  * \section so_env__coop_methods Methods for working with cooperations.
707  *
708  * Cooperations can be created by environment_t::make_coop() methods.
709  *
710  * The method environment_t::register_coop() should be used for the
711  * cooperation registration.
712  *
713  * Method environment_t::deregister_coop() should be used for the
714  * cooperation deregistration.
715  */
716 class SO_5_TYPE environment_t
717 {
718 	friend class so_5::impl::internal_env_iface_t;
719 
720 		//! Auxiliary methods for getting reference to itself.
721 		/*!
722 		 * Could be used in constructors without compiler warnings.
723 		 */
724 		environment_t &
725 		self_ref();
726 
727 	public:
728 		explicit environment_t(
729 			//! Initialization params.
730 			environment_params_t && so_environment_params );
731 
732 		virtual ~environment_t();
733 
734 		environment_t( const environment_t & ) = delete;
735 		environment_t &
736 		operator=( const environment_t & ) = delete;
737 
738 		/*!
739 		 * \name Methods for working with mboxes.
740 		 * \{
741 		 */
742 
743 		//! Create an anonymous mbox with the default mutex.
744 		/*!
745 		 *	\note always creates a new mbox.
746 		 */
747 		mbox_t
748 		create_mbox();
749 
750 		//! Create named mbox.
751 		/*!
752 		 * If \a mbox_name is unique then a new mbox will be created.
753 		 * If not the reference to existing mbox will be returned.
754 		 */
755 		mbox_t
756 		create_mbox(
757 			//! Mbox name.
758 			nonempty_name_t mbox_name );
759 		/*!
760 		 * \}
761 		 */
762 
763 		/*!
764 		 * \name Method for working with message chains.
765 		 * \{
766 		 */
767 
768 		/*!
769 		 * \since
770 		 * v.5.5.13
771 		 *
772 		 * \brief Create message chain.
773 		 *
774 		 * \par Usage examples:
775 			\code
776 			so_5::environment_t & env = ...;
777 			// Create mchain with size-unlimited queue.
778 			auto ch1 = env.create_mchain(
779 				so_5::make_unlimited_mchain_params() );
780 			// Create mchain with size-limited queue without a timeout
781 			// on attempt to push another message to full mchain...
782 			auto ch2 = env.create_mchain(
783 				so_5::make_limited_without_waiting_mchain_params(
784 					// ...maximum size of the chain.
785 					100,
786 					// ...memory for chain will be allocated and deallocated dynamically...
787 					so_5::mchain_props::memory_usage_t::dynamic,
788 					// ...an exception will be thrown on overflow.
789 					so_5::mchain_props::overflow_reaction_t::throw_exception ) );
790 			// Create mchain with size-limited queue with a timeout for 200ms
791 			// on attempt to push another message to full mchain...
792 			auto ch3 = env.create_mchain(
793 				so_5::make_limited_with_waiting_mchain_params(
794 					// ...maximum size of the chain.
795 					100,
796 					// ...memory for chain will be preallocated...
797 					so_5::mchain_props::memory_usage_t::preallocated,
798 					// ...an oldest message from mchain will be removed on overflow...
799 					so_5::mchain_props::overflow_reaction_t::remove_oldest,
800 					// ...timeout for waiting on attempt to push a message into full mchain.
801 					std::chrono::milliseconds(200) ) );
802 			// Create size-unlimited mchain with custom notificator for
803 			// 'not_empty' situations.
804 			auto ch4 = env.create_mchain(
805 				so_5::make_unlimited_mchain_params().not_empty_notificator(
806 					[&] { some_widget.send_notify(); } ) );
807 			\endcode
808 		 */
809 		mchain_t
810 		create_mchain(
811 			//! Parameters for a new bag.
812 			const mchain_params_t & params );
813 		/*!
814 		 * \}
815 		 */
816 
817 		/*!
818 		 * \name Method for working with dispatchers.
819 		 * \{
820 		 */
821 
822 		//! Set up an exception logger.
823 		void
824 		install_exception_logger(
825 			event_exception_logger_unique_ptr_t logger );
826 		/*!
827 		 * \}
828 		 */
829 
830 		/*!
831 		 * \name Methods for working with cooperations.
832 		 * \{
833 		 */
834 
835 		//! Create a cooperation.
836 		/*!
837 		 * \return A new cooperation. This cooperation
838 		 * will use default dispatcher binders.
839 		 *
840 		 * \since
841 		 * v.5.6.0
842 		 */
843 		[[nodiscard]]
844 		coop_unique_holder_t
845 		make_coop();
846 
847 		//! Create a cooperation with specified dispatcher binder.
848 		/*!
849 		 * A binder \a disp_binder will be used for binding cooperation
850 		 * agents to the dispatcher. This binder will be default binder for
851 		 * this cooperation.
852 		 *
853 			\code
854 			so_5::environment_t & so_env = ...;
855 			so_5::coop_unique_holder_t coop = so_env.make_coop(
856 				so_5::disp::active_group::make_dispatcher( so_env ).binder(
857 					"some_active_group" ) );
858 
859 			// That agent will be bound to the dispatcher "active_group"
860 			// and will be member of an active group with name
861 			// "some_active_group".
862 			coop->make_agent< a_some_agent_t >();
863 			\endcode
864 		 *
865 		 * \since
866 		 * v.5.6.0
867 		 */
868 		[[nodiscard]]
869 		coop_unique_holder_t
870 		make_coop(
871 			//! A default binder for this cooperation.
872 			disp_binder_shptr_t disp_binder );
873 
874 		/*!
875 		 * \brief Create a new cooperation that will be a child for
876 		 * specified parent coop.
877 		 *
878 		 * The new cooperation will use the default dispatcher binder.
879 		 *
880 		 * Usage example:
881 		 * \code
882 		 * class parent_t final : public so_5::agent_t {
883 		 * 	void on_some_event(mhood_t<some_message>) {
884 		 * 		// We need to create a child coop.
885 		 * 		auto coop = so_environment().make_coop(
886 		 * 				// We as a parent.
887 		 * 				so_coop());
888 		 * 		...
889 		 * 	}
890 		 * };
891 		 * \endcode
892 		 *
893 		 * \since
894 		 * v.5.6.0
895 		 */
896 		[[nodiscard]]
897 		coop_unique_holder_t
898 		make_coop(
899 			//! Parent coop.
900 			coop_handle_t parent );
901 
902 		/*!
903 		 * \brief Create a new cooperation that will be a child for
904 		 * specified parent coop.
905 		 *
906 		 * The new cooperation will use the specified dispatcher binder.
907 		 *
908 		 * Usage example:
909 		 * \code
910 		 * class parent_t final : public so_5::agent_t {
911 		 * 	void on_some_event(mhood_t<some_message>) {
912 		 * 		// We need to create a child coop.
913 		 * 		auto coop = so_environment().make_coop(
914 		 * 				// We as a parent.
915 		 * 				so_coop(),
916 		 * 				// The default dispatcher for the new coop.
917 		 * 				so_5::disp::active_obj::make_dispatcher(
918 		 * 						so_environment() ).binder() );
919 		 * 		...
920 		 * 	}
921 		 * };
922 		 * \endcode
923 		 *
924 		 * \since
925 		 * v.5.6.0
926 		 */
927 		[[nodiscard]]
928 		coop_unique_holder_t
929 		make_coop(
930 			//! Parent coop.
931 			coop_handle_t parent,
932 			//! A default binder for this cooperation.
933 			disp_binder_shptr_t disp_binder );
934 
935 		//! Register a cooperation.
936 		/*!
937 		 * The cooperation registration includes following steps:
938 		 *
939 		 * - binding agents to the cooperation object;
940 		 * - checking uniques of the cooperation name. The cooperation will
941 		 *   not be registered if its name isn't unique;
942 		 * - agent_t::so_define_agent() will be called for each agent
943 		 *   in the cooperation;
944 		 * - binding of each agent to the dispatcher.
945 		 *
946 		 * If all these actions are successful then the cooperation is
947 		 * marked as registered.
948 		 */
949 		coop_handle_t
950 		register_coop(
951 			//! Cooperation to be registered.
952 			coop_unique_holder_t agent_coop );
953 
954 		/*!
955 		 * \brief Register single agent as a cooperation.
956 		 *
957 		 * It is just a helper methods for convience.
958 		 *
959 		 * Usage sample:
960 		 * \code
961 		   std::unique_ptr< my_agent > a( new my_agent(...) );
962 		   so_env.register_agent_as_coop( std::move(a) );
963 		 * \endcode
964 		 */
965 		template< class A >
966 		coop_handle_t
register_agent_as_coop(std::unique_ptr<A> agent)967 		register_agent_as_coop(
968 			std::unique_ptr< A > agent )
969 		{
970 			auto coop = make_coop();
971 			coop->add_agent( std::move( agent ) );
972 			return register_coop( std::move( coop ) );
973 		}
974 
975 		/*!
976 		 * \brief Register single agent as a cooperation with specified
977 		 * dispatcher binder.
978 		 *
979 		 * \since
980 		 * v.5.2.1
981 		 *
982 		 * It is just a helper methods for convience.
983 		 *
984 		 * Usage sample:
985 		 * \code
986 		   so_env.register_agent_as_coop(
987 					std::make_unique<my_agent>(...),
988 		   		so_5::disp::active_group::create_disp_binder(
989 		   				"active_group",
990 		   				"some_active_group" ) );
991 		 * \endcode
992 		 */
993 		template< class A >
994 		coop_handle_t
register_agent_as_coop(std::unique_ptr<A> agent,disp_binder_shptr_t disp_binder)995 		register_agent_as_coop(
996 			std::unique_ptr< A > agent,
997 			disp_binder_shptr_t disp_binder )
998 		{
999 			auto coop = make_coop( std::move( disp_binder ) );
1000 			coop->add_agent( std::move( agent ) );
1001 			return register_coop( std::move( coop ) );
1002 		}
1003 
1004 		//! Deregister the cooperation.
1005 		/*!
1006 		 * Method searches the cooperation within registered cooperations and if
1007 		 * it is found deregisters it.
1008 		 *
1009 		 * Deregistration can take some time.
1010 		 *
1011 		 * At first a special signal is sent to cooperation agents.
1012 		 * By receiving these signal agents stop receiving new messages.
1013 		 * When the local event queue for an agent becomes empty the
1014 		 * agent informs the cooperation about this. When the cooperation
1015 		 * receives all these signals from agents it informs
1016 		 * the SObjectizer Run-Time.
1017 		 *
1018 		 * Only after this the cooperation is deregistered on the special
1019 		 * context.
1020 		 *
1021 		 * After the cooperation deregistration agents are unbound from
1022 		 * dispatchers.
1023 		 *
1024 		 * \note
1025 		 * This method is marked as noexcept because there is no way
1026 		 * to recover if any exception is raised here.
1027 		 */
1028 		void
deregister_coop(coop_handle_t coop,int reason)1029 		deregister_coop(
1030 			//! The coop to be deregistered.
1031 			coop_handle_t coop,
1032 			//! Deregistration reason.
1033 			int reason ) noexcept
1034 		{
1035 			auto coop_shptr = low_level_api::to_shptr_noexcept( coop );
1036 			if( coop_shptr )
1037 				coop_shptr->deregister( reason );
1038 		}
1039 		/*!
1040 		 * \}
1041 		 */
1042 
1043 		/*!
1044 		 * \name Methods for working with layers.
1045 		 * \{
1046 		 */
1047 
1048 		//! Get access to the layer without raising exception if layer
1049 		//! is not found.
1050 		template< class SO_Layer >
1051 		SO_Layer *
query_layer_noexcept() const1052 		query_layer_noexcept() const
1053 		{
1054 			static_assert( std::is_base_of< layer_t, SO_Layer >::value,
1055 					"SO_Layer must be derived from so_layer_t class" );
1056 
1057 			return dynamic_cast< SO_Layer * >(
1058 					query_layer( std::type_index( typeid( SO_Layer ) ) ) );
1059 		}
1060 
1061 		//! Get access to the layer with exception if layer is not found.
1062 		template< class SO_Layer >
1063 		SO_Layer *
query_layer() const1064 		query_layer() const
1065 		{
1066 			auto layer = query_layer_noexcept< SO_Layer >();
1067 
1068 			if( !layer )
1069 				SO_5_THROW_EXCEPTION(
1070 					rc_layer_does_not_exist,
1071 					"layer does not exist" );
1072 
1073 			return layer;
1074 		}
1075 
1076 		//! Add an additional layer.
1077 		template< class SO_Layer >
1078 		void
add_extra_layer(std::unique_ptr<SO_Layer> layer_ptr)1079 		add_extra_layer(
1080 			std::unique_ptr< SO_Layer > layer_ptr )
1081 		{
1082 			add_extra_layer(
1083 				std::type_index( typeid( SO_Layer ) ),
1084 				layer_ref_t( layer_ptr.release() ) );
1085 		}
1086 		/*!
1087 		 * \}
1088 		 */
1089 
1090 		/*!
1091 		 * \name Methods for starting, initializing and stopping of the Run-Time.
1092 		 * \{
1093 		 */
1094 
1095 		//! Run the SObjectizer Run-Time.
1096 		void
1097 		run();
1098 
1099 		//! Initialization hook.
1100 		/*!
1101 		 * \attention A hang inside of this method will prevent the Run-Time
1102 		 * from stopping. For example if a dialog with an application user
1103 		 * is performed inside init() then SObjectizer cannot finish
1104 		 * its work until this dialog is finished.
1105 		 */
1106 		virtual void
1107 		init() = 0;
1108 
1109 		//! Send a shutdown signal to the Run-Time.
1110 		void
1111 		stop();
1112 		/*!
1113 		 * \}
1114 		 */
1115 
1116 		/*!
1117 		 * \brief Call event exception logger for logging an exception.
1118 		 *
1119 		 * \note
1120 		 * Since v.5.6.0 this method is marked as noexcept.
1121 		 *
1122 		 * \since
1123 		 * v.5.2.3.
1124 		 */
1125 		void
1126 		call_exception_logger(
1127 			//! Exception caught.
1128 			const std::exception & event_exception,
1129 			//! A cooperation to which agent is belong.
1130 			const coop_handle_t & coop ) noexcept;
1131 
1132 		/*!
1133 		 * \brief An exception reaction for the whole SO Environment.
1134 		 *
1135 		 * \since
1136 		 * v.5.3.0
1137 		 */
1138 		exception_reaction_t
1139 		exception_reaction() const;
1140 
1141 		/*!
1142 		 * \brief Get the error_logger object.
1143 		 *
1144 		 * \since
1145 		 * v.5.5.0
1146 		 */
1147 		error_logger_t &
1148 		error_logger() const;
1149 
1150 		/*!
1151 		 * \brief Helper method for simplification of agents creation.
1152 		 *
1153 		 * \since
1154 		 * v.5.5.4
1155 		 *
1156 		 * \note Creates an instance of agent of type \a Agent by using
1157 		 * environment_t::make_agent() template function and adds it to
1158 		 * the cooperation. Uses the fact that most agent types use reference
1159 		 * to the environment object as the first argument.
1160 		 *
1161 		 * \return unique pointer to the new agent.
1162 		 *
1163 		 * \tparam Agent type of agent to be created.
1164 		 * \tparam Args type of parameters list for agent constructor.
1165 		 *
1166 		 * \par Usage sample:
1167 		 \code
1168 		 so_5::environment_t & env = ...;
1169 		 // For the case of constructor like my_agent(environmen_t&).
1170 		 auto a1 = env.make_agent< my_agent >();
1171 		 // For the case of constructor like your_agent(environment_t&, std::string).
1172 		 auto a2 = env.make_agent< your_agent >( "hello" );
1173 		 // For the case of constructor like their_agent(environment_t&, std::string, mbox_t).
1174 		 auto a3 = env.make_agent< their_agent >( "bye", a2->so_direct_mbox() );
1175 		 \endcode
1176 		 */
1177 		template< class Agent, typename... Args >
1178 		std::unique_ptr< Agent >
make_agent(Args &&...args)1179 		make_agent( Args &&... args )
1180 		{
1181 			return std::unique_ptr< Agent >(
1182 					new Agent( *this, std::forward<Args>(args)... ) );
1183 		}
1184 
1185 		/*!
1186 		 * \brief Access to controller of run-time monitoring.
1187 		 *
1188 		 * \since
1189 		 * v.5.5.4
1190 		 */
1191 		stats::controller_t &
1192 		stats_controller();
1193 
1194 		/*!
1195 		 * \brief Access to repository of data sources for run-time monitoring.
1196 		 *
1197 		 * \since
1198 		 * v.5.5.4
1199 		 */
1200 		stats::repository_t &
1201 		stats_repository();
1202 
1203 		/*!
1204 		 * \brief Helper method for simplification of cooperation creation
1205 		 * and registration.
1206 		 *
1207 		 * \return The value returned from lambda-function. Or void if
1208 		 * the lambda-function returns void.
1209 		 *
1210 		 * \since
1211 		 * v.5.5.5
1212 		 *
1213 		 * \par Usage samples:
1214 			\code
1215 			// The default dispatcher will be used for binding.
1216 			env.introduce_coop( []( so_5::coop_t & coop ) {
1217 				coop.make_agent< first_agent >(...);
1218 				coop.make_agent< second_agent >(...);
1219 			});
1220 
1221 			// For the case when dispatcher binder is specified.
1222 			env.introduce_coop(
1223 				so_5::disp::active_obj::make_dispatcher( env ).binder(),
1224 				[]( so_5::coop_t & coop ) {
1225 					coop.make_agent< first_agent >(...);
1226 					coop.make_agent< second_agent >(...);
1227 				} );
1228 
1229 			// Usage of return value from the lambda function.
1230 			so_5::mbox_t mbox = env.introduce_coop( [](so_5::coop_t & coop) {
1231 					auto * a = coop.make_agent< first_agent >(...);
1232 					coop.make_agent< second_agent >(...);
1233 
1234 					return a->so_direct_mbox();
1235 				});
1236 			\endcode
1237 		 */
1238 		template< typename... Args >
1239 		decltype(auto)
1240 		introduce_coop( Args &&... args );
1241 
1242 		/*!
1243 		 * \brief Get activity tracking flag for the whole SObjectizer Environment.
1244 		 *
1245 		 * \since
1246 		 * v.5.5.18
1247 		 */
1248 		work_thread_activity_tracking_t
1249 		work_thread_activity_tracking() const;
1250 
1251 		/*!
1252 		 * \brief Get binding to the default dispatcher.
1253 		 *
1254 		 * \note
1255 		 * This method is part of environment_t for possibility to
1256 		 * write custom implementations of environment_infrastructure_t.
1257 		 * Because of that this method can be changed or removed in
1258 		 * future versions of SObjectizer.
1259 		 *
1260 		 * \since
1261 		 * v.5.5.19
1262 		 */
1263 		disp_binder_shptr_t
1264 		so_make_default_disp_binder();
1265 
1266 		/*!
1267 		 * \brief Get autoshutdown_disabled flag.
1268 		 *
1269 		 * Autoshutdown feature is on by default. It can be turned off
1270 		 * in environment_params_t. This methods returns <i>true</i> if
1271 		 * autoshutdown is turned off.
1272 		 *
1273 		 * \since
1274 		 * v.5.5.19
1275 		 */
1276 		bool
1277 		autoshutdown_disabled() const;
1278 
1279 		//! Schedule timer event.
1280 		/*!
1281 		 * \attention
1282 		 * Values of \a pause and \a period should be non-negative.
1283 		 *
1284 		 * \note
1285 		 * This method is a part of low-level SObjectizer's API.
1286 		 * Because of that it can be changed or removed in some
1287 		 * future version of SObjectizer without prior notice.
1288 		 */
1289 		so_5::timer_id_t
1290 		so_schedule_timer(
1291 			//! Parameters for new timer.
1292 			const low_level_api::schedule_timer_params_t params );
1293 
1294 		//! Schedule a single shot timer event.
1295 		/*!
1296 		 * \attention
1297 		 * Value of \a pause should be non-negative.
1298 		 *
1299 		 * \note
1300 		 * This method is a part of low-level SObjectizer's API.
1301 		 * Because of that it can be changed or removed in some
1302 		 * future version of SObjectizer without prior notice.
1303 		 */
1304 		void
1305 		so_single_timer(
1306 			//! Parameters for new timer.
1307 			const low_level_api::single_timer_params_t params );
1308 
1309 		//! Create a custom mbox.
1310 		/*!
1311 		 * \tparam Lambda type of actual lambda with all creation actions.
1312 		 * The Lambda must be lambda-function or functional objects with
1313 		 * the following format:
1314 		 * \code
1315 		 * so_5::mbox_t lambda(const so_5::mbox_creation_data_t &);
1316 		 * \endcode
1317 		 *
1318 		 * \since
1319 		 * v.5.5.19.2
1320 		 */
1321 		template< typename Lambda >
1322 		mbox_t
make_custom_mbox(Lambda && lambda)1323 		make_custom_mbox( Lambda && lambda )
1324 			{
1325 				using namespace custom_mbox_details;
1326 
1327 				creator_template_t< Lambda > creator( std::forward<Lambda>(lambda) );
1328 				return do_make_custom_mbox( creator );
1329 			}
1330 
1331 		/*!
1332 		 * \name Methods for working with stop_guards.
1333 		 * \{
1334 		 */
1335 		//! Set up a new stop_guard.
1336 		/*!
1337 		 * Usage examples:
1338 		 * \code
1339 		 * // Add a stop_guard.
1340 		 * // Note: an exception can be thrown if stop is in progress
1341 		 * class my_stop_guard
1342 		 * 	: public so_5::stop_guard_t
1343 		 * 	, public std::enable_shared_from_this< my_stop_guard >
1344 		 * {...};
1345 		 *
1346 		 * class my_agent : public so_5::agent_t
1347 		 * {
1348 		 * 	...
1349 		 * 	void on_some_event()
1350 		 * 	{
1351 		 * 		// We need a stop_guard here.
1352 		 * 		m_my_guard = std::make_shared< my_stop_guard >(...);
1353 		 * 		so_environment().setup_stop_guard( m_my_guard );
1354 		 * 	}
1355 		 * private :
1356 		 * 	so_5::stop_guard_shptr_t m_my_guard;
1357 		 * };
1358 		 *
1359 		 * //
1360 		 * // Add a stop_guard without throwing an exception if stop is in progress
1361 		 * //
1362 		 * class my_stop_guard
1363 		 * 	: public so_5::stop_guard_t
1364 		 * 	, public std::enable_shared_from_this< my_stop_guard >
1365 		 * {...};
1366 		 *
1367 		 * class my_agent : public so_5::agent_t
1368 		 * {
1369 		 * 	...
1370 		 * 	void on_some_event()
1371 		 * 	{
1372 		 * 		// We need a stop_guard here.
1373 		 * 		m_my_guard = std::make_shared< my_stop_guard >(...);
1374 		 * 		const auto r = so_environment().setup_stop_guard(
1375 		 * 				m_my_guard,
1376 		 * 				so_5::stop_guard_t::what_if_stop_in_progress_t::return_negative_result );
1377 		 *			if( so_5::stop_guard_t::setup_result_t::stop_already_in_progress  == r )
1378 		 *				... // handle error here.
1379 		 * 	}
1380 		 * private :
1381 		 * 	so_5::stop_guard_shptr_t m_my_guard;
1382 		 * };
1383 		 * \endcode
1384 		 *
1385 		 * \note
1386 		 * Uniqueness of stop_guard is not checked. It means that
1387 		 * it is possible to add the same stop_guard several times.
1388 		 * But it seems to be useless.
1389 		 *
1390 		 * \since
1391 		 * v.5.5.19.2
1392 		 */
1393 		stop_guard_t::setup_result_t
1394 		setup_stop_guard(
1395 			//! Stop guard to be set.
1396 			//! Should not be nullptr.
1397 			stop_guard_shptr_t guard,
1398 			//! What to do is the stop operation is already in progress?
1399 			stop_guard_t::what_if_stop_in_progress_t reaction_on_stop_in_progress
1400 				= stop_guard_t::what_if_stop_in_progress_t::throw_exception );
1401 
1402 		//! Remove stop_guard and complete the stop operation if necessary.
1403 		/*!
1404 		 * Every stop_guard which was added to the environment must be
1405 		 * explicitely removed from the environment. It is done by this method.
1406 		 * If there is no more stop_guard and the stop operation is in progress
1407 		 * then the environment will complete the stop operation.
1408 		 *
1409 		 * Usage examples:
1410 		 * \code
1411 		 * // Note: an exception can be thrown if stop is in progress
1412 		 * class my_stop_guard
1413 		 * 	: public so_5::stop_guard_t
1414 		 * 	, public std::enable_shared_from_this< my_stop_guard >
1415 		 * {...};
1416 		 *
1417 		 * class my_agent : public so_5::agent_t
1418 		 * {
1419 		 * 	...
1420 		 * 	void on_some_event()
1421 		 * 	{
1422 		 * 		// We need a stop_guard here.
1423 		 * 		m_my_guard = std::make_shared< my_stop_guard >(...);
1424 		 * 		so_environment().setup_stop_guard( m_my_guard );
1425 		 * 	}
1426 		 *
1427 		 * 	void on_work_finished_signal()
1428 		 * 	{
1429 		 * 		// Stop_guard must be removed now.
1430 		 * 		so_environment().remove_stop_guard( m_my_guard );
1431 		 * 	}
1432 		 * private :
1433 		 * 	so_5::stop_guard_shptr_t m_my_guard;
1434 		 * };
1435 		 * \endcode
1436 		 * \since
1437 		 * v.5.5.19.2
1438 		 */
1439 		void
1440 		remove_stop_guard(
1441 			//! Stop guard to be removed.
1442 			stop_guard_shptr_t guard );
1443 		/*!
1444 		 * \}
1445 		 */
1446 
1447 		/*!
1448 		 * \name Methods for working with msg_tracing's filters.
1449 		 * \{
1450 		 */
1451 		/*!
1452 		 * \brief Change the current msg_tracing's filter to a new one.
1453 		 *
1454 		 * Usage example:
1455 		 * \code
1456 		 * so_5::launch([](so_5::environment_t & env) {...},
1457 		 * 	[](so_5::environment_params_t & params) {
1458 		 * 		// Turn message delivery tracing on.
1459 		 * 		params.message_delivery_tracer(
1460 		 * 			so_5::msg_tracing::std_cout_tracer());
1461 		 * 		// Disable all trace messages.
1462 		 * 		// It is expected that trace filter will be changed in the future.
1463 		 * 		params.message_delivery_tracer_filter(
1464 		 * 			so_5::msg_tracing::make_disable_all_filter());
1465 		 * 		...
1466 		 * 	} );
1467 		 * ...
1468 		 * void some_agent_t::turn_msg_tracing_on() {
1469 		 * 	// Remove trace filter. As result all trace messages will be printed.
1470 		 * 	so_environment().change_message_delivery_tracer_filter(
1471 		 * 		so_5::msg_tracing::no_filter());
1472 		 * 	...
1473 		 * }
1474 		 * \endcode
1475 		 *
1476 		 * \note
1477 		 * It is possible that there are active calls to
1478 		 * so_5::msg_tracing::filter_t::filter() methods at the time of
1479 		 * invocation of change_message_delivery_tracer_filter(). In this
1480 		 * case all active calls will be completed with the previous
1481 		 * filter. This could lead to mixture of messages in the trace:
1482 		 * some of them will be enabled by old filter and some of them
1483 		 * will be enabled by new filter. And it is possible that
1484 		 * messages enabled by new filter will precede messages enabled
1485 		 * by old filter.
1486 		 *
1487 		 * \throw exception_t if message delivery tracing is disabled.
1488 		 *
1489 		 * \since
1490 		 * v.5.5.22
1491 		 */
1492 		void
1493 		change_message_delivery_tracer_filter(
1494 			//! A new filter to be used.
1495 			//! It can be an empty pointer. In this case all trace messages
1496 			//! will be passed to tracer object.
1497 			so_5::msg_tracing::filter_shptr_t filter );
1498 		/*!
1499 		 * \}
1500 		 */
1501 
1502 	private:
1503 		//! Access to an additional layer.
1504 		layer_t *
1505 		query_layer(
1506 			const std::type_index & type ) const;
1507 
1508 		//! Add an additional layer.
1509 		void
1510 		add_extra_layer(
1511 			const std::type_index & type,
1512 			const layer_ref_t & layer );
1513 
1514 		//! Remove an additional layer.
1515 		void
1516 		remove_extra_layer(
1517 			const std::type_index & type );
1518 
1519 		//! Actual creation of a custom mbox.
1520 		/*!
1521 		 * \since
1522 		 * v.5.5.19.2
1523 		 */
1524 		mbox_t
1525 		do_make_custom_mbox(
1526 			custom_mbox_details::creator_iface_t & creator );
1527 
1528 		struct internals_t;
1529 
1530 		//! SObjectizer Environment internals.
1531 		std::unique_ptr< internals_t > m_impl;
1532 
1533 		/*!
1534 		 * \name Implementation details related to run/stop functionality.
1535 		 * \{
1536 		 */
1537 		/*!
1538 		 * \brief Run controller for run-time monitoring
1539 		 * and call next run stage.
1540 		 *
1541 		 * \since
1542 		 * v.5.5.4
1543 		 */
1544 		void
1545 		impl__run_stats_controller_and_go_further();
1546 
1547 		/*!
1548 		 * \brief Run layers and call next run stage.
1549 		 */
1550 		void
1551 		impl__run_layers_and_go_further();
1552 
1553 		/*!
1554 		 * \brief Launch environment infrastructure and wait for finish.
1555 		 *
1556 		 * \since
1557 		 * v.5.5.19
1558 		 */
1559 		void
1560 		impl__run_infrastructure();
1561 		/*!
1562 		 * \}
1563 		 */
1564 };
1565 
1566 namespace details
1567 {
1568 
1569 /*!
1570  * \brief Helper class for building and registering new cooperation.
1571  *
1572  * \since
1573  * v.5.5.5
1574  */
1575 class introduce_coop_helper_t
1576 {
1577 private :
1578 	//! Environment for creation of cooperation.
1579 	environment_t & m_env;
1580 	//! Optional parent coop.
1581 	coop_handle_t m_parent;
1582 
1583 	template< typename Lambda >
1584 	decltype(auto)
build_and_register_coop(disp_binder_shptr_t binder,Lambda && lambda)1585 	build_and_register_coop(
1586 		disp_binder_shptr_t binder,
1587 		Lambda && lambda )
1588 	{
1589 		const auto coop_maker = [this, b = std::move(binder)] {
1590 			if( m_parent )
1591 				return m_env.make_coop( m_parent, std::move(b) );
1592 			else
1593 				return m_env.make_coop( std::move(b) );
1594 		};
1595 
1596 		auto coop = coop_maker();
1597 
1598 		using return_type = std::invoke_result_t<Lambda, so_5::coop_t &>;
1599 
1600 		if constexpr( std::is_void_v<return_type> )
1601 		{
1602 			lambda( *coop );
1603 			m_env.register_coop( std::move( coop ) );
1604 		}
1605 		else if constexpr( std::is_reference_v<return_type> )
1606 		{
1607 			auto && ret_val = lambda( *coop );
1608 			m_env.register_coop( std::move( coop ) );
1609 
1610 			return std::forward<decltype(ret_val)>(ret_val);
1611 		}
1612 		else
1613 		{
1614 			auto ret_val = lambda( *coop );
1615 			m_env.register_coop( std::move( coop ) );
1616 
1617 			return ret_val;
1618 		}
1619 	}
1620 
1621 public :
1622 	//! Constructor for the case of creation a cooperation without parent.
introduce_coop_helper_t(environment_t & env)1623 	introduce_coop_helper_t( environment_t & env )
1624 		:	m_env{ env }
1625 	{}
1626 	//! Constructor for the case of creation a cooperation with parent.
introduce_coop_helper_t(environment_t & env,coop_handle_t parent)1627 	introduce_coop_helper_t(
1628 		environment_t & env,
1629 		coop_handle_t parent )
1630 		:	m_env{ env }
1631 		,	m_parent{ std::move(parent) }
1632 	{}
1633 
1634 	/*!
1635 	 * For the case:
1636 	 * - default dispatcher is used.
1637 	 */
1638 	template< typename L >
1639 	decltype(auto)
introduce(L && lambda)1640 	introduce( L && lambda )
1641 	{
1642 		return build_and_register_coop(
1643 				m_env.so_make_default_disp_binder(),
1644 				std::forward< L >( lambda ) );
1645 	}
1646 
1647 	/*!
1648 	 * For the case:
1649 	 * - dispatcher builder is specified.
1650 	 */
1651 	template< typename L >
1652 	decltype(auto)
introduce(disp_binder_shptr_t binder,L && lambda)1653 	introduce( disp_binder_shptr_t binder, L && lambda )
1654 	{
1655 		return build_and_register_coop(
1656 				std::move( binder ),
1657 				std::forward< L >( lambda ) );
1658 	}
1659 };
1660 
1661 } /* namespace details */
1662 
1663 template< typename... Args >
1664 decltype(auto)
introduce_coop(Args &&...args)1665 environment_t::introduce_coop( Args &&... args )
1666 {
1667 	details::introduce_coop_helper_t helper{ *this };
1668 	return helper.introduce( std::forward< Args >( args )... );
1669 }
1670 
1671 /*!
1672  * \brief A simple way for creating child cooperation.
1673  *
1674  * \since
1675  * v.5.5.3
1676  *
1677  * \par Usage sample
1678 	\code
1679 	class owner : public so_5::agent_t
1680 	{
1681 	public :
1682 		...
1683 		virtual void
1684 		so_evt_start() override
1685 		{
1686 			auto child = so_5::create_child_coop( *this );
1687 			child->make_agent< worker >();
1688 			...
1689 			so_environment().register_coop( std::move( child ) );
1690 		}
1691 	};
1692 	\endcode
1693  */
1694 template< typename... Args >
1695 [[nodiscard]]
1696 coop_unique_holder_t
create_child_coop(agent_t & owner,Args &&...args)1697 create_child_coop(
1698 	//! Owner of the cooperation.
1699 	agent_t & owner,
1700 	//! Arguments for the environment_t::make_coop() method.
1701 	Args&&... args )
1702 {
1703 	auto coop = owner.so_environment().make_coop(
1704 			owner.so_coop(),
1705 			std::forward< Args >(args)... );
1706 
1707 	return coop;
1708 }
1709 
1710 /*!
1711  * \brief A simple way for creating child cooperation when there is
1712  * a reference to the parent cooperation object.
1713  *
1714  * \since
1715  * v.5.5.8
1716  *
1717  * \par Usage sample
1718 	\code
1719 	class parent final : public so_5::agent_t {
1720 		void on_some_event(mhood_t<some_message>) {
1721 			auto child = so_5::create_child_coop(
1722 					// We as the parent coop.
1723 					so_coop(),
1724 					// The default binder for the new coop.
1725 					so_5::disp::one_thread::make_dispatcher(
1726 							so_environment().binder() ) );
1727 			...
1728 			so_environment().register_coop( std::move(child) );
1729 		}
1730 		...
1731 	};
1732 	\endcode
1733  */
1734 template< typename... Args >
1735 [[nodiscard]]
1736 coop_unique_holder_t
create_child_coop(coop_handle_t parent,Args &&...args)1737 create_child_coop(
1738 	//! Parent cooperation.
1739 	coop_handle_t parent,
1740 	//! Arguments for the environment_t::make_coop() method.
1741 	Args&&... args )
1742 {
1743 	return low_level_api::to_shptr(parent)->environment().make_coop(
1744 			parent,
1745 			std::forward< Args >(args)... );
1746 }
1747 
1748 /*!
1749  * \brief A simple way for creating and registering child cooperation.
1750  *
1751  * \since
1752  * v.5.5.5
1753  *
1754  * \par Usage sample
1755 	\code
1756 	class owner : public so_5::agent_t
1757 	{
1758 	public :
1759 		...
1760 		virtual void
1761 		so_evt_start() override
1762 		{
1763 			so_5::introduce_child_coop( *this, []( so_5::coop_t & coop ) {
1764 				coop.make_agent< worker >();
1765 			} );
1766 		}
1767 	};
1768 	\endcode
1769 
1770  * \note This function is just a tiny wrapper around
1771  * so_5::environment_t::introduce_coop() helper method. For more
1772  * examples with usage of introduce_coop() please see description of
1773  * that method.
1774  */
1775 template< typename... Args >
1776 decltype(auto)
introduce_child_coop(agent_t & owner,Args &&...args)1777 introduce_child_coop(
1778 	//! Owner of the cooperation.
1779 	agent_t & owner,
1780 	//! Arguments for the environment_t::introduce_coop() method.
1781 	Args&&... args )
1782 {
1783 	return details::introduce_coop_helper_t{
1784 					owner.so_environment(),
1785 					owner.so_coop()
1786 			}.introduce( std::forward< Args >(args)... );
1787 }
1788 
1789 /*!
1790  * \brief A simple way for creating and registering child cooperation
1791  * when there is a reference to parent coop.
1792  *
1793  * \since
1794  * v.5.5.8
1795  *
1796  * \par Usage sample
1797 	\code
1798 	class owner : public so_5::agent_t
1799 	{
1800 	public :
1801 		...
1802 		virtual void
1803 		so_evt_start() override
1804 		{
1805 			so_5::introduce_child_coop( so_coop(), []( so_5::coop_t & coop ) {
1806 				coop.make_agent< worker >();
1807 			} );
1808 		}
1809 	};
1810 	\endcode
1811 
1812  * \note This function is just a tiny wrapper around
1813  * so_5::environment_t::introduce_coop() helper method. For more
1814  * examples with usage of introduce_coop() please see description of
1815  * that method.
1816  */
1817 template< typename... Args >
1818 decltype(auto)
introduce_child_coop(coop_handle_t parent,Args &&...args)1819 introduce_child_coop(
1820 	//! Parent cooperation.
1821 	coop_handle_t parent,
1822 	//! Arguments for the environment_t::introduce_coop() method.
1823 	Args&&... args )
1824 {
1825 	return details::introduce_coop_helper_t{
1826 					low_level_api::to_shptr(parent)->environment(),
1827 					parent
1828 			}.introduce( std::forward< Args >(args)... );
1829 }
1830 
1831 //
1832 // make_default_disp_binder
1833 //
1834 /*!
1835  * \brief Create an instance of the default dispatcher binder.
1836  *
1837  * \note This function takes into account a possibility to have
1838  * different types of environment infrastructures (introduced in v.5.5.19)
1839  * and creates a default dispatcher binder with respect to the
1840  * actual environment infrastructure type.
1841  *
1842  * Usage example:
1843  * \code
1844  * so_5::launch( [](so_5::environment_t & env) {
1845  * 	env.introduce_coop(
1846  * 		// Agents from that coop will be bound to the default dispatcher.
1847  * 		so_5::make_default_disp_binder(env),
1848  * 		[](so_5::coop_t & coop) {
1849  * 			coop.make_agent<...>(...);
1850  * 		} );
1851  * } );
1852  * \endcode
1853  *
1854  * \since
1855  * v.5.5.19
1856  */
1857 inline disp_binder_shptr_t
make_default_disp_binder(environment_t & env)1858 make_default_disp_binder( environment_t & env )
1859 	{
1860 		return env.so_make_default_disp_binder();
1861 	}
1862 
1863 namespace low_level_api
1864 {
1865 
1866 //! Schedule periodic timer event.
1867 /*!
1868  * \note
1869  * This function is a part of low-level SObjectizer's interface.
1870  * Because of that this function can be removed or changed in some
1871  * future version without prior notice.
1872  *
1873  * \attention
1874  * Values of \a pause and \a period should be non-negative.
1875  *
1876  * \since
1877  * v.5.6.0
1878  */
1879 [[nodiscard]] inline so_5::timer_id_t
schedule_timer(const std::type_index & subscription_type,message_ref_t msg,const mbox_t & mbox,std::chrono::steady_clock::duration pause,std::chrono::steady_clock::duration period)1880 schedule_timer(
1881 	//! Message type for searching subscribers.
1882 	const std::type_index & subscription_type,
1883 	//! Message to be sent after timeout.
1884 	message_ref_t msg,
1885 	//! Mbox to which message will be delivered.
1886 	const mbox_t & mbox,
1887 	//! Timeout before the first delivery.
1888 	std::chrono::steady_clock::duration pause,
1889 	//! Period of the delivery repetition for periodic messages.
1890 	/*!
1891 		\note Value 0 indicates that it's not periodic message
1892 			(will be delivered one time).
1893 	*/
1894 	std::chrono::steady_clock::duration period )
1895 {
1896 	return mbox->environment().so_schedule_timer(
1897 			schedule_timer_params_t{
1898 					std::cref(subscription_type),
1899 					std::cref(msg),
1900 					std::cref(mbox),
1901 					pause,
1902 					period } );
1903 }
1904 
1905 //! Schedule single timer event.
1906 /*!
1907  * \note
1908  * This function is a part of low-level SObjectizer's interface.
1909  * Because of that this function can be removed or changed in some
1910  * future version without prior notice.
1911  *
1912  * \attention
1913  * Value \a period should be non-negative.
1914  *
1915  * \since
1916  * v.5.6.0
1917  */
1918 inline void
single_timer(const std::type_index & subscription_type,message_ref_t msg,const mbox_t & mbox,std::chrono::steady_clock::duration pause)1919 single_timer(
1920 	//! Message type for searching subscribers.
1921 	const std::type_index & subscription_type,
1922 	//! Message to be sent after timeout.
1923 	message_ref_t msg,
1924 	//! Mbox to which message will be delivered.
1925 	const mbox_t & mbox,
1926 	//! Timeout before the delivery.
1927 	std::chrono::steady_clock::duration pause )
1928 {
1929 	return mbox->environment().so_single_timer(
1930 			single_timer_params_t{
1931 					std::cref(subscription_type),
1932 					std::cref(msg),
1933 					std::cref(mbox),
1934 					pause } );
1935 }
1936 
1937 } /* namespace low_level_api */
1938 
1939 } /* namespace so_5 */
1940 
1941 #if defined( SO_5_MSVC )
1942 	#pragma warning(pop)
1943 #endif
1944 
1945