SObjectizer-5 Extra
simple_mtsafe.hpp
Go to the documentation of this file.
1 /*!
2  * \file
3  * \brief Implementation of Asio-based simple thread safe
4  * environment infrastructure.
5  */
6 
7 #pragma once
8 
9 #include <so_5_extra/env_infrastructures/asio/impl/common.hpp>
10 
11 #include <so_5/version.hpp>
12 #if SO_5_VERSION < SO_5_VERSION_MAKE(5u, 7u, 3u)
13 #error "SObjectizer-5.7.3 is required"
14 #endif
15 
16 #include <so_5/impl/st_env_infrastructure_reuse.hpp>
17 #include <so_5/impl/internal_env_iface.hpp>
18 #include <so_5/details/sync_helpers.hpp>
19 #include <so_5/details/at_scope_exit.hpp>
20 #include <so_5/details/invoke_noexcept_code.hpp>
21 
22 #include <asio.hpp>
23 
24 #include <string_view>
25 
26 namespace so_5 {
27 
28 namespace extra {
29 
30 namespace env_infrastructures {
31 
32 namespace asio {
33 
34 namespace simple_mtsafe {
35 
36 namespace impl {
37 
38 //! A short name for namespace with reusable stuff.
40 
41 //! A short name for namespace with common stuff.
43 
44 //
45 // shutdown_status_t
46 //
48 
49 //
50 // coop_repo_t
51 //
52 /*!
53  * \brief Implementation of coop_repository for
54  * simple thread-safe single-threaded environment infrastructure.
55  */
57 
58 //
59 // stats_controller_t
60 //
61 /*!
62  * \brief Implementation of stats_controller for that type of
63  * single-threaded environment.
64  */
65 using stats_controller_t =
67 
68 //
69 // event_queue_impl_t
70 //
71 /*!
72  * \brief Implementation of event_queue interface for the default dispatcher.
73  *
74  * \tparam Activity_Tracker A type for tracking work thread activity.
75  */
76 template< typename Activity_Tracker >
78  {
80 
81  public :
82  //! Type for representation of statistical data for this event queue.
83  struct stats_t
84  {
85  //! The current size of the demands queue.
87  };
88 
89  //! Initializing constructor.
91  //! Asio's io_context to be used for dispatching.
92  outliving_reference_t<::asio::io_context> io_svc,
93  //! Actual activity tracker.
94  outliving_reference_t<Activity_Tracker> activity_tracker )
95  : m_io_svc(io_svc)
98  {
99  }
100 
101  virtual void
102  push( execution_demand_t demand ) override
103  {
104  // Statistics must be updated.
106 
107  // Now we can schedule processing of the demand.
108  // It ::asio::post fails then statistics must be reverted.
110  [&] {
111  ::asio::post(
112  m_io_svc.get(),
113  [this, d = std::move(demand)]() mutable {
114  // Statistics must be updated.
116 
117  // Update wait statistics.
119  const auto wait_starter = ::so_5::details::at_scope_exit(
120  [this]{ m_activity_tracker.get().wait_started(); } );
121 
122  // The demand can be handled now.
123  // With working time tracking.
125  {
126  // For the case if call_handler will throw.
127  const auto stopper = ::so_5::details::at_scope_exit(
128  [this]{ m_activity_tracker.get().work_stopped(); });
129 
131  }
132  } );
133  },
134  [this] {
136  } );
137  }
138 
139  //! Notification that event queue work is started.
140  void
142  //! ID of the main working thread.
143  current_thread_id_t thread_id )
144  {
146 
147  // There is no any pending demand now. We can start counting
148  // the waiting time.
150  }
151 
152  //! Get the current statistics.
153  stats_t
154  query_stats() const noexcept
155  {
157  }
158 
159  private :
162 
164 
166  };
167 
168 //
169 // disp_ds_name_parts_t
170 //
171 /*!
172  * \brief A class with major part of dispatcher name.
173  */
174 struct disp_ds_name_parts_t final
175  {
176  static constexpr std::string_view
177  disp_type_part() noexcept { return { "asio_mtsafe" }; }
178  };
179 
180 //
181 // default_dispatcher_t
182 //
183 /*!
184  * \brief An implementation of dispatcher to be used in
185  * places where default dispatcher is needed.
186  *
187  * \tparam Activity_Tracker a type of activity tracker to be used
188  * for run-time statistics.
189  *
190  * \since
191  * v.1.3.0
192  */
193 template< typename Activity_Tracker >
195  : public reusable::default_dispatcher_t<
199  {
204 
205  public :
207  outliving_reference_t< environment_t > env,
208  outliving_reference_t< event_queue_impl_t<Activity_Tracker> > event_queue,
209  outliving_reference_t< Activity_Tracker > activity_tracker )
211  {
212  // Event queue should be started manually.
213  // We known that the default dispatcher is created on a thread
214  // that will be used for events dispatching.
215  event_queue.get().start( this->thread_id() );
216  }
217  };
218 
219 //
220 // env_infrastructure_t
221 //
222 /*!
223  * \brief Default implementation of not-thread safe single-threaded environment
224  * infrastructure.
225  *
226  * \attention
227  * This object doesn't have any mutexes. All synchronization is done via
228  * delegation mutating operations to Asio's context (asio::post and
229  * asio::dispatch are used).
230  *
231  * \tparam Activity_Tracker A type of activity tracker to be used.
232  */
233 template< typename Activity_Tracker >
236  {
237  public :
239  //! Asio's io_context to be used.
240  outliving_reference_t<::asio::io_context> io_svc,
241  //! Environment to work in.
242  environment_t & env,
243  //! Cooperation action listener.
244  coop_listener_unique_ptr_t coop_listener,
245  //! Mbox for distribution of run-time stats.
246  mbox_t stats_distribution_mbox );
247 
248  void
249  launch( env_init_t init_fn ) override;
250 
251  void
252  stop() override;
253 
254  [[nodiscard]]
256  make_coop(
259 
262  coop_unique_holder_t coop ) override;
263 
264  void
266  coop_shptr_t coop ) noexcept override;
267 
268  bool
270  coop_shptr_t coop_name ) override;
271 
274  const std::type_index & type_wrapper,
275  const message_ref_t & msg,
276  const mbox_t & mbox,
278  std::chrono::steady_clock::duration period ) override;
279 
280  void
281  single_timer(
282  const std::type_index & type_wrapper,
283  const message_ref_t & msg,
284  const mbox_t & mbox,
285  std::chrono::steady_clock::duration pause ) override;
286 
288  stats_controller() noexcept override;
289 
291  stats_repository() noexcept override;
292 
294  query_coop_repository_stats() override;
295 
297  query_timer_thread_stats() override;
298 
300  make_default_disp_binder() override;
301 
302  private :
303  //! Asio's io_context to be used.
305 
306  //! Actual SObjectizer Environment.
308 
309  //! Status of shutdown procedure.
311 
312  //! Repository of registered coops.
314 
315  //! Actual activity tracker.
316  Activity_Tracker m_activity_tracker;
317 
318  //! Event queue which is necessary for the default dispatcher.
319  event_queue_impl_t< Activity_Tracker > m_event_queue;
320 
321  //! Dispatcher to be used as default dispatcher.
322  /*!
323  * \note
324  * Has an actual value only inside launch() method.
325  */
327 
328  //! Stats controller for this environment.
330 
331  //! Counter of cooperations which are waiting for final deregistration
332  //! step.
333  /*!
334  * It is necessary for building correct run-time stats.
335  */
337 
338  //! The pointer to an exception that was thrown during init phase.
339  /*!
340  * This exception is stored inside a callback posted to Asio.
341  * An then this exception will be rethrown from launch() method
342  * after the shutdown of SObjectizer.
343  */
345 
346  void
347  run_default_dispatcher_and_go_further( env_init_t init_fn );
348 
349  /*!
350  * \note Calls m_io_svc.stop() and m_default_disp.shutdown() if necessary.
351  *
352  * \attention Must be called only for locked object!
353  */
354  void
356  };
357 
358 template< typename Activity_Tracker >
360  outliving_reference_t<::asio::io_context> io_svc,
361  environment_t & env,
362  coop_listener_unique_ptr_t coop_listener,
363  mbox_t stats_distribution_mbox )
364  : m_io_svc( io_svc )
365  , m_env( env )
373  {}
374 
375 template< typename Activity_Tracker >
376 void
377 env_infrastructure_t<Activity_Tracker>::launch( env_init_t init_fn )
378  {
379  // Post initial operation to Asio event loop.
380  ::asio::post( m_io_svc.get(), [this, init = std::move(init_fn)] {
382  } );
383 
384  // Default dispatcher should be destroyed on exit from this function.
387  } );
388 
389  // Tell that there is a work to do.
390  auto work = ::asio::make_work_guard( m_io_svc.get() );
391 
392  // Launch Asio event loop.
393  m_io_svc.get().run();
394 
396  // Some exception was thrown during initialization.
397  // It should be rethrown.
399  }
400 
401 template< typename Activity_Tracker >
402 void
403 env_infrastructure_t<Activity_Tracker>::stop()
404  {
405  // NOTE: if the code below throws then we don't know the actual
406  // state of env_infrastructure. Because of that we just terminate
407  // the whole application the the case of an exception.
413  {
414  // All registered cooperations must be deregistered now.
415  ::asio::dispatch( m_io_svc.get(),
416  [this] {
418 
420 
422  } );
423  }
424  else
425  // Check for shutdown completeness must be performed only
426  // on the main Asio's thread.
427  ::asio::dispatch( m_io_svc.get(), [this] {
429  } );
430  } );
431  }
432 
433 template< typename Activity_Tracker >
435 env_infrastructure_t< Activity_Tracker >::make_coop(
438  {
439  return m_coop_repo.make_coop(
440  std::move(parent),
441  std::move(default_binder) );
442  }
443 
444 template< typename Activity_Tracker >
446 env_infrastructure_t< Activity_Tracker >::register_coop(
448  {
449  return m_coop_repo.register_coop( std::move(coop) );
450  }
451 
452 template< typename Activity_Tracker >
453 void
455  coop_shptr_t coop_to_dereg ) noexcept
456  {
458 
459  ::asio::post( m_io_svc.get(), [this, coop = std::move(coop_to_dereg)] {
463  } );
464  }
465 
466 template< typename Activity_Tracker >
467 bool
469  coop_shptr_t coop )
470  {
473  }
474 
475 template< typename Activity_Tracker >
478  const std::type_index & type_index,
479  const message_ref_t & msg,
480  const mbox_t & mbox,
483  {
484  using namespace asio_common;
485 
486  // We do not control shutdown_status_t here. Because it seems
487  // to be safe to call schedule_timer after call to stop().
488  // New timer will simply ignored during shutdown process.
490  if( period != std::chrono::steady_clock::duration::zero() )
491  {
494  m_io_svc.get(),
495  type_index,
496  msg,
497  mbox,
498  period ) };
499 
500  result = timer_id_t{
502 
504  }
505  else
506  {
509  m_io_svc.get(),
510  type_index,
511  msg,
512  mbox ) };
513 
514  result = timer_id_t{
516 
518  }
519 
520  return result;
521  }
522 
523 template< typename Activity_Tracker >
524 void
526  const std::type_index & type_index,
527  const message_ref_t & msg,
528  const mbox_t & mbox,
529  std::chrono::steady_clock::duration pause )
530  {
531  using namespace asio_common;
532 
533  // We do not control shutdown_status_t here. Because it seems
534  // to be safe to call schedule_timer after call to stop().
535  // New timer will simply ignored during shutdown process.
536 
539  m_io_svc.get(),
540  type_index,
541  msg,
542  mbox ) };
543 
545  }
546 
547 template< typename Activity_Tracker >
549 env_infrastructure_t<Activity_Tracker>::stats_controller() noexcept
550  {
551  return m_stats_controller;
552  }
553 
554 template< typename Activity_Tracker >
556 env_infrastructure_t<Activity_Tracker>::stats_repository() noexcept
557  {
558  return m_stats_controller;
559  }
560 
561 template< typename Activity_Tracker >
564  {
565  const auto stats = m_coop_repo.query_stats();
566 
571  };
572  }
573 
574 template< typename Activity_Tracker >
577  {
578  // NOTE: this type of environment_infrastructure doesn't support
579  // statistics for timers.
580  return { 0, 0 };
581  }
582 
583 template< typename Activity_Tracker >
586  {
587  return { m_default_disp };
588  }
589 
590 template< typename Activity_Tracker >
591 void
593  env_init_t init_fn )
594  {
595  try
596  {
602 
603  // User-supplied init can be called now.
604  init_fn();
605  }
606  catch(...)
607  {
608  // We can't restore if the following fragment throws and exception.
610  // The current exception should be stored to be
611  // rethrown later.
613 
614  // SObjectizer's shutdown should be initiated.
615  stop();
616 
617  // NOTE: pointer to the default dispatcher will be dropped
618  // in launch() method.
619  } );
620  }
621  }
622 
623 template< typename Activity_Tracker >
624 void
626  {
628  {
629  // If there is no more live coops then shutdown must be
630  // completed.
631  if( !m_coop_repo.has_live_coop() )
632  {
634  // Asio's event loop must be broken here!
635  m_io_svc.get().stop();
636  }
637  }
638  }
639 
640 } /* namespace impl */
641 
642 //
643 // factory
644 //
645 /*!
646  * \brief A factory for creation of environment infrastructure based on
647  * Asio's event loop.
648  *
649  * \attention
650  * This environment infrastructure is not a thread safe.
651  *
652  * Usage example:
653  * \code
654 int main()
655 {
656  asio::io_context io_svc;
657 
658  so_5::launch( [](so_5::environment_t & env) {
659  ... // Some initialization stuff.
660  },
661  [&io_svc](so_5::environment_params_t & params) {
662  using asio_env = so_5::extra::env_infrastructures::asio::simple_mtsafe;
663 
664  params.infrastructure_factory( asio_env::factory(io_svc) );
665  } );
666 
667  return 0;
668 }
669  * \endcode
670  */
673  {
674  using namespace impl;
675 
676  return [&io_svc](
677  environment_t & env,
678  environment_params_t & env_params,
679  mbox_t stats_distribution_mbox )
680  {
681  environment_infrastructure_t * obj = nullptr;
682 
683  // Create environment infrastructure object in dependence of
684  // work thread activity tracking flag.
685  const auto tracking = env_params.work_thread_activity_tracking();
686  if( work_thread_activity_tracking_t::on == tracking )
687  obj = new env_infrastructure_t< reusable::real_activity_tracker_t >(
688  outliving_mutable(io_svc),
689  env,
690  env_params.so5_giveout_coop_listener(),
691  std::move(stats_distribution_mbox) );
692  else
693  obj = new env_infrastructure_t< reusable::fake_activity_tracker_t >(
694  outliving_mutable(io_svc),
695  env,
696  env_params.so5_giveout_coop_listener(),
697  std::move(stats_distribution_mbox) );
698 
699  return environment_infrastructure_unique_ptr_t(
700  obj,
701  environment_infrastructure_t::default_deleter() );
702  };
703  }
704 
705 } /* namespace simple_mtsafe */
706 
707 } /* namespace asio */
708 
709 } /* namespace env_infrastructures */
710 
711 } /* namespace extra */
712 
713 } /* namespace so_5 */
Type for representation of statistical data for this event queue.
env_infrastructure_t(outliving_reference_t<::asio::io_context > io_svc, environment_t &env, coop_listener_unique_ptr_t coop_listener, mbox_t stats_distribution_mbox)
std::shared_ptr< default_dispatcher_t< Activity_Tracker > > m_default_disp
Dispatcher to be used as default dispatcher.
environment_infrastructure_factory_t factory(::asio::io_context &io_svc)
A factory for creation of environment infrastructure based on Asio&#39;s event loop.
Implementation of event_queue interface for the default dispatcher.
std::exception_ptr m_exception_from_init
The pointer to an exception that was thrown during init phase.
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
An implementation of dispatcher to be used in places where default dispatcher is needed.
default_dispatcher_t(outliving_reference_t< environment_t > env, outliving_reference_t< event_queue_impl_t< Activity_Tracker > > event_queue, outliving_reference_t< Activity_Tracker > activity_tracker)
Ranges for error codes of each submodules.
Definition: details.hpp:13
stats_t query_stats() const noexcept
Get the current statistics.
void start(current_thread_id_t thread_id)
Notification that event queue work is started.
std::atomic< std::size_t > m_final_dereg_coop_count
Counter of cooperations which are waiting for final deregistration step.
coop_unique_holder_t make_coop(coop_handle_t parent, disp_binder_shptr_t default_binder) override
event_queue_impl_t(outliving_reference_t<::asio::io_context > io_svc, outliving_reference_t< Activity_Tracker > activity_tracker)
Initializing constructor.
so_5::environment_infrastructure_t::coop_repository_stats_t query_coop_repository_stats() override
event_queue_impl_t< Activity_Tracker > m_event_queue
Event queue which is necessary for the default dispatcher.
stats_controller_t m_stats_controller
Stats controller for this environment.
Default implementation of not-thread safe single-threaded environment infrastructure.
std::atomic< shutdown_status_t > m_shutdown_status
Status of shutdown procedure.
outliving_reference_t< ::asio::io_context > m_io_svc
Asio&#39;s io_context to be used.
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