SObjectizer  5.8
Loading...
Searching...
No Matches
simple_mtsafe_st_env_infrastructure.cpp
Go to the documentation of this file.
1/*
2 * SObjectizer-5
3 */
4
5/*!
6 * \file
7 * \brief A very simple multithreaded-safe single thread
8 * environment infrastructure.
9 *
10 * \since v.5.5.19
11 */
12
13#include <so_5/impl/st_env_infrastructure_reuse.hpp>
14
15#include <so_5/impl/run_stage.hpp>
16#include <so_5/impl/internal_env_iface.hpp>
17
18#include <so_5/impl/final_dereg_chain_helpers.hpp>
19
20#include <so_5/disp/reuse/data_source_prefix_helpers.hpp>
21
22#include <so_5/environment.hpp>
23#include <so_5/send_functions.hpp>
24
25#include <so_5/details/at_scope_exit.hpp>
26#include <so_5/details/sync_helpers.hpp>
27
28namespace so_5 {
29
30namespace env_infrastructures {
31
32namespace simple_mtsafe {
33
34namespace impl {
35
36namespace helpers {
37
38template< typename Action >
39auto
42 Action && action ) -> decltype(action())
43 {
47 } );
48
49 return action();
50 }
51
52} /* namespace helpers */
53
54//! A short name for namespace with run-time stats stuff.
55namespace stats = ::so_5::stats;
56
57//! A short name for namespace with reusable stuff.
58namespace reusable = ::so_5::env_infrastructures::st_reusable_stuff;
59
60/*!
61 * \brief Status of the main thread on which environment is working.
62 *
63 * Main thread can handle some events or can sleep and wait for new events.
64 *
65 * \since v.5.5.19
66 */
68 {
69 working,
71 };
72
73/*!
74 * \brief A bunch of sync objects which need to be shared between
75 * various parts of env_infrastructure.
76 *
77 * \since v.5.5.19
78 */
80 {
81 //! Main lock for environment infrastructure.
83 //! A condition to sleep on when no activities to handle.
85
86 //! The current status of the main thread.
88 };
89
90/*!
91 * \note
92 * Mutex from sync_objects must be already acquired!
93 */
94inline void
96 {
97 if( main_thread_status_t::waiting == sync_objects.m_status )
98 sync_objects.m_wakeup_condition.notify_one();
99 }
100
101//
102// shutdown_status_t
103//
104using shutdown_status_t = reusable::shutdown_status_t;
105
106//
107// event_queue_impl_t
108//
109/*!
110 * \brief Implementation of event_queue interface for this type of
111 * environment infrastructure.
112 *
113 * \since v.5.5.19
114 */
115class event_queue_impl_t final : public so_5::event_queue_t
116 {
117 public :
118 //! Type for representation of statistical data for this event queue.
119 struct stats_t
120 {
121 //! The current size of the demands queue.
123 };
124
126 : m_sync_objects( sync_objects )
127 {}
128
129 /*!
130 * \note
131 * This method locks the main mutex by itself.
132 */
133 void
134 push( execution_demand_t demand ) override
135 {
136 std::lock_guard< std::mutex > lock( m_sync_objects.m_lock );
137
138 m_demands.push_back( std::move(demand) );
139
141 }
142
143 /*!
144 * \note
145 * Delegates the work to the push() method.
146 */
147 void
149 {
150 this->push( std::move(demand) );
151 }
152
153 /*!
154 * \note
155 * Delegates the work to the push() method.
156 *
157 * \attention
158 * Terminates the whole application if the push() throws.
159 */
160 void
161 push_evt_finish( execution_demand_t demand ) noexcept override
162 {
163 this->push( std::move(demand) );
164 }
165
166 /*!
167 * \note
168 * This method locks the main mutex by itself.
169 */
170 stats_t
172 {
173 std::lock_guard< std::mutex > lock( m_sync_objects.m_lock );
174
175 return { m_demands.size() };
176 }
177
178 //! Type for result of extraction operation.
179 enum class pop_result_t
180 {
181 extracted,
183 };
184
185 /*!
186 * \note
187 * NOTE: this method must be called only when main thread's mutex
188 * is locked.
189 */
191 pop( execution_demand_t & receiver ) noexcept
192 {
193 if( !m_demands.empty() )
194 {
195 receiver = std::move(m_demands.front());
196 m_demands.pop_front();
198 }
199
201 }
202
203 private :
205
207 };
208
209//
210// coop_repo_t
211//
212/*!
213 * \brief Implementation of coop_repository for
214 * simple thread-safe single-threaded environment infrastructure.
215 *
216 * \since v.5.5.19
217 */
218using coop_repo_t = reusable::coop_repo_t;
219
220//
221// disp_ds_name_parts_t
222//
223/*!
224 * \brief A special class for generation of names for dispatcher data sources.
225 *
226 * \since v.5.5.19
227 */
229 {
230 static constexpr const char *
231 disp_type_part() noexcept { return "mtsafe_st_env"; }
232 };
233
234//
235// default_dispatcher_t
236//
237/*!
238 * \brief An implementation of dispatcher to be used in
239 * places where default dispatcher is needed.
240 *
241 * \tparam Activity_Tracker a type of activity tracker to be used
242 * for run-time statistics.
243 *
244 * \since v.5.5.19
245 */
246template< typename Activity_Tracker >
247using default_dispatcher_t =
248 reusable::default_dispatcher_t<
249 event_queue_impl_t,
250 Activity_Tracker,
252
253//
254// stats_controller_t
255//
256/*!
257 * \brief Implementation of stats_controller for that type of
258 * single-threaded environment.
259 *
260 * \since v.5.5.19
261 */
264
265//
266// env_infrastructure_t
267//
268/*!
269 * \brief Default implementation of multithreaded environment infrastructure.
270 *
271 * \tparam Activity_Tracker A type for tracking activity of main working thread.
272 *
273 * \since v.5.5.19
274 */
275template< typename Activity_Tracker >
278 {
279 public :
281 //! Environment to work in.
282 environment_t & env,
283 //! Factory for timer manager.
284 timer_manager_factory_t timer_factory,
285 //! Error logger necessary for timer_manager.
286 error_logger_shptr_t error_logger,
287 //! Cooperation action listener.
288 coop_listener_unique_ptr_t coop_listener,
289 //! Mbox for distribution of run-time stats.
290 mbox_t stats_distribution_mbox );
291
292 void
293 launch( env_init_t init_fn ) override;
294
295 void
296 stop() noexcept override;
297
298 [[nodiscard]]
300 make_coop(
301 coop_handle_t parent,
302 disp_binder_shptr_t default_binder ) override;
303
304 [[nodiscard]]
307 coop_unique_holder_t coop ) override;
308
309 void
311 coop_shptr_t coop ) noexcept override;
312
313 bool
315 coop_shptr_t coop ) noexcept override;
316
319 const std::type_index & type_wrapper,
320 const message_ref_t & msg,
321 const mbox_t & mbox,
322 std::chrono::steady_clock::duration pause,
323 std::chrono::steady_clock::duration period ) override;
324
325 void
327 const std::type_index & type_wrapper,
328 const message_ref_t & msg,
329 const mbox_t & mbox,
330 std::chrono::steady_clock::duration pause ) override;
331
332 stats::controller_t &
333 stats_controller() noexcept override;
334
335 stats::repository_t &
336 stats_repository() noexcept override;
337
340
342 query_timer_thread_stats() override;
343
345 make_default_disp_binder() override;
346
347 private :
349
350 //! All sync objects to be shared between different parts.
352
353 /*!
354 * \brief The chain of coops for the final deregistration.
355 *
356 * \since v.5.8.0
357 */
359
360 //! Status of shutdown procedure.
361 shutdown_status_t m_shutdown_status{ shutdown_status_t::not_started };
362
363 //! A collector for elapsed timers.
364 reusable::actual_elapsed_timers_collector_t m_timers_collector;
365
366 //! A timer manager to be used.
368
369 //! Queue for execution_demands which must be handled on the main thread.
370 event_queue_impl_t m_event_queue;
371
372 //! Repository of registered coops.
373 coop_repo_t m_coop_repo;
374
375 //! Actual activity tracker for main working thread.
376 Activity_Tracker m_activity_tracker;
377
378 //! Dispatcher to be used as default dispatcher.
379 /*!
380 * \note
381 * Has an actual value only inside launch() method.
382 */
384
385 //! Stats controller for this environment.
387
388 void
390 env_init_t init_fn );
391
392 void
394 env_init_t init_fn );
395
396 void
397 run_main_loop() noexcept;
398
399 void
401 std::unique_lock< std::mutex > & acquired_lock ) noexcept;
402
403 void
405 std::unique_lock< std::mutex > & acquired_lock ) noexcept;
406
407 void
409 std::unique_lock< std::mutex > & acquired_lock ) noexcept;
410
411 void
413 std::unique_lock< std::mutex > & acquired_lock ) noexcept;
414 };
415
416template< typename Activity_Tracker >
418 environment_t & env,
419 timer_manager_factory_t timer_factory,
420 error_logger_shptr_t error_logger,
421 coop_listener_unique_ptr_t coop_listener,
422 mbox_t stats_distribution_mbox )
423 : m_env( env )
433 {}
434
435template< typename Activity_Tracker >
436void
437env_infrastructure_t< Activity_Tracker >::launch( env_init_t init_fn )
438 {
440 }
441
442template< typename Activity_Tracker >
443void
454
455template< typename Activity_Tracker >
457env_infrastructure_t< Activity_Tracker >::make_coop(
458 coop_handle_t parent,
459 disp_binder_shptr_t default_binder )
460 {
461 return m_coop_repo.make_coop(
462 std::move(parent),
464 }
465
466template< typename Activity_Tracker >
470 {
472 }
473
474template< typename Activity_Tracker >
475void
477 coop_shptr_t coop ) noexcept
478 {
480
482
484 }
485
486template< typename Activity_Tracker >
487bool
489 coop_shptr_t coop ) noexcept
490 {
493 }
494
495template< typename Activity_Tracker >
498 const std::type_index & type_wrapper,
499 const message_ref_t & msg,
500 const mbox_t & mbox,
501 std::chrono::steady_clock::duration pause,
502 std::chrono::steady_clock::duration period )
503 {
505
508 mbox,
509 msg,
510 pause,
511 period );
512
514
515 return timer;
516 }
517
518template< typename Activity_Tracker >
519void
521 const std::type_index & type_wrapper,
522 const message_ref_t & msg,
523 const mbox_t & mbox,
524 std::chrono::steady_clock::duration pause )
525 {
527
530 mbox,
531 msg,
532 pause,
534
536 }
537
538template< typename Activity_Tracker >
539stats::controller_t &
540env_infrastructure_t< Activity_Tracker >::stats_controller() noexcept
541 {
542 return m_stats_controller;
543 }
544
545template< typename Activity_Tracker >
546stats::repository_t &
547env_infrastructure_t< Activity_Tracker >::stats_repository() noexcept
548 {
549 return m_stats_controller;
550 }
551
552template< typename Activity_Tracker >
566
567template< typename Activity_Tracker >
575
576template< typename Activity_Tracker >
579 {
580 return { m_default_disp };
581 }
582
583template< typename Activity_Tracker >
584void
586 env_init_t init_fn )
587 {
588 ::so_5::impl::run_stage(
589 "run_default_dispatcher",
590 [this] {
596 },
597 [this] {
599 },
600 [this, init_func=std::move(init_fn)] {
602 } );
603 }
604
605template< typename Activity_Tracker >
606void
608 env_init_t init_fn )
609 {
610 /*
611 If init_fn throws an exception we can found ourselves
612 in a situation where there are some working coops.
613 Those coops should be correctly deregistered. It means
614 that we should usual main loop even in the case of
615 an exception from init_fn. But this main loop should
616 work only until all coops will be deregistered.
617
618 To do that we will catch an exception from init and
619 initiate shutdown even before the call to run_main_loop().
620 Then we call run_main_loop() and wail for its completion.
621 Then we reraise the exception caught.
622
623 Note that in this scheme run_main_loop() should be
624 noexcept function because otherwise we will loose the
625 initial exception from init_fn.
626 */
628 try
629 {
631 }
632 catch( ... )
633 {
634 // We can't restore if there will be an exception.
636 // Store the content of the exception to reraise it later.
638 // Execution should be stopped.
639 stop();
640 } );
641 }
642
643 // We don't expect exceptions from the main loop.
646 } );
647
648 // If there was an exception from init_fn this exception
649 // should be rethrown.
652 }
653
654template< typename Activity_Tracker >
655void
656env_infrastructure_t< Activity_Tracker >::run_main_loop() noexcept
657 {
658 // Assume that waiting for new demands is started.
659 // This call is necessary because if there is a demand
660 // in event queue then m_activity_tracker.wait_stopped() will be
661 // called without previous m_activity_tracker.wait_started().
663
664 // Acquire the main lock for the first time.
665 // It will be released and reacquired many times later.
667 for(;;)
668 {
669 // The first step: all pending final deregs must be processed.
671
672 // There can be pending shutdown operation. It must be handled.
675 break;
676
677 // The next step: all timers must be converted to events.
679
680 // The last step: an attempt to process demand.
681 // Or sleep for some time until next demand arrived.
683 }
684 }
685
686template< typename Activity_Tracker >
687void
688env_infrastructure_t< Activity_Tracker >::process_final_deregs_if_any(
689 std::unique_lock< std::mutex > & acquired_lock ) noexcept
690 {
691 // This loop is necessary because it is possible that new
692 // final dereg demand will be added during processing of
693 // the current final dereg demand.
694 while( !m_final_dereg_chain.empty() )
695 {
697
699 [&head] {
701 } );
702 }
703 }
704
705template< typename Activity_Tracker >
706void
707env_infrastructure_t< Activity_Tracker >::perform_shutdown_related_actions_if_needed(
708 std::unique_lock< std::mutex > & acquired_lock ) noexcept
709 {
711 {
712 // Shutdown procedure must be started.
714
715 // All registered cooperations must be deregistered now.
716 // We must unlock out main lock because there is a need to
717 // push a final event for deregisteing agents to event queue.
719 [this] {
721 } );
722 }
723
725 {
726 // If there is no more live coops then shutdown must be completed.
729 }
730 }
731
732template< typename Activity_Tracker >
733void
734env_infrastructure_t< Activity_Tracker >::handle_expired_timers_if_any(
735 std::unique_lock< std::mutex > & acquired_lock ) noexcept
736 {
737 // All expired timers must be collected.
739
741 // Actual handling of elapsed timers must be done
742 // on unlocked env_infrastructure.
743 // It is necessary because there can be an attempt to
744 // deliver delayed/periodic messages into event_queue of
745 // the default_dispatcher. But it is impossible to do if
746 // env_infrastructure's mutex is locked.
748 [this] {
750 } );
751 }
752
753template< typename Activity_Tracker >
754void
755env_infrastructure_t< Activity_Tracker >::try_handle_next_demand(
756 std::unique_lock< std::mutex > & acquired_lock ) noexcept
757 {
759 const auto pop_result = m_event_queue.pop( demand );
760 // If there is no demands we must go to sleep for some time...
762 {
763 // ... but we should go to sleep only if there is no
764 // pending final deregistration actions.
766 {
767 // Tracking time for 'waiting' state must be turned on.
769
770 const auto sleep_time =
772 std::chrono::minutes(1) );
773
775
778 sleep_time );
779
781 }
782 }
783 else
784 {
785 // Tracking time for 'waiting' must be turned off, but
786 // tracking time for 'working' must be tuned on and then off again.
790 [this]{ m_activity_tracker.work_stopped(); } );
791
792 // There is at least one demand to process.
794 [this, &demand] {
796 } );
797 }
798 }
799
800} /* namespace impl */
801
802//
803// simple_mtsafe_st_env_infrastructure_factory
804//
807 {
808 using namespace impl;
809
810 return [infrastructure_params](
811 environment_t & env,
812 environment_params_t & env_params,
813 mbox_t stats_distribution_mbox )
814 {
815 environment_infrastructure_t * obj = nullptr;
816
817 auto timer_manager_factory =
818 infrastructure_params.timer_manager();
819
820 // Create environment infrastructure object in dependence of
821 // work thread activity tracking flag.
822 const auto tracking = env_params.work_thread_activity_tracking();
823 if( work_thread_activity_tracking_t::on == tracking )
824 obj = new env_infrastructure_t< reusable::real_activity_tracker_t >(
825 env,
826 std::move(timer_manager_factory),
827 env_params.so5_error_logger(),
828 env_params.so5_giveout_coop_listener(),
829 std::move(stats_distribution_mbox) );
830 else
831 obj = new env_infrastructure_t< reusable::fake_activity_tracker_t >(
832 env,
833 std::move(timer_manager_factory),
834 env_params.so5_error_logger(),
835 env_params.so5_giveout_coop_listener(),
836 std::move(stats_distribution_mbox) );
837
838 return environment_infrastructure_unique_ptr_t(
839 obj,
840 environment_infrastructure_t::default_deleter() );
841 };
842 }
843
844} /* namespace simple_mtsafe */
845
846} /* namespace env_infrastructures */
847
848} /* namespace so_5 */
Type of smart handle for a cooperation.
A special type that plays role of unique_ptr for coop.
Definition coop.hpp:1342
Default implementation of multithreaded environment infrastructure.
std::shared_ptr< default_dispatcher_t< Activity_Tracker > > m_default_disp
Dispatcher to be used as default dispatcher.
stats::repository_t & stats_repository() noexcept override
Get stats repository for the environment.
void single_timer(const std::type_index &type_wrapper, const message_ref_t &msg, const mbox_t &mbox, std::chrono::steady_clock::duration pause) override
Initiate a delayed message.
reusable::actual_elapsed_timers_collector_t m_timers_collector
A collector for elapsed timers.
so_5::timer_id_t schedule_timer(const std::type_index &type_wrapper, const message_ref_t &msg, const mbox_t &mbox, std::chrono::steady_clock::duration pause, std::chrono::steady_clock::duration period) override
Initiate a timer (delayed or periodic message).
void launch(env_init_t init_fn) override
Do actual launch of SObjectizer's Environment.
coop_handle_t register_coop(coop_unique_holder_t coop) override
Register new cooperation.
void stop() noexcept override
Initiate a signal for shutdown of Environment.
so_5::impl::final_dereg_chain_holder_t m_final_dereg_chain
The chain of coops for the final deregistration.
void perform_shutdown_related_actions_if_needed(std::unique_lock< std::mutex > &acquired_lock) noexcept
main_thread_sync_objects_t m_sync_objects
All sync objects to be shared between different parts.
void try_handle_next_demand(std::unique_lock< std::mutex > &acquired_lock) noexcept
timer_thread_stats_t query_timer_thread_stats() override
Query run-time statistics for timer (thread or manager).
void process_final_deregs_if_any(std::unique_lock< std::mutex > &acquired_lock) noexcept
so_5::environment_infrastructure_t::coop_repository_stats_t query_coop_repository_stats() override
Query run-time statistics for cooperation repository.
coop_unique_holder_t make_coop(coop_handle_t parent, disp_binder_shptr_t default_binder) override
Create an instance of a new coop.
bool final_deregister_coop(coop_shptr_t coop) noexcept override
Do final actions of the cooperation deregistration.
event_queue_impl_t m_event_queue
Queue for execution_demands which must be handled on the main thread.
env_infrastructure_t(environment_t &env, timer_manager_factory_t timer_factory, error_logger_shptr_t error_logger, coop_listener_unique_ptr_t coop_listener, mbox_t stats_distribution_mbox)
disp_binder_shptr_t make_default_disp_binder() override
Create a binder for the default dispatcher.
void handle_expired_timers_if_any(std::unique_lock< std::mutex > &acquired_lock) noexcept
stats::controller_t & stats_controller() noexcept override
Get stats controller for the environment.
Activity_Tracker m_activity_tracker
Actual activity tracker for main working thread.
An implementation of dispatcher to be used in places where default dispatcher is needed.
An interface for environment_infrastructure entity.
SObjectizer Environment.
An interface of event queue for agent.
Helper class for holding the current chain of coops for the final deregistration.
A public interface for control SObjectizer monitoring options.
An interface of data sources repository.
An indentificator for the timer.
Definition timers.hpp:82
#define SO_5_FUNC
Definition declspec.hpp:48
auto unlock_do_and_lock_again(std::unique_lock< std::mutex > &acquired_lock, Action &&action) -> decltype(action())
main_thread_status_t
A short name for namespace with run-time stats stuff.
void wakeup_if_waiting(main_thread_sync_objects_t &sync_objects)
Simple single-threaded environment infrastructure with thread safety.
SO_5_FUNC environment_infrastructure_factory_t factory(params_t &&params)
A factory for creation of simple thread-safe single-thread environment infrastructure object.
Various reusable stuff which can be used in implementation of single-threaded environment infrastruct...
shutdown_status_t
A short name for namespace with run-time stats stuff.
Various implementations of environment_infrastructure.
Details of SObjectizer run-time implementations.
Definition agent.cpp:905
All stuff related to run-time monitoring and statistics.
Private part of message limit implementation.
Definition agent.cpp:33
A special class for generation of names for dispatcher data sources.
A bunch of sync objects which need to be shared between various parts of env_infrastructure.
std::condition_variable m_wakeup_condition
A condition to sleep on when no activities to handle.
Statistical data for run-time monitoring of coop repository content.
A description of event execution demand.
Statistics for run-time monitoring.
Definition timers.hpp:136