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, 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_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  //! Notification that event queue work is started.
136  void
138  //! ID of the main working thread.
139  current_thread_id_t thread_id )
140  {
142 
143  // There is no any pending demand now. We can start counting
144  // the waiting time.
146  }
147 
148  //! Get the current statistics.
149  stats_t
150  query_stats() const noexcept
151  {
152  return m_stats;
153  }
154 
155  private :
158 
161  };
162 
163 //
164 // disp_ds_name_parts_t
165 //
166 /*!
167  * \brief A class with major part of dispatcher name.
168  */
169 struct disp_ds_name_parts_t final
170  {
171  static constexpr std::string_view
172  disp_type_part() noexcept { return { "asio_not_mtsafe" }; }
173  };
174 
175 //
176 // default_dispatcher_t
177 //
178 /*!
179  * \brief An implementation of dispatcher to be used in
180  * places where default dispatcher is needed.
181  *
182  * \tparam Activity_Tracker a type of activity tracker to be used
183  * for run-time statistics.
184  *
185  * \since
186  * v.1.3.0
187  */
188 template< typename Activity_Tracker >
190  : public reusable::default_dispatcher_t<
194  {
199 
200  public :
202  outliving_reference_t< environment_t > env,
203  outliving_reference_t< event_queue_impl_t<Activity_Tracker> > event_queue,
204  outliving_reference_t< Activity_Tracker > activity_tracker )
206  {
207  // Event queue should be started manually.
208  // We known that the default dispatcher is created on a thread
209  // that will be used for events dispatching.
210  event_queue.get().start( this->thread_id() );
211  }
212  };
213 
214 //
215 // env_infrastructure_t
216 //
217 /*!
218  * \brief Default implementation of not-thread safe single-threaded environment
219  * infrastructure.
220  *
221  * \attention
222  * This class doesn't have any mutex inside.
223  *
224  * \tparam Activity_Tracker A type of activity tracker to be used.
225  */
226 template< typename Activity_Tracker >
229  {
230  public :
232  //! Asio's io_context to be used.
233  outliving_reference_t<::asio::io_context> io_svc,
234  //! Environment to work in.
235  environment_t & env,
236  //! Cooperation action listener.
237  coop_listener_unique_ptr_t coop_listener,
238  //! Mbox for distribution of run-time stats.
239  mbox_t stats_distribution_mbox );
240 
241  void
242  launch( env_init_t init_fn ) override;
243 
244  void
245  stop() override;
246 
247  [[nodiscard]]
249  make_coop(
252 
255  coop_unique_holder_t coop ) override;
256 
257  void
259  coop_shptr_t coop ) noexcept override;
260 
261  bool
263  coop_shptr_t coop_name ) override;
264 
267  const std::type_index & type_wrapper,
268  const message_ref_t & msg,
269  const mbox_t & mbox,
271  std::chrono::steady_clock::duration period ) override;
272 
273  void
274  single_timer(
275  const std::type_index & type_wrapper,
276  const message_ref_t & msg,
277  const mbox_t & mbox,
278  std::chrono::steady_clock::duration pause ) override;
279 
281  stats_controller() noexcept override;
282 
284  stats_repository() noexcept override;
285 
287  query_coop_repository_stats() override;
288 
290  query_timer_thread_stats() override;
291 
293  make_default_disp_binder() override;
294 
295  private :
296  //! Asio's io_context to be used.
298 
299  //! Actual SObjectizer Environment.
301 
302  //! Status of shutdown procedure.
304 
305  //! Repository of registered coops.
307 
308  //! Actual activity tracker.
309  Activity_Tracker m_activity_tracker;
310 
311  //! Event queue which is necessary for the default dispatcher.
312  event_queue_impl_t< Activity_Tracker > m_event_queue;
313 
314  //! Dispatcher to be used as default dispatcher.
315  /*!
316  * \note
317  * Has an actual value only inside launch() method.
318  */
320 
321  //! Stats controller for this environment.
323 
324  //! Counter of cooperations which are waiting for final deregistration
325  //! step.
326  /*!
327  * It is necessary for building correct run-time stats.
328  */
330 
331  //! The pointer to an exception that was thrown during init phase.
332  /*!
333  * This exception is stored inside a callback posted to Asio.
334  * An then this exception will be rethrown from launch() method
335  * after the shutdown of SObjectizer.
336  */
338 
339  void
340  run_default_dispatcher_and_go_further( env_init_t init_fn );
341 
342  /*!
343  * \note Calls m_io_svc.stop() if necessary.
344  */
345  void
347  };
348 
349 template< typename Activity_Tracker >
351  outliving_reference_t<::asio::io_context> io_svc,
352  environment_t & env,
353  coop_listener_unique_ptr_t coop_listener,
354  mbox_t stats_distribution_mbox )
355  : m_io_svc( io_svc )
356  , m_env( env )
362  {}
363 
364 template< typename Activity_Tracker >
365 void
366 env_infrastructure_t<Activity_Tracker>::launch( env_init_t init_fn )
367  {
368  // Post initial operation to Asio event loop.
369  ::asio::post( m_io_svc.get(), [this, init = std::move(init_fn)] {
371  } );
372 
373  // Default dispatcher should be destroyed on exit from this function.
376  } );
377 
378  // Launch Asio event loop.
379  m_io_svc.get().run();
380 
381  // Event loop can be finished in two cases:
382  // 1. SObjectizer has been shut down. We should do nothing in that case.
383  // 2. There is no more work for Asio. But SObjectizer is still working.
384  // In that case a normal shutdown must be initiated.
385  //
386  const auto still_working = [this]{
388  };
389 
390  if( still_working() )
391  {
392  // Initiate a shutdown operation.
393  stop();
394  // Run Asio event loop until shutdown will be finished.
395  do
396  {
397  m_io_svc.get().restart();
398  m_io_svc.get().run();
399  }
400  while( still_working() );
401  }
402 
404  // Some exception was thrown during initialization.
405  // It should be rethrown.
407  }
408 
409 template< typename Activity_Tracker >
410 void
411 env_infrastructure_t<Activity_Tracker>::stop()
412  {
413  // Note: if the code below throws then we don't know the actual
414  // state of env_infrastructure. Because of that we just terminate
415  // the whole application in the case of an exception.
418  {
420  ::asio::post( m_io_svc.get(), [this] {
421  // Shutdown procedure must be started.
423 
424  // All registered cooperations must be deregistered now.
426 
428  } );
429  }
430  else
432  } );
433  }
434 
435 template< typename Activity_Tracker >
437 env_infrastructure_t< Activity_Tracker >::make_coop(
440  {
441  return m_coop_repo.make_coop(
442  std::move(parent),
443  std::move(default_binder) );
444  }
445 
446 template< typename Activity_Tracker >
448 env_infrastructure_t< Activity_Tracker >::register_coop(
450  {
451  return m_coop_repo.register_coop( std::move(coop) );
452  }
453 
454 template< typename Activity_Tracker >
455 void
457  coop_shptr_t coop_to_dereg ) noexcept
458  {
460 
461  ::asio::post( m_io_svc.get(), [this, coop = std::move(coop_to_dereg)] {
465  } );
466  }
467 
468 template< typename Activity_Tracker >
469 bool
471  coop_shptr_t coop )
472  {
475  }
476 
477 template< typename Activity_Tracker >
480  const std::type_index & type_index,
481  const message_ref_t & msg,
482  const mbox_t & mbox,
485  {
486  using namespace asio_common;
487 
489  if( period != std::chrono::steady_clock::duration::zero() )
490  {
493  m_io_svc.get(),
494  type_index,
495  msg,
496  mbox,
497  period ) };
498 
499  result = timer_id_t{
501 
503  }
504  else
505  {
508  m_io_svc.get(),
509  type_index,
510  msg,
511  mbox ) };
512 
513  result = timer_id_t{
515 
517  }
518 
519  return result;
520  }
521 
522 template< typename Activity_Tracker >
523 void
525  const std::type_index & type_index,
526  const message_ref_t & msg,
527  const mbox_t & mbox,
528  std::chrono::steady_clock::duration pause )
529  {
530  using namespace asio_common;
531 
534  m_io_svc.get(),
535  type_index,
536  msg,
537  mbox ) };
538 
540  }
541 
542 template< typename Activity_Tracker >
544 env_infrastructure_t<Activity_Tracker>::stats_controller() noexcept
545  {
546  return m_stats_controller;
547  }
548 
549 template< typename Activity_Tracker >
551 env_infrastructure_t<Activity_Tracker>::stats_repository() noexcept
552  {
553  return m_stats_controller;
554  }
555 
556 template< typename Activity_Tracker >
559  {
560  const auto stats = m_coop_repo.query_stats();
561 
566  };
567  }
568 
569 template< typename Activity_Tracker >
572  {
573  // Note: this type of environment_infrastructure doesn't support
574  // statistics for timers.
575  return { 0, 0 };
576  }
577 
578 template< typename Activity_Tracker >
581  {
582  return { m_default_disp };
583  }
584 
585 template< typename Activity_Tracker >
586 void
588  env_init_t init_fn )
589  {
590  try
591  {
597 
598  // User-supplied init can be called now.
599  init_fn();
600  }
601  catch(...)
602  {
603  // We can't restore if the following fragment throws and exception.
605  // The current exception should be stored to be
606  // rethrown later.
608 
609  // SObjectizer's shutdown should be initiated.
610  stop();
611 
612  // NOTE: pointer to the default dispatcher will be dropped
613  // in launch() method.
614  } );
615  }
616  }
617 
618 template< typename Activity_Tracker >
619 void
621  {
623  {
624  // If there is no more live coops then shutdown must be
625  // completed.
626  if( !m_coop_repo.has_live_coop() )
627  {
629  // Asio's event loop must be broken here!
630  m_io_svc.get().stop();
631  }
632  }
633  }
634 
635 //
636 // ensure_autoshutdown_enabled
637 //
638 /*!
639  * Throws an exception if autoshutdown feature is disabled.
640  */
641 void
643  const environment_params_t & env_params )
644  {
645  if( env_params.autoshutdown_disabled() )
646  SO_5_THROW_EXCEPTION( rc_autoshutdown_must_be_enabled,
647  "autoshutdown feature must be enabled for "
648  "so_5::env_infrastructures::simple_not_mtsafe" );
649  }
650 
651 } /* namespace impl */
652 
653 //
654 // factory
655 //
656 /*!
657  * \brief A factory for creation of environment infrastructure based on
658  * Asio's event loop.
659  *
660  * \attention
661  * This environment infrastructure is not a thread safe.
662  *
663  * Usage example:
664  * \code
665 int main()
666 {
667  asio::io_context io_svc;
668 
669  so_5::launch( [](so_5::environment_t & env) {
670  ... // Some initialization stuff.
671  },
672  [&io_svc](so_5::environment_params_t & params) {
673  using asio_env = so_5::extra::env_infrastructures::asio::simple_not_mtsafe;
674 
675  params.infrastructure_factory( asio_env::factory(io_svc) );
676  } );
677 
678  return 0;
679 }
680  * \endcode
681  */
684  {
685  using namespace impl;
686 
687  return [&io_svc](
688  environment_t & env,
689  environment_params_t & env_params,
690  mbox_t stats_distribution_mbox )
691  {
692  ensure_autoshutdown_enabled( env_params );
693 
694  environment_infrastructure_t * obj = nullptr;
695 
696  // Create environment infrastructure object in dependence of
697  // work thread activity tracking flag.
698  const auto tracking = env_params.work_thread_activity_tracking();
699  if( work_thread_activity_tracking_t::on == tracking )
700  obj = new env_infrastructure_t< reusable::real_activity_tracker_t >(
701  outliving_mutable(io_svc),
702  env,
703  env_params.so5_giveout_coop_listener(),
704  std::move(stats_distribution_mbox) );
705  else
706  obj = new env_infrastructure_t< reusable::fake_activity_tracker_t >(
707  outliving_mutable(io_svc),
708  env,
709  env_params.so5_giveout_coop_listener(),
710  std::move(stats_distribution_mbox) );
711 
712  return environment_infrastructure_unique_ptr_t(
713  obj,
714  environment_infrastructure_t::default_deleter() );
715  };
716  }
717 
718 } /* namespace simple_not_mtsafe */
719 
720 } /* namespace asio */
721 
722 } /* namespace env_infrastructures */
723 
724 } /* namespace extra */
725 
726 } /* 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.