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