1 /*!
2 * \file
3 * \brief Implementation of Asio's One Thread dispatcher.
4 *
5 * \since
6 * v.1.4.1
7 */
8
9 #pragma once
10
11 #include <so_5_extra/error_ranges.hpp>
12
13 #include <so_5/disp_binder.hpp>
14 #include <so_5/send_functions.hpp>
15
16 #include <so_5/disp/reuse/work_thread_activity_tracking.hpp>
17 #include <so_5/disp/reuse/data_source_prefix_helpers.hpp>
18
19 #include <so_5/stats/repository.hpp>
20 #include <so_5/stats/messages.hpp>
21 #include <so_5/stats/std_names.hpp>
22 #include <so_5/stats/impl/activity_tracking.hpp>
23
24 #include <so_5/details/invoke_noexcept_code.hpp>
25 #include <so_5/details/rollback_on_exception.hpp>
26 #include <so_5/details/abort_on_fatal_error.hpp>
27
28 #include <so_5/impl/thread_join_stuff.hpp>
29
30 #include <so_5/outliving.hpp>
31
32 #include <asio/io_context.hpp>
33 #include <asio/post.hpp>
34
35 namespace so_5 {
36
37 namespace extra {
38
39 namespace disp {
40
41 namespace asio_one_thread {
42
43 namespace errors {
44
45 //! Asio IoService is not set for asio_thread_pool dispatcher.
46 const int rc_io_context_is_not_set =
47 so_5::extra::errors::asio_one_thread_errors;
48
49 } /* namespace errors */
50
51 /*!
52 * \brief An alias for shared-pointer to io_context object.
53 *
54 * \since v.1.4.1
55 */
56 using io_context_shptr_t = std::shared_ptr< asio::io_context >;
57
58 //
59 // disp_params_t
60 //
61 /*!
62 * \brief Parameters for %asio_one_thread dispatcher.
63 */
64 class disp_params_t
65 : public ::so_5::disp::reuse::work_thread_activity_tracking_flag_mixin_t< disp_params_t >
66 {
67 using activity_tracking_mixin_t = ::so_5::disp::reuse::
68 work_thread_activity_tracking_flag_mixin_t< disp_params_t >;
69
70 public :
71 //! Default constructor.
72 disp_params_t() = default;
73
74 friend inline void
swap(disp_params_t & a,disp_params_t & b)75 swap(
76 disp_params_t & a, disp_params_t & b ) noexcept
77 {
78 using std::swap;
79
80 swap(
81 static_cast< activity_tracking_mixin_t & >(a),
82 static_cast< activity_tracking_mixin_t & >(b) );
83
84 swap( a.m_io_context, b.m_io_context );
85 }
86
87 //! Use external Asio io_context object with dispatcher.
88 /*!
89 * Usage example:
90 * \code
91 * int main() {
92 * asio::io_context svc;
93 * so_5::launch( [&](so_5::environment_t & env) {
94 * namespace asio_ot = so_5::extra::disp::asio_one_thread;
95 * auto disp = asio_ot::create_private_disp(
96 * env, "asio_ot",
97 * asio_tp::disp_params_t{}.use_external_io_context(svc) );
98 * ...
99 * } );
100 * }
101 * \endcode
102 */
103 disp_params_t &
use_external_io_context(::asio::io_context & service)104 use_external_io_context(
105 ::asio::io_context & service )
106 {
107 m_io_context = std::shared_ptr< ::asio::io_context >(
108 std::addressof( service ),
109 // Empty deleter.
110 [](::asio::io_context *) {} );
111 return *this;
112 }
113
114 //! Use external Asio io_context object with dispatcher.
115 /*!
116 * \note
117 * Ownership of this io_context object must be shared with
118 * others.
119 */
120 disp_params_t &
use_external_io_context(std::shared_ptr<::asio::io_context> service)121 use_external_io_context(
122 std::shared_ptr< ::asio::io_context > service )
123 {
124 m_io_context = std::move(service);
125 return *this;
126 }
127
128 //! Use own Asio io_context object.
129 /*!
130 * Note this object will be dynamically created at the start
131 * of the dispatcher. And will be destroyed with the dispatcher object.
132 *
133 * A created io_context can be accessed later via io_context() method.
134 */
135 disp_params_t &
use_own_io_context()136 use_own_io_context()
137 {
138 m_io_context = std::make_shared< ::asio::io_context >();
139 return *this;
140 }
141
142 //! Get the io_context.
143 std::shared_ptr< ::asio::io_context >
io_context() const144 io_context() const noexcept
145 {
146 return m_io_context;
147 }
148
149 private :
150 //! Asio's io_context which must be used with this dispatcher.
151 std::shared_ptr< ::asio::io_context > m_io_context;
152 };
153
154 namespace impl {
155
156 //
157 // actual_disp_binder_t
158 //
159 /*!
160 * \brief An actual interace of disp_binder for asio_one_thread dispatcher.
161 *
162 * That binder should allow to get a reference to io_context objects.
163 *
164 * \since v.1.4.1
165 */
166 class actual_disp_binder_t
167 : public disp_binder_t
168 {
169 public :
170 [[nodiscard]]
171 virtual asio::io_context &
172 io_context() noexcept = 0;
173 };
174
175 //
176 // actual_disp_binder_shptr_t
177 //
178 /*!
179 * \brief An alias for shared-pointer to actual_disp_binder.
180 *
181 * \since v.1.4.1
182 */
183 using actual_disp_binder_shptr_t =
184 std::shared_ptr< actual_disp_binder_t >;
185
186 class dispatcher_handle_maker_t;
187
188 } /* namespace impl */
189
190 //
191 // dispatcher_handle_t
192 //
193
194 /*!
195 * \brief A handle for %asio_one_thread dispatcher.
196 *
197 * \since
198 * v.1.4.1
199 */
200 class [[nodiscard]] dispatcher_handle_t
201 {
202 friend class impl::dispatcher_handle_maker_t;
203
204 //! A reference to actual implementation of a dispatcher.
205 impl::actual_disp_binder_shptr_t m_binder;
206
dispatcher_handle_t(impl::actual_disp_binder_shptr_t binder)207 dispatcher_handle_t(
208 impl::actual_disp_binder_shptr_t binder ) noexcept
209 : m_binder{ std::move(binder) }
210 {}
211
212 //! Is this handle empty?
213 [[nodiscard]]
214 bool
empty() const215 empty() const noexcept { return !m_binder; }
216
217 public :
218 dispatcher_handle_t() noexcept = default;
219
220 //! Get a binder for that dispatcher.
221 /*!
222 * Usage example:
223 * \code
224 * using namespace so_5::extra::disp::asio_one_thread;
225 *
226 * asio::io_context io_ctx;
227 *
228 * so_5::environment_t & env = ...;
229 * auto disp = make_dispatcher( env, "my_disp", io_ctx );
230 *
231 * env.introduce_coop( [&]( so_5::coop_t & coop ) {
232 * coop.make_agent_with_binder< some_agent_type >(disp.binder(), ...);
233 * ...
234 * } );
235 * \endcode
236 *
237 * \attention
238 * An attempt to call this method on empty handle is UB.
239 */
240 [[nodiscard]]
241 disp_binder_shptr_t
binder() const242 binder() const
243 {
244 return m_binder;
245 }
246
247 //! Get reference to io_context from that dispatcher.
248 /*!
249 * \attention
250 * An attempt to call this method on empty handle is UB.
251 */
252 [[nodiscard]]
253 ::asio::io_context &
io_context()254 io_context() noexcept
255 {
256 return m_binder->io_context();
257 }
258
259 //! Is this handle empty?
260 [[nodiscard]]
operator bool() const261 operator bool() const noexcept { return empty(); }
262
263 //! Does this handle contain a reference to dispatcher?
264 [[nodiscard]]
265 bool
operator !() const266 operator!() const noexcept { return !empty(); }
267
268 //! Drop the content of handle.
269 void
reset()270 reset() noexcept { m_binder.reset(); }
271 };
272
273 namespace impl {
274
275 //
276 // demands_counter_t
277 //
278 /*!
279 * \brief Type of atomic counter for counting waiting demands.
280 *
281 * \since
282 * v.1.4.1
283 */
284 using demands_counter_t = std::atomic< std::size_t >;
285
286 namespace work_thread_details {
287
288 /*!
289 * \brief A type of holder of data common to all worker thread
290 * implementations.
291 *
292 * \since v.1.4.1
293 */
294 template< typename Thread_Type >
295 struct common_data_t
296 {
297 //! Asio's context to be used.
298 io_context_shptr_t m_io_context;
299
300 //! Thread object.
301 /*!
302 * @note
303 * Thread object is stored via unique_ptr becase custom thread
304 * type can have deleted/disable move constructor/operator.
305 */
306 std::unique_ptr< Thread_Type > m_thread;
307
308 //! ID of the work thread.
309 /*!
310 * \note Receives actual value only after successful start
311 * of the thread.
312 */
313 so_5::current_thread_id_t m_thread_id;
314
315 //! Counter of waiting demands.
316 demands_counter_t m_demands_counter{ 0u };
317
common_data_tso_5::extra::disp::asio_one_thread::impl::work_thread_details::common_data_t318 common_data_t( io_context_shptr_t io_context )
319 : m_io_context( std::move(io_context) ) {}
320
321 [[nodiscard]]
322 asio::io_context &
io_contextso_5::extra::disp::asio_one_thread::impl::work_thread_details::common_data_t323 io_context() const noexcept { return *(this->m_io_context); }
324
325 [[nodiscard]]
326 demands_counter_t &
demands_counterso_5::extra::disp::asio_one_thread::impl::work_thread_details::common_data_t327 demands_counter() noexcept { return this->m_demands_counter; }
328 };
329
330 /*!
331 * \brief Base class for implementation of worker thread without
332 * thread activity tracking.
333 *
334 * Methods work_started() and work_finished() are empty.
335 *
336 * \brief v.1.4.1
337 */
338 template< typename Thread_Type >
339 class no_activity_tracking_impl_t : protected common_data_t< Thread_Type >
340 {
341 using base_type_t = common_data_t< Thread_Type >;
342
343 public :
no_activity_tracking_impl_t(io_context_shptr_t io_context)344 no_activity_tracking_impl_t( io_context_shptr_t io_context )
345 : base_type_t( std::move(io_context) )
346 {}
347
348 using base_type_t::io_context;
349
350 using base_type_t::demands_counter;
351
352 protected :
353 void
work_started()354 work_started() { /* Nothing to do. */ }
355
356 void
work_finished()357 work_finished() { /* Nothing to do. */ }
358 };
359
360 /*!
361 * \brief Base class for implementation of worker thread with
362 * thread activity tracking.
363 *
364 * Methods work_started() and work_finished() perform actial activity
365 * tracking.
366 *
367 * This class also provides public method take_activity_stats() to
368 * retrive activity statistics.
369 *
370 * \brief v.1.4.1
371 */
372 template< typename Thread_Type >
373 class with_activity_tracking_impl_t : protected common_data_t< Thread_Type >
374 {
375 using base_type_t = common_data_t< Thread_Type >;
376
377 public :
with_activity_tracking_impl_t(io_context_shptr_t io_context)378 with_activity_tracking_impl_t( io_context_shptr_t io_context )
379 : base_type_t( std::move(io_context) )
380 {}
381
382 using base_type_t::io_context;
383
384 using base_type_t::demands_counter;
385
386 [[nodiscard]]
387 so_5::stats::work_thread_activity_stats_t
take_activity_stats()388 take_activity_stats()
389 {
390 so_5::stats::work_thread_activity_stats_t result;
391
392 result.m_working_stats = m_working_stats.take_stats();
393
394 return result;
395 }
396
397 protected :
398 //! Statictics for work activity.
399 so_5::stats::activity_tracking_stuff::stats_collector_t<
400 so_5::stats::activity_tracking_stuff::internal_lock >
401 m_working_stats;
402
403 void
work_started()404 work_started() { m_working_stats.start(); }
405
406 void
work_finished()407 work_finished() { m_working_stats.stop(); }
408 };
409
410 } /* namespace work_thread_details */
411
412 //
413 // work_thread_template_t
414 //
415 /*!
416 * \brief An implementation of worker thread in form of the template class.
417 *
418 * Work_Thread parameter is expected to be
419 * work_thread_details::no_activity_tracking_impl_t or
420 * work_thread_details::with_activity_tracking_impl_t.
421 *
422 * This class is also play a role of event_queue. It's because there
423 * is no real event-queue to be controlled by this class. All
424 * demands are delegated to io_context object.
425 *
426 * \since v.1.4.1
427 */
428 template<
429 typename Thread_Type,
430 template<class> class Work_Thread >
431 class work_thread_template_t
432 : public Work_Thread< Thread_Type >
433 , public event_queue_t
434 {
435 using base_type_t = Work_Thread< Thread_Type >;
436
437 //! SObjectizer Environment to work in.
438 /*!
439 * We have to store that reference to have an ability to
440 * log error messages in thread's body.
441 */
442 environment_t & m_env;
443
444 public :
445 //! Initializing constructor.
work_thread_template_t(environment_t & env,io_context_shptr_t io_context)446 work_thread_template_t(
447 environment_t & env,
448 io_context_shptr_t io_context )
449 : base_type_t( std::move(io_context) )
450 , m_env( env )
451 {}
452
453 //! Starts a new thread.
454 /*!
455 * Passes all \a thread_init_args to the constructor of
456 * of Thread_Type after the lambda-function with thread body.
457 * It means that if there is a call:
458 * \code
459 * work_thread.start( 0, "my-thread", 0.1f );
460 * \endcode
461 * Then the actual call for Thread_Type's constructor will
462 * look like:
463 * \code
464 * new Thread_Type( [this] {...}, 0, "my-thread", 0.1f );
465 * \endcode
466 */
467 template< typename... Thread_Init_Args >
468 void
start(Thread_Init_Args &&...thread_init_args)469 start( Thread_Init_Args && ...thread_init_args )
470 {
471 this->m_thread = std::make_unique< Thread_Type >(
472 [this]() { body(); },
473 std::forward<Thread_Init_Args>(thread_init_args)... );
474 }
475
476 void
stop()477 stop()
478 {
479 this->io_context().stop();
480 }
481
482 void
join()483 join()
484 {
485 if( this->m_thread )
486 {
487 so_5::impl::ensure_join_from_different_thread(
488 this->m_thread_id );
489 this->m_thread->join();
490 }
491 }
492
493 so_5::current_thread_id_t
thread_id() const494 thread_id() const
495 {
496 return this->m_thread_id;
497 }
498
499 void
push(execution_demand_t demand)500 push( execution_demand_t demand ) override
501 {
502 // Demand count statistics should be updated.
503 ++(this->demands_counter());
504
505 // If posting a demand fails the count of demands
506 // should be decremented.
507 ::so_5::details::do_with_rollback_on_exception(
508 [&] {
509 asio::post( this->io_context(),
510 [d = std::move(demand), this]() mutable {
511 this->handle_demand( std::move(d) );
512 } );
513 },
514 [this] {
515 --this->demands_counter();
516 } );
517 }
518
519 private :
520 void
body()521 body()
522 {
523 this->m_thread_id = so_5::query_current_thread_id();
524
525 // We don't expect any errors here.
526 // But if something happens then there is no way to
527 // recover and the whole application should be aborted.
528 try
529 {
530 // Prevent return from io_context::run() if there is no
531 // more Asio's events.
532 auto work = ::asio::make_work_guard( this->io_context() );
533 this->io_context().run();
534 }
535 catch( const std::exception & x )
536 {
537 ::so_5::details::abort_on_fatal_error( [&] {
538 SO_5_LOG_ERROR( this->m_env, log_stream ) {
539 log_stream << "An exception caught in work thread "
540 "of so_5::extra::disp::asio_one_thread dispatcher."
541 " Exception: "
542 << x.what() << std::endl;
543 }
544 } );
545 }
546 catch( ... )
547 {
548 ::so_5::details::abort_on_fatal_error( [&] {
549 SO_5_LOG_ERROR( this->m_env, log_stream ) {
550 log_stream << "An unknown exception caught in work thread "
551 "of so_5::extra::disp::asio_one_thread dispatcher."
552 << std::endl;
553 }
554 } );
555 }
556 }
557
558 void
handle_demand(execution_demand_t demand)559 handle_demand( execution_demand_t demand ) noexcept
560 {
561 // Demand count statistics should be updated.
562 --(this->demands_counter());
563
564 this->work_started();
565 auto work_meter_stopper = so_5::details::at_scope_exit(
566 [this] { this->work_finished(); } );
567
568 demand.call_handler( this->m_thread_id );
569 }
570 };
571
572 //
573 // work_thread_no_activity_tracking_t
574 //
575 template< typename Thread_Type >
576 using work_thread_no_activity_tracking_t =
577 work_thread_template_t<
578 Thread_Type,
579 work_thread_details::no_activity_tracking_impl_t >;
580
581 template< typename Thread_Type >
582 void
send_thread_activity_stats(const so_5::mbox_t &,const so_5::stats::prefix_t &,work_thread_no_activity_tracking_t<Thread_Type> &)583 send_thread_activity_stats(
584 const so_5::mbox_t &,
585 const so_5::stats::prefix_t &,
586 work_thread_no_activity_tracking_t< Thread_Type > & )
587 {
588 /* Nothing to do */
589 }
590
591 //
592 // work_thread_with_activity_tracking_t
593 //
594 template< typename Thread_Type >
595 using work_thread_with_activity_tracking_t =
596 work_thread_template_t<
597 Thread_Type,
598 work_thread_details::with_activity_tracking_impl_t >;
599
600 template< typename Thread_Type >
601 void
send_thread_activity_stats(const so_5::mbox_t & mbox,const so_5::stats::prefix_t & prefix,work_thread_with_activity_tracking_t<Thread_Type> & wt)602 send_thread_activity_stats(
603 const so_5::mbox_t & mbox,
604 const so_5::stats::prefix_t & prefix,
605 work_thread_with_activity_tracking_t< Thread_Type > & wt )
606 {
607 so_5::send< so_5::stats::messages::work_thread_activity >(
608 mbox,
609 prefix,
610 so_5::stats::suffixes::work_thread_activity(),
611 wt.thread_id(),
612 wt.take_activity_stats() );
613 }
614
615 //
616 // dispatcher_template_t
617 //
618 /*!
619 * \brief An implementation of the dispatcher in the form of template class.
620 *
621 * This dispatcher launches worker thread in the constructor and
622 * stops and joins it in the destructor.
623 *
624 * \since v.1.4.1
625 */
626 template< typename Work_Thread >
627 class dispatcher_template_t final : public actual_disp_binder_t
628 {
629 friend class disp_data_source_t;
630
631 public:
632 template< typename... Thread_Init_Args >
dispatcher_template_t(outliving_reference_t<environment_t> env,const std::string_view name_base,disp_params_t params,Thread_Init_Args &&...thread_init_args)633 dispatcher_template_t(
634 outliving_reference_t< environment_t > env,
635 const std::string_view name_base,
636 disp_params_t params,
637 Thread_Init_Args && ...thread_init_args )
638 : m_work_thread{ env.get(), params.io_context() }
639 , m_data_source{
640 outliving_mutable(env.get().stats_repository()),
641 name_base,
642 outliving_mutable(*this)
643 }
644 {
645 m_work_thread.start(
646 std::forward<Thread_Init_Args>(thread_init_args)... );
647 }
648
~dispatcher_template_t()649 ~dispatcher_template_t() noexcept override
650 {
651 m_work_thread.stop();
652 m_work_thread.join();
653 }
654
655 void
preallocate_resources(agent_t &)656 preallocate_resources(
657 agent_t & /*agent*/ ) override
658 {
659 // Nothing to do.
660 }
661
662 void
undo_preallocation(agent_t &)663 undo_preallocation(
664 agent_t & /*agent*/ ) noexcept override
665 {
666 // Nothing to do.
667 }
668
669 void
bind(agent_t & agent)670 bind(
671 agent_t & agent ) noexcept override
672 {
673 agent.so_bind_to_dispatcher( m_work_thread );
674 ++m_agents_bound;
675 }
676
677 void
unbind(agent_t &)678 unbind(
679 agent_t & /*agent*/ ) noexcept override
680 {
681 --m_agents_bound;
682 }
683
684 [[nodiscard]]
685 asio::io_context &
io_context()686 io_context() noexcept override
687 {
688 return m_work_thread.io_context();
689 }
690
691 private:
692
693 /*!
694 * \brief Data source for run-time monitoring of whole dispatcher.
695 *
696 * \since
697 * v.1.4.1
698 */
699 class disp_data_source_t : public stats::source_t
700 {
701 //! Dispatcher to work with.
702 outliving_reference_t< dispatcher_template_t > m_dispatcher;
703
704 //! Basic prefix for data sources.
705 stats::prefix_t m_base_prefix;
706
707 public :
disp_data_source_t(const std::string_view name_base,outliving_reference_t<dispatcher_template_t> disp)708 disp_data_source_t(
709 const std::string_view name_base,
710 outliving_reference_t< dispatcher_template_t > disp )
711 : m_dispatcher{ disp }
712 , m_base_prefix{ so_5::disp::reuse::make_disp_prefix(
713 "ext-asio-ot",
714 name_base,
715 &(disp.get()) )
716 }
717 {}
718
719 void
distribute(const mbox_t & mbox)720 distribute( const mbox_t & mbox ) override
721 {
722 so_5::send< stats::messages::quantity< std::size_t > >(
723 mbox,
724 this->m_base_prefix,
725 stats::suffixes::agent_count(),
726 m_dispatcher.get().m_agents_bound.load(
727 std::memory_order_acquire ) );
728
729 so_5::send< stats::messages::quantity< std::size_t > >(
730 mbox,
731 this->m_base_prefix,
732 stats::suffixes::work_thread_queue_size(),
733 m_dispatcher.get().m_work_thread.demands_counter().load(
734 std::memory_order_acquire ) );
735
736 send_thread_activity_stats(
737 mbox,
738 this->m_base_prefix,
739 m_dispatcher.get().m_work_thread );
740 }
741
742 private:
743 };
744
745 //! Working thread for the dispatcher.
746 Work_Thread m_work_thread;
747
748 //! Data source for run-time monitoring.
749 stats::auto_registered_source_holder_t< disp_data_source_t >
750 m_data_source;
751
752 //! Count of agents bound to this dispatcher.
753 std::atomic< std::size_t > m_agents_bound = { 0 };
754 };
755
756 //
757 // dispatcher_handle_maker_t
758 //
759 /*!
760 * \brief A factory class for creation of dispatcher_handle instances.
761 *
762 * \since v.1.4.1
763 */
764 class dispatcher_handle_maker_t
765 {
766 public :
767 [[nodiscard]]
768 static dispatcher_handle_t
make(actual_disp_binder_shptr_t binder)769 make( actual_disp_binder_shptr_t binder ) noexcept
770 {
771 return { std::move( binder ) };
772 }
773 };
774
775 //
776 // create_dispatcher
777 //
778 /*!
779 * \brief The actual implementation of dispatcher creation procedure.
780 *
781 * \tparam Traits Type of traits to be used for a new dispatcher.
782 * \tparam Thread_Init_Args Types of arguments to be passed as additional
783 * parameters to the constructor of Traits::thread_type.
784 *
785 * \since v.1.4.1
786 */
787 template<
788 typename Traits,
789 typename... Thread_Init_Args >
790 [[nodiscard]]
791 dispatcher_handle_t
create_dispatcher(environment_t & env,const std::string_view data_sources_name_base,disp_params_t params,Thread_Init_Args &&...thread_init_args)792 create_dispatcher(
793 //! SObjectizer environment to work in.
794 environment_t & env,
795 //! Short name for this instance to be used in thread activity stats.
796 //! Can be empty string. In this case name will be generated automatically.
797 const std::string_view data_sources_name_base,
798 //! Parameters for this dispatcher instance.
799 disp_params_t params,
800 //! Parameters for initialization of a custom thread.
801 Thread_Init_Args && ...thread_init_args )
802 {
803 using namespace so_5::disp::reuse;
804
805 const auto io_svc_ptr = params.io_context();
806 if( !io_svc_ptr )
807 SO_5_THROW_EXCEPTION(
808 errors::rc_io_context_is_not_set,
809 "io_context is not set in disp_params" );
810
811 using thread_type = typename Traits::thread_type;
812
813 using dispatcher_no_activity_tracking_t =
814 dispatcher_template_t<
815 work_thread_no_activity_tracking_t< thread_type > >;
816
817 using dispatcher_with_activity_tracking_t =
818 dispatcher_template_t<
819 work_thread_with_activity_tracking_t< thread_type > >;
820
821 using so_5::stats::activity_tracking_stuff::create_appropriate_disp;
822 actual_disp_binder_shptr_t binder =
823 create_appropriate_disp<
824 // Type of result pointer.
825 impl::actual_disp_binder_t,
826 // Actual type of dispatcher without thread activity tracking.
827 dispatcher_no_activity_tracking_t,
828 // Actual type of dispatcher with thread activity tracking.
829 dispatcher_with_activity_tracking_t >(
830 outliving_mutable(env),
831 data_sources_name_base,
832 std::move(params),
833 std::forward<Thread_Init_Args>(thread_init_args)... );
834
835 return dispatcher_handle_maker_t::make( std::move(binder) );
836 }
837
838 } /* namespace impl */
839
840 //
841 // default_traits_t
842 //
843 /*!
844 * \brief Default traits of %asio_one_thread dispatcher.
845 *
846 * \since
847 * v.1.4.1
848 */
849 struct default_traits_t
850 {
851 //! Type of thread.
852 using thread_type = std::thread;
853 };
854
855 //
856 // make_dispatcher
857 //
858 /*!
859 * \brief A function for creation an instance of %asio_one_thread dispatcher.
860 *
861 * Usage examples:
862 * \code
863 * // Dispatcher which uses own Asio IoContext and default traits.
864 * namespace asio_disp = so_5::extra::disp::asio_one_thread;
865 * asio_disp::disp_params_t params;
866 * params.use_own_io_context(); // Asio IoContext object will be created here.
867 * // This object will be accessible later via
868 * // dispatcher_handle_t::io_context() method.
869 * auto disp = asio_disp::make_dispatcher(
870 * env,
871 * "my_asio_disp",
872 * std::move(disp_params) );
873 * \endcode
874 *
875 * \code
876 * // Dispatcher which uses external Asio IoContext and default traits.
877 * asio::io_context & io_svc = ...;
878 * namespace asio_disp = so_5::extra::disp::asio_one_thread;
879 * asio_disp::disp_params_t params;
880 * params.use_external_io_context( io_svc );
881 * auto disp = asio_disp::make_dispatcher(
882 * env,
883 * "my_asio_disp",
884 * std::move(disp_params) );
885 * \endcode
886 *
887 * \code
888 * // Dispatcher which uses own Asio IoContext and custom traits.
889 * struct my_traits
890 * {
891 * using thread_type = my_custom_thread_type;
892 * };
893 * namespace asio_disp = so_5::extra::disp::asio_one_thread;
894 * asio_disp::disp_params_t params;
895 * params.use_own_io_context();
896 * auto disp = asio_disp::make_dispatcher< my_traits >(
897 * env,
898 * "my_asio_tp",
899 * std::move(disp_params) );
900 * \endcode
901 *
902 * \par Requirements for traits type
903 * Traits type must define a type which looks like:
904 * \code
905 * struct traits
906 * {
907 * // Name of type to be used for thread class.
908 * using thread_type = ...;
909 * };
910 * \endcode
911 *
912 * \par Requirements for custom thread type
913 * By default std::thread is used as a class for working with threads.
914 * But user can specify its own custom thread type via \a Traits::thread_type
915 * parameter. A custom thread type must be a class which looks like:
916 * \code
917 * class custom_thread_type {
918 * public :
919 * // Must provide this constructor.
920 * // F -- is a type of functional object which can be converted
921 * // into std::function<void()>.
922 * template<typename F>
923 * custom_thread_type(F && f) {...}
924 *
925 * // Destructor must join thread if it is not joined yet.
926 * ~custom_thread_type() noexcept {...}
927 *
928 * // The same semantic like std::thread::join.
929 * void join() noexcept {...}
930 * };
931 * \endcode
932 * This class doesn't need to be DefaultConstructible, CopyConstructible,
933 * MoveConstructible, Copyable or Moveable.
934 *
935 * \tparam Traits Type with traits for a dispatcher. For the requirements
936 * for \a Traits type see the section "Requirements for traits type" above.
937 *
938 * \since
939 * v.1.4.1
940 */
941 template< typename Traits = default_traits_t >
942 dispatcher_handle_t
make_dispatcher(environment_t & env,const std::string_view data_sources_name_base,disp_params_t params)943 make_dispatcher(
944 //! SObjectizer environment to work in.
945 environment_t & env,
946 //! Short name for this instance to be used in thread activity stats.
947 //! Can be empty string. In this case name will be generated automatically.
948 const std::string_view data_sources_name_base,
949 //! Parameters for this dispatcher instance.
950 disp_params_t params )
951 {
952 return impl::create_dispatcher< Traits >(
953 env,
954 data_sources_name_base,
955 std::move(params) );
956 }
957
958 /*!
959 * \brief A function for creation an instance of %asio_one_thread dispatcher
960 * with a set of arguments for custom thread object's constructor.
961 *
962 * Usage example:
963 * \code
964 * // Dispatcher which uses own Asio IoContext and custom traits.
965 * class my_custom_thread_type
966 * {
967 * ...
968 * public:
969 * template< typename F >
970 * my_custom_thread_type(
971 * F && body,
972 * int priority,
973 * std::string instance_name,
974 * std::size_t stack_size )
975 * {...}
976 *
977 * ...
978 * };
979 * struct my_traits
980 * {
981 * using thread_type = my_custom_thread_type;
982 * };
983 * namespace asio_disp = so_5::extra::disp::asio_one_thread;
984 * asio_disp::disp_params_t params;
985 * params.use_own_io_context();
986 * auto disp = asio_disp::make_dispatcher< my_traits >(
987 * env,
988 * "my_asio_tp",
989 * std::move(disp_params),
990 * // Those parameters will be passed to the constructor
991 * // of my_custom_thread_type.
992 * 2, "my-asio-one-thread"s, 8192u);
993 * \endcode
994 *
995 * \par Requirements for traits type
996 * Traits type must define a type which looks like:
997 * \code
998 * struct traits
999 * {
1000 * // Name of type to be used for thread class.
1001 * using thread_type = ...;
1002 * };
1003 * \endcode
1004 *
1005 * \par Requirements for custom thread type
1006 * A custom thread type must be a class which looks like:
1007 * \code
1008 * class custom_thread_type {
1009 * public :
1010 * // Must provide this constructor.
1011 * // F -- is a type of functional object which can be converted
1012 * // into std::function<void()>.
1013 * template<
1014 * typename F,
1015 * typename... Init_Args>
1016 * custom_thread_type(F && f, Init_Args && ...init_args) {...}
1017 *
1018 * // Destructor must join thread if it is not joined yet.
1019 * ~custom_thread_type() noexcept {...}
1020 *
1021 * // The same semantic like std::thread::join.
1022 * void join() noexcept {...}
1023 * };
1024 * \endcode
1025 * This class doesn't need to be DefaultConstructible, CopyConstructible,
1026 * MoveConstructible, Copyable or Moveable.
1027 *
1028 * \tparam Traits Type with traits for a dispatcher. For the requirements
1029 * for \a Traits type see the section "Requirements for traits type" above.
1030 * \tparam Thread_Init_Args Types of parameters to be passed to the
1031 * constructor of Traits::thread_type.
1032 *
1033 * \since
1034 * v.1.4.1
1035 */
1036 template<
1037 typename Traits,
1038 typename... Thread_Init_Args >
1039 dispatcher_handle_t
make_dispatcher(environment_t & env,const std::string_view data_sources_name_base,disp_params_t params,Thread_Init_Args &&...thread_init_args)1040 make_dispatcher(
1041 //! SObjectizer environment to work in.
1042 environment_t & env,
1043 //! Short name for this instance to be used in thread activity stats.
1044 //! Can be empty string. In this case name will be generated automatically.
1045 const std::string_view data_sources_name_base,
1046 //! Parameters for this dispatcher instance.
1047 disp_params_t params,
1048 //! Parameters for initialization of a custom thread.
1049 Thread_Init_Args && ...thread_init_args )
1050 {
1051 return impl::create_dispatcher< Traits >(
1052 env,
1053 data_sources_name_base,
1054 std::move(params),
1055 std::forward<Thread_Init_Args>(thread_init_args)... );
1056 }
1057
1058 } /* namespace asio_one_thread */
1059
1060 } /* namespace disp */
1061
1062 } /* namespace extra */
1063
1064 } /* namespace so_5 */
1065
1066