SObjectizer-5 Extra
simple_not_mtsafe.hpp
Go to the documentation of this file.
1 /*!
2  * \file
3  * \brief Implementation of Asio-based simple not 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, 8u, 0u)
13 #error "SObjectizer-5.8.0 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_not_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.
42 /*!
43  * \since
44  * v.1.0.2
45  */
47 
48 //
49 // shutdown_status_t
50 //
52 
53 //
54 // coop_repo_t
55 //
56 /*!
57  * \brief Implementation of coop_repository for
58  * simple thread-safe single-threaded environment infrastructure.
59  */
61 
62 //
63 // stats_controller_t
64 //
65 /*!
66  * \brief Implementation of stats_controller for that type of
67  * single-threaded environment.
68  */
69 using stats_controller_t =
71 
72 //
73 // event_queue_impl_t
74 //
75 /*!
76  * \brief Implementation of event_queue interface for the default dispatcher.
77  *
78  * \tparam Activity_Tracker A type for tracking work thread activity.
79  */
80 template< typename Activity_Tracker >
82  {
83  public :
84  //! Type for representation of statistical data for this event queue.
85  struct stats_t
86  {
87  //! The current size of the demands queue.
89  };
90 
91  //! Initializing constructor.
93  //! Asio's io_context to be used for dispatching.
94  outliving_reference_t<::asio::io_context> io_svc,
95  //! Actual activity tracker.
96  outliving_reference_t<Activity_Tracker> activity_tracker )
97  : m_io_svc(io_svc)
99  {}
100 
101  void
102  push( execution_demand_t demand ) override
103  {
104  ::asio::post(
105  m_io_svc.get(),
106  [this, d = std::move(demand)]() mutable {
107  // Statistics must be updated.
109 
110  // The demand can be handled now.
111  // With working time tracking.
113  {
114  // For the case if call_handler will throw.
115  const auto stopper = ::so_5::details::at_scope_exit(
116  [this]{ m_activity_tracker.get().work_stopped(); });
117 
119  }
120 
121  // If there is no any pending demands then
122  // waiting must be started.
123  if( !m_stats.m_demands_count )
125  } );
126 
127  if( !m_stats.m_demands_count )
128  // Waiting must be stopped because we have received an event.
130 
131  // Increment demands count only if post doesn't throw.
133  }
134 
135  void
136  push_evt_start( execution_demand_t demand ) override
137  {
138  // Just delegate to the ordinary push().
139  this->push( std::move(demand) );
140  }
141 
142  void
143  push_evt_finish( execution_demand_t demand ) noexcept override
144  {
145  // Just delegate to the ordinary push() despite
146  // the fact that push() isn't noexcept.
147  this->push( std::move(demand) );
148  }
149 
150  //! Notification that event queue work is started.
151  void
153  //! ID of the main working thread.
154  current_thread_id_t thread_id )
155  {
157 
158  // There is no any pending demand now. We can start counting
159  // the waiting time.
161  }
162 
163  //! Get the current statistics.
164  stats_t
165  query_stats() const noexcept
166  {
167  return m_stats;
168  }
169 
170  private :
173 
176  };
177 
178 //
179 // disp_ds_name_parts_t
180 //
181 /*!
182  * \brief A class with major part of dispatcher name.
183  */
184 struct disp_ds_name_parts_t final
185  {
186  static constexpr std::string_view
187  disp_type_part() noexcept { return { "asio_not_mtsafe" }; }
188  };
189 
190 //
191 // default_dispatcher_t
192 //
193 /*!
194  * \brief An implementation of dispatcher to be used in
195  * places where default dispatcher is needed.
196  *
197  * \tparam Activity_Tracker a type of activity tracker to be used
198  * for run-time statistics.
199  *
200  * \since
201  * v.1.3.0
202  */
203 template< typename Activity_Tracker >
205  : public reusable::default_dispatcher_t<
209  {
214 
215  public :
217  outliving_reference_t< environment_t > env,
218  outliving_reference_t< event_queue_impl_t<Activity_Tracker> > event_queue,
219  outliving_reference_t< Activity_Tracker > activity_tracker )
221  {
222  // Event queue should be started manually.
223  // We known that the default dispatcher is created on a thread
224  // that will be used for events dispatching.
225  event_queue.get().start( this->thread_id() );
226  }
227  };
228 
229 //
230 // env_infrastructure_t
231 //
232 /*!
233  * \brief Default implementation of not-thread safe single-threaded environment
234  * infrastructure.
235  *
236  * \attention
237  * This class doesn't have any mutex inside.
238  *
239  * \tparam Activity_Tracker A type of activity tracker to be used.
240  */
241 template< typename Activity_Tracker >
244  {
245  public :
247  //! Asio's io_context to be used.
248  outliving_reference_t<::asio::io_context> io_svc,
249  //! Environment to work in.
250  environment_t & env,
251  //! Cooperation action listener.
252  coop_listener_unique_ptr_t coop_listener,
253  //! Mbox for distribution of run-time stats.
254  mbox_t stats_distribution_mbox );
255 
256  void
257  launch( env_init_t init_fn ) override;
258 
259  /*!
260  * \attention
261  * Since SO-5.8.0 this method should be a noexcept, but
262  * Asio's post() can throw. In that case the whole application
263  * will be terminated.
264  */
265  void
266  stop() noexcept override;
267 
268  [[nodiscard]]
270  make_coop(
273 
276  coop_unique_holder_t coop ) override;
277 
278  void
280  coop_shptr_t coop ) noexcept override;
281 
282  bool
284  coop_shptr_t coop_name ) noexcept override;
285 
288  const std::type_index & type_wrapper,
289  const message_ref_t & msg,
290  const mbox_t & mbox,
292  std::chrono::steady_clock::duration period ) override;
293 
294  void
295  single_timer(
296  const std::type_index & type_wrapper,
297  const message_ref_t & msg,
298  const mbox_t & mbox,
299  std::chrono::steady_clock::duration pause ) override;
300 
302  stats_controller() noexcept override;
303 
305  stats_repository() noexcept override;
306 
308  query_coop_repository_stats() override;
309 
311  query_timer_thread_stats() override;
312 
314  make_default_disp_binder() override;
315 
316  private :
317  //! Asio's io_context to be used.
319 
320  //! Actual SObjectizer Environment.
322 
323  //! Status of shutdown procedure.
325 
326  //! Repository of registered coops.
328 
329  //! Actual activity tracker.
330  Activity_Tracker m_activity_tracker;
331 
332  //! Event queue which is necessary for the default dispatcher.
333  event_queue_impl_t< Activity_Tracker > m_event_queue;
334 
335  //! Dispatcher to be used as default dispatcher.
336  /*!
337  * \note
338  * Has an actual value only inside launch() method.
339  */
341 
342  //! Stats controller for this environment.
344 
345  //! Counter of cooperations which are waiting for final deregistration
346  //! step.
347  /*!
348  * It is necessary for building correct run-time stats.
349  */
351 
352  //! The pointer to an exception that was thrown during init phase.
353  /*!
354  * This exception is stored inside a callback posted to Asio.
355  * An then this exception will be rethrown from launch() method
356  * after the shutdown of SObjectizer.
357  */
359 
360  void
361  run_default_dispatcher_and_go_further( env_init_t init_fn );
362 
363  /*!
364  * \note Calls m_io_svc.stop() if necessary.
365  */
366  void
368  };
369 
370 template< typename Activity_Tracker >
372  outliving_reference_t<::asio::io_context> io_svc,
373  environment_t & env,
374  coop_listener_unique_ptr_t coop_listener,
375  mbox_t stats_distribution_mbox )
376  : m_io_svc( io_svc )
377  , m_env( env )
383  {}
384 
385 template< typename Activity_Tracker >
386 void
387 env_infrastructure_t<Activity_Tracker>::launch( env_init_t init_fn )
388  {
389  // Post initial operation to Asio event loop.
390  ::asio::post( m_io_svc.get(), [this, init = std::move(init_fn)] {
392  } );
393 
394  // Default dispatcher should be destroyed on exit from this function.
397  } );
398 
399  // Launch Asio event loop.
400  m_io_svc.get().run();
401 
402  // Event loop can be finished in two cases:
403  // 1. SObjectizer has been shut down. We should do nothing in that case.
404  // 2. There is no more work for Asio. But SObjectizer is still working.
405  // In that case a normal shutdown must be initiated.
406  //
407  const auto still_working = [this]{
409  };
410 
411  if( still_working() )
412  {
413  // Initiate a shutdown operation.
414  stop();
415  // Run Asio event loop until shutdown will be finished.
416  do
417  {
418  m_io_svc.get().restart();
419  m_io_svc.get().run();
420  }
421  while( still_working() );
422  }
423 
425  // Some exception was thrown during initialization.
426  // It should be rethrown.
428  }
429 
430 template< typename Activity_Tracker >
431 void
432 env_infrastructure_t<Activity_Tracker>::stop() noexcept
433  {
435  {
437  ::asio::post( m_io_svc.get(), [this] {
438  // Shutdown procedure must be started.
440 
441  // All registered cooperations must be deregistered now.
443 
445  } );
446  }
447  else
449  }
450 
451 template< typename Activity_Tracker >
453 env_infrastructure_t< Activity_Tracker >::make_coop(
456  {
457  return m_coop_repo.make_coop(
458  std::move(parent),
459  std::move(default_binder) );
460  }
461 
462 template< typename Activity_Tracker >
464 env_infrastructure_t< Activity_Tracker >::register_coop(
466  {
467  return m_coop_repo.register_coop( std::move(coop) );
468  }
469 
470 template< typename Activity_Tracker >
471 void
473  coop_shptr_t coop_to_dereg ) noexcept
474  {
476 
477  ::asio::post( m_io_svc.get(), [this, coop = std::move(coop_to_dereg)] {
481  } );
482  }
483 
484 template< typename Activity_Tracker >
485 bool
487  coop_shptr_t coop ) noexcept
488  {
491  }
492 
493 template< typename Activity_Tracker >
496  const std::type_index & type_index,
497  const message_ref_t & msg,
498  const mbox_t & mbox,
501  {
502  using namespace asio_common;
503 
505  if( period != std::chrono::steady_clock::duration::zero() )
506  {
509  m_io_svc.get(),
510  type_index,
511  msg,
512  mbox,
513  period ) };
514 
515  result = timer_id_t{
517 
519  }
520  else
521  {
524  m_io_svc.get(),
525  type_index,
526  msg,
527  mbox ) };
528 
529  result = timer_id_t{
531 
533  }
534 
535  return result;
536  }
537 
538 template< typename Activity_Tracker >
539 void
541  const std::type_index & type_index,
542  const message_ref_t & msg,
543  const mbox_t & mbox,
544  std::chrono::steady_clock::duration pause )
545  {
546  using namespace asio_common;
547 
550  m_io_svc.get(),
551  type_index,
552  msg,
553  mbox ) };
554 
556  }
557 
558 template< typename Activity_Tracker >
560 env_infrastructure_t<Activity_Tracker>::stats_controller() noexcept
561  {
562  return m_stats_controller;
563  }
564 
565 template< typename Activity_Tracker >
567 env_infrastructure_t<Activity_Tracker>::stats_repository() noexcept
568  {
569  return m_stats_controller;
570  }
571 
572 template< typename Activity_Tracker >
575  {
576  const auto stats = m_coop_repo.query_stats();
577 
582  };
583  }
584 
585 template< typename Activity_Tracker >
588  {
589  // Note: this type of environment_infrastructure doesn't support
590  // statistics for timers.
591  return { 0, 0 };
592  }
593 
594 template< typename Activity_Tracker >
597  {
598  return { m_default_disp };
599  }
600 
601 template< typename Activity_Tracker >
602 void
604  env_init_t init_fn )
605  {
606  try
607  {
613 
614  // User-supplied init can be called now.
615  init_fn();
616  }
617  catch(...)
618  {
619  // We can't restore if the following fragment throws and exception.
621  // The current exception should be stored to be
622  // rethrown later.
624 
625  // SObjectizer's shutdown should be initiated.
626  stop();
627 
628  // NOTE: pointer to the default dispatcher will be dropped
629  // in launch() method.
630  } );
631  }
632  }
633 
634 template< typename Activity_Tracker >
635 void
637  {
639  {
640  // If there is no more live coops then shutdown must be
641  // completed.
642  if( !m_coop_repo.has_live_coop() )
643  {
645  // Asio's event loop must be broken here!
646  m_io_svc.get().stop();
647  }
648  }
649  }
650 
651 //
652 // ensure_autoshutdown_enabled
653 //
654 /*!
655  * Throws an exception if autoshutdown feature is disabled.
656  */
657 void
659  const environment_params_t & env_params )
660  {
661  if( env_params.autoshutdown_disabled() )
662  SO_5_THROW_EXCEPTION( rc_autoshutdown_must_be_enabled,
663  "autoshutdown feature must be enabled for "
664  "so_5::env_infrastructures::simple_not_mtsafe" );
665  }
666 
667 } /* namespace impl */
668 
669 //
670 // factory
671 //
672 /*!
673  * \brief A factory for creation of environment infrastructure based on
674  * Asio's event loop.
675  *
676  * \attention
677  * This environment infrastructure is not a thread safe.
678  *
679  * Usage example:
680  * \code
681 int main()
682 {
683  asio::io_context io_svc;
684 
685  so_5::launch( [](so_5::environment_t & env) {
686  ... // Some initialization stuff.
687  },
688  [&io_svc](so_5::environment_params_t & params) {
689  using asio_env = so_5::extra::env_infrastructures::asio::simple_not_mtsafe;
690 
691  params.infrastructure_factory( asio_env::factory(io_svc) );
692  } );
693 
694  return 0;
695 }
696  * \endcode
697  */
700  {
701  using namespace impl;
702 
703  return [&io_svc](
704  environment_t & env,
705  environment_params_t & env_params,
706  mbox_t stats_distribution_mbox )
707  {
708  ensure_autoshutdown_enabled( env_params );
709 
710  environment_infrastructure_t * obj = nullptr;
711 
712  // Create environment infrastructure object in dependence of
713  // work thread activity tracking flag.
714  const auto tracking = env_params.work_thread_activity_tracking();
715  if( work_thread_activity_tracking_t::on == tracking )
716  obj = new env_infrastructure_t< reusable::real_activity_tracker_t >(
717  outliving_mutable(io_svc),
718  env,
719  env_params.so5_giveout_coop_listener(),
720  std::move(stats_distribution_mbox) );
721  else
722  obj = new env_infrastructure_t< reusable::fake_activity_tracker_t >(
723  outliving_mutable(io_svc),
724  env,
725  env_params.so5_giveout_coop_listener(),
726  std::move(stats_distribution_mbox) );
727 
728  return environment_infrastructure_unique_ptr_t(
729  obj,
730  environment_infrastructure_t::default_deleter() );
731  };
732  }
733 
734 } /* namespace simple_not_mtsafe */
735 
736 } /* namespace asio */
737 
738 } /* namespace env_infrastructures */
739 
740 } /* namespace extra */
741 
742 } /* namespace so_5 */
Type for representation of statistical data for this event queue.
event_queue_impl_t(outliving_reference_t<::asio::io_context > io_svc, outliving_reference_t< Activity_Tracker > activity_tracker)
Initializing constructor.
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
void start(current_thread_id_t thread_id)
Notification that event queue work is started.
An implementation of dispatcher to be used in places where default dispatcher is needed.
void ensure_autoshutdown_enabled(const environment_params_t &env_params)
Implementation of event_queue interface for the default dispatcher.
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.
Ranges for error codes of each submodules.
Definition: details.hpp:13
std::size_t m_final_dereg_coop_count
Counter of cooperations which are waiting for final deregistration step.
so_5::environment_infrastructure_t::coop_repository_stats_t query_coop_repository_stats() override
outliving_reference_t< ::asio::io_context > m_io_svc
Asio&#39;s io_context to be used.
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)
environment_infrastructure_factory_t factory(::asio::io_context &io_svc)
A factory for creation of environment infrastructure based on Asio&#39;s event loop.
Default implementation of not-thread safe single-threaded environment infrastructure.
std::shared_ptr< default_dispatcher_t< Activity_Tracker > > m_default_disp
Dispatcher to be used as default dispatcher.
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
coop_unique_holder_t make_coop(coop_handle_t parent, disp_binder_shptr_t default_binder) override
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::exception_ptr m_exception_from_init
The pointer to an exception that was thrown during init phase.