1 /*
2 SObjectizer 5.
3 */
4
5 /*!
6 \file
7 \since
8 v.5.5.0
9
10 \brief Timers and tools for working with timers.
11 */
12
13 #pragma once
14
15 #include <chrono>
16 #include <functional>
17
18 #include <so_5/declspec.hpp>
19 #include <so_5/compiler_features.hpp>
20
21 #include <so_5/error_logger.hpp>
22 #include <so_5/atomic_refcounted.hpp>
23
24 #include <so_5/mbox.hpp>
25 #include <so_5/message.hpp>
26
27 #include <so_5/outliving.hpp>
28
29 namespace so_5
30 {
31
32 #if defined( SO_5_MSVC )
33 #pragma warning(push)
34 #pragma warning(disable: 4251)
35 #endif
36
37 //
38 // timer_t
39 //
40 /*!
41 * \since
42 * v.5.5.0
43 *
44 * \brief A base class for timer identificator.
45 */
46 class SO_5_TYPE timer_t
47 : private so_5::atomic_refcounted_t
48 {
49 friend class intrusive_ptr_t< timer_t >;
50
51 public :
52 virtual ~timer_t() noexcept = default;
53
54 //! Is this timer event is active?
55 virtual bool
56 is_active() const noexcept = 0;
57
58 //! Release the timer event.
59 virtual void
60 release() noexcept = 0;
61 };
62
63 //
64 // timer_id_t
65 //
66 /*!
67 * \since
68 * v.5.5.0
69 *
70 * \brief An indentificator for the timer.
71 */
72 class SO_5_TYPE timer_id_t
73 {
74 public :
75 //! Default constructor.
76 timer_id_t() noexcept = default;
77 //! Initializing constructor.
timer_id_t(so_5::intrusive_ptr_t<timer_t> && timer)78 timer_id_t(
79 so_5::intrusive_ptr_t< timer_t > && timer ) noexcept
80 : m_timer( std::move(timer) )
81 {}
82
83 //! Swapping.
84 void
swap(timer_id_t & o)85 swap( timer_id_t & o ) noexcept
86 {
87 m_timer.swap( o.m_timer );
88 }
89
90 //! Is this timer event is active?
91 bool
is_active() const92 is_active() const noexcept
93 {
94 return ( m_timer && m_timer->is_active() );
95 }
96
97 //! Release the timer event.
98 void
release()99 release() noexcept
100 {
101 if( m_timer )
102 m_timer->release();
103 }
104
105 private :
106 //! Actual timer.
107 so_5::intrusive_ptr_t< timer_t > m_timer;
108 };
109
110 //
111 // timer_thread_stats_t
112 //
113 /*!
114 * \since
115 * v.5.5.4
116 *
117 * \brief Statistics for run-time monitoring.
118 */
119 struct timer_thread_stats_t
120 {
121 //! Quantity of single-shot timers.
122 std::size_t m_single_shot_count;
123
124 //! Quantity of periodic timers.
125 std::size_t m_periodic_count;
126 };
127
128 //
129 // timer_thread_t
130 //
131
132 //! Timer thread interface.
133 /*!
134
135 All timer threads for SObjectizer must be derived from this class.
136
137 A real timer may not be implemented as a thread. The name of this class is
138 just a consequence of some historic reasons.
139
140 A timer is started by timer_thread_t::start() method. To stop timer the
141 timer_thread_t::finish() method is used. The finish() method should block
142 caller until all timer resources will be released and all dedicated
143 timer threads (if any) are completelly stopped.
144 */
145 class SO_5_TYPE timer_thread_t
146 {
147 timer_thread_t( const timer_thread_t & ) = delete;
148 timer_thread_t &
149 operator=( const timer_thread_t & ) = delete;
150
151 public:
152 timer_thread_t() = default;
153 virtual ~timer_thread_t() = default;
154
155 //! Launch timer.
156 virtual void
157 start() = 0;
158
159 //! Finish timer and wait for full stop.
160 virtual void
161 finish() = 0;
162
163 //! Push delayed/periodic message to the timer queue.
164 /*!
165 * A timer can be deactivated later by using returned timer_id.
166 */
167 virtual timer_id_t
168 schedule(
169 //! Type of message to be sheduled.
170 const std::type_index & type_index,
171 //! Mbox for message delivery.
172 const mbox_t & mbox,
173 //! Message to be sent.
174 const message_ref_t & msg,
175 //! Pause before first message delivery.
176 std::chrono::steady_clock::duration pause,
177 //! Period for message repetition.
178 //! Zero value means single shot delivery.
179 std::chrono::steady_clock::duration period ) = 0;
180
181 //! Push anonymous delayed/periodic message to the timer queue.
182 /*!
183 * A timer cannot be deactivated later.
184 */
185 virtual void
186 schedule_anonymous(
187 //! Type of message to be sheduled.
188 const std::type_index & type_index,
189 //! Mbox for message delivery.
190 const mbox_t & mbox,
191 //! Message to be sent.
192 const message_ref_t & msg,
193 //! Pause before first message delivery.
194 std::chrono::steady_clock::duration pause,
195 //! Period for message repetition.
196 //! Zero value means single shot delivery.
197 std::chrono::steady_clock::duration period ) = 0;
198
199 /*!
200 * \since
201 * v.5.5.4
202 *
203 * \brief Get statistics for run-time monitoring.
204 */
205 virtual timer_thread_stats_t
206 query_stats() = 0;
207 };
208
209 //! Auxiliary typedef for timer_thread autopointer.
210 using timer_thread_unique_ptr_t = std::unique_ptr< timer_thread_t >;
211
212 //
213 // timer_thread_factory_t
214 //
215 /*!
216 * \since
217 * v.5.5.0
218 *
219 * \brief Type of factory for creating timer_thread objects.
220 */
221 using timer_thread_factory_t = std::function<
222 timer_thread_unique_ptr_t( error_logger_shptr_t ) >;
223
224 /*!
225 * \name Tools for creating timer threads.
226 * \{
227 */
228 /*!
229 * \since
230 * v.5.5.0
231 *
232 * \brief Create timer thread based on timer_wheel mechanism.
233 * \note Default parameters will be used for timer thread.
234 */
235 SO_5_FUNC timer_thread_unique_ptr_t
236 create_timer_wheel_thread(
237 //! A logger for handling error messages inside timer_thread.
238 error_logger_shptr_t logger );
239
240 /*!
241 * \since
242 * v.5.5.0
243 *
244 * \brief Create timer thread based on timer_wheel mechanism.
245 * \note Parameters must be specified explicitely.
246 */
247 SO_5_FUNC timer_thread_unique_ptr_t
248 create_timer_wheel_thread(
249 //! A logger for handling error messages inside timer_thread.
250 error_logger_shptr_t logger,
251 //! Size of the wheel.
252 unsigned int wheel_size,
253 //! A size of one time step for the wheel.
254 std::chrono::steady_clock::duration granuality );
255
256 /*!
257 * \since
258 * v.5.5.0
259 *
260 * \brief Create timer thread based on timer_heap mechanism.
261 * \note Default parameters will be used for timer thread.
262 */
263 SO_5_FUNC timer_thread_unique_ptr_t
264 create_timer_heap_thread(
265 //! A logger for handling error messages inside timer_thread.
266 error_logger_shptr_t logger );
267
268 /*!
269 * \since
270 * v.5.5.0
271 *
272 * \brief Create timer thread based on timer_heap mechanism.
273 * \note Parameters must be specified explicitely.
274 */
275 SO_5_FUNC timer_thread_unique_ptr_t
276 create_timer_heap_thread(
277 //! A logger for handling error messages inside timer_thread.
278 error_logger_shptr_t logger,
279 //! Initical capacity of heap array.
280 std::size_t initial_heap_capacity );
281
282 /*!
283 * \since
284 * v.5.5.0
285 *
286 * \brief Create timer thread based on timer_list mechanism.
287 */
288 SO_5_FUNC timer_thread_unique_ptr_t
289 create_timer_list_thread(
290 //! A logger for handling error messages inside timer_thread.
291 error_logger_shptr_t logger );
292 /*!
293 * \}
294 */
295
296 /*!
297 * \name Standard timer thread factories.
298 * \{
299 */
300 /*!
301 * \since
302 * v.5.5.0
303 *
304 * \brief Factory for timer_wheel thread with default parameters.
305 */
306 inline timer_thread_factory_t
timer_wheel_factory()307 timer_wheel_factory()
308 {
309 // Use this trick because create_timer_wheel_thread is overloaded.
310 timer_thread_unique_ptr_t (*f)( error_logger_shptr_t ) =
311 create_timer_wheel_thread;
312 return f;
313 }
314
315 /*!
316 * \since
317 * v.5.5.0
318 *
319 * \brief Factory for timer_wheel thread with explicitely specified parameters.
320 */
321 inline timer_thread_factory_t
timer_wheel_factory(unsigned int wheel_size,std::chrono::steady_clock::duration granularity)322 timer_wheel_factory(
323 //! Size of the wheel.
324 unsigned int wheel_size,
325 //! A size of one time step for the wheel.
326 std::chrono::steady_clock::duration granularity )
327 {
328 // Use this trick because create_timer_wheel_thread is overloaded.
329 timer_thread_unique_ptr_t (*f)(
330 error_logger_shptr_t,
331 unsigned int,
332 std::chrono::steady_clock::duration ) =
333 create_timer_wheel_thread;
334
335 return std::bind(
336 f,
337 std::placeholders::_1,
338 wheel_size,
339 granularity );
340 }
341
342 /*!
343 * \since
344 * v.5.5.0
345 *
346 * \brief Factory for timer_heap thread with default parameters.
347 */
348 inline timer_thread_factory_t
timer_heap_factory()349 timer_heap_factory()
350 {
351 // Use this trick because create_timer_heap_thread is overloaded.
352 timer_thread_unique_ptr_t (*f)( error_logger_shptr_t ) =
353 create_timer_heap_thread;
354 return f;
355 }
356
357 /*!
358 * \since
359 * v.5.5.0
360 *
361 * \brief Factory for timer_heap thread with explicitely specified parameters.
362 */
363 inline timer_thread_factory_t
timer_heap_factory(std::size_t initial_heap_capacity)364 timer_heap_factory(
365 //! Initial capacity of heap array.
366 std::size_t initial_heap_capacity )
367 {
368 // Use this trick because create_timer_heap_thread is overloaded.
369 timer_thread_unique_ptr_t (*f)( error_logger_shptr_t, std::size_t ) =
370 create_timer_heap_thread;
371
372 return std::bind(
373 f,
374 std::placeholders::_1,
375 initial_heap_capacity );
376 }
377
378 /*!
379 * \since
380 * v.5.5.0
381 *
382 * \brief Factory for timer_list thread with default parameters.
383 */
384 inline timer_thread_factory_t
timer_list_factory()385 timer_list_factory()
386 {
387 return &create_timer_list_thread;
388 }
389 /*!
390 * \}
391 */
392
393 //
394 // timer_manager_t
395 //
396
397 //! Timer manager interface.
398 /*!
399 * Interface for all implementations of timer_managers.
400 *
401 * Timer managers do not create externals threads and must not use
402 * any mutexs/spinlocks inside. All work must be done only on the context
403 * of the caller thread.
404 *
405 * \since
406 * v.5.5.19
407 */
408 class SO_5_TYPE timer_manager_t
409 {
410 timer_manager_t( const timer_manager_t & ) = delete;
411 timer_manager_t &
412 operator=( const timer_manager_t & ) = delete;
413
414 public:
415 /*!
416 * \brief An interface for collector of elapsed timers.
417 *
418 * Handling of elapsed timers in single threaded environments
419 * differs from handling in multi-threaded environments.
420 * When timers are handled by separated timer thread then that
421 * thread can send delayed/periodic messages directly to target
422 * mboxes. But in single-threaded environment this is impossible
423 * if some receiver is bound to the default dispatcher. In that
424 * case a deadlock is possible.
425 *
426 * Because of that timer_managers uses different schemes: main
427 * working thread periodically calls process_expired_timers() method
428 * and collects elapsed timers. Then the main working thread handles
429 * all elapsed timers.
430 *
431 * To collect elapsed timer some container is necessary.
432 * The class elapsed_timers_collector_t describes interface of
433 * that container.
434 */
435 class SO_5_TYPE elapsed_timers_collector_t
436 {
437 elapsed_timers_collector_t( const elapsed_timers_collector_t & ) = delete;
438 elapsed_timers_collector_t &
439 operator=( const elapsed_timers_collector_t & ) = delete;
440
441 public :
442 elapsed_timers_collector_t() noexcept = default;
443 virtual ~elapsed_timers_collector_t() noexcept = default;
444
445 //! Accept and store info about elapsed timer.
446 virtual void
447 accept(
448 //! A type of message to be sent.
449 std::type_index type_index,
450 //! Target mbox for the message.
451 mbox_t mbox,
452 //! A message to be sent.
453 message_ref_t msg ) = 0;
454 };
455
456 timer_manager_t() noexcept = default;
457 virtual ~timer_manager_t() noexcept = default;
458
459 //! Translation of expired timers into message sends.
460 virtual void
461 process_expired_timers() = 0;
462
463 //! Calculate time before the nearest timer (if any).
464 /*!
465 * Return timeout before the nearest timer or \c default_timeout
466 * if there is no any timer.
467 */
468 virtual std::chrono::steady_clock::duration
469 timeout_before_nearest_timer(
470 //! Default timeout if there is no any timer.
471 std::chrono::steady_clock::duration default_timer ) = 0;
472
473 //! Push delayed/periodic message to the timer queue.
474 /*!
475 * A timer can be deactivated later by using returned timer_id.
476 */
477 virtual timer_id_t
478 schedule(
479 //! Type of message to be sheduled.
480 const std::type_index & type_index,
481 //! Mbox for message delivery.
482 const mbox_t & mbox,
483 //! Message to be sent.
484 const message_ref_t & msg,
485 //! Pause before first message delivery.
486 std::chrono::steady_clock::duration pause,
487 //! Period for message repetition.
488 //! Zero value means single shot delivery.
489 std::chrono::steady_clock::duration period ) = 0;
490
491 //! Push anonymous delayed/periodic message to the timer queue.
492 /*!
493 * A timer cannot be deactivated later.
494 */
495 virtual void
496 schedule_anonymous(
497 //! Type of message to be sheduled.
498 const std::type_index & type_index,
499 //! Mbox for message delivery.
500 const mbox_t & mbox,
501 //! Message to be sent.
502 const message_ref_t & msg,
503 //! Pause before first message delivery.
504 std::chrono::steady_clock::duration pause,
505 //! Period for message repetition.
506 //! Zero value means single shot delivery.
507 std::chrono::steady_clock::duration period ) = 0;
508
509 /*!
510 * \return true if there is no any pending timers.
511 */
512 virtual bool
513 empty() = 0;
514
515 /*!
516 * \since
517 * v.5.5.4
518 *
519 * \brief Get statistics for run-time monitoring.
520 */
521 virtual timer_thread_stats_t
522 query_stats() = 0;
523 };
524
525 //! Auxiliary typedef for timer_manager autopointer.
526 /*!
527 * \since
528 * v.5.5.19
529 */
530 using timer_manager_unique_ptr_t = std::unique_ptr< timer_manager_t >;
531
532 //
533 // timer_manager_factory_t
534 //
535 /*!
536 * \brief Type of factory for creating timer_manager objects.
537 *
538 * \since
539 * v.5.5.19
540 */
541 using timer_manager_factory_t = std::function<
542 timer_manager_unique_ptr_t(
543 error_logger_shptr_t,
544 outliving_reference_t< timer_manager_t::elapsed_timers_collector_t > ) >;
545
546 /*!
547 * \name Tools for creating timer managers.
548 * \{
549 */
550 /*!
551 * \brief Create timer manager based on timer_wheel mechanism.
552 * \note Default parameters will be used for timer manager.
553 *
554 * \since
555 * v.5.5.19
556 */
557 SO_5_FUNC timer_manager_unique_ptr_t
558 create_timer_wheel_manager(
559 //! A logger for handling error messages inside timer_manager.
560 error_logger_shptr_t logger,
561 //! A collector for elapsed timers.
562 outliving_reference_t< timer_manager_t::elapsed_timers_collector_t >
563 collector );
564
565 /*!
566 * \since
567 * v.5.5.0
568 *
569 * \brief Create timer manager based on timer_wheel mechanism.
570 * \note Parameters must be specified explicitely.
571 */
572 SO_5_FUNC timer_manager_unique_ptr_t
573 create_timer_wheel_manager(
574 //! A logger for handling error messages inside timer_manager.
575 error_logger_shptr_t logger,
576 //! A collector for elapsed timers.
577 outliving_reference_t< timer_manager_t::elapsed_timers_collector_t >
578 collector,
579 //! Size of the wheel.
580 unsigned int wheel_size,
581 //! A size of one time step for the wheel.
582 std::chrono::steady_clock::duration granuality );
583
584 /*!
585 * \since
586 * v.5.5.0
587 *
588 * \brief Create timer manager based on timer_heap mechanism.
589 * \note Default parameters will be used for timer manager.
590 */
591 SO_5_FUNC timer_manager_unique_ptr_t
592 create_timer_heap_manager(
593 //! A logger for handling error messages inside timer_manager.
594 error_logger_shptr_t logger,
595 //! A collector for elapsed timers.
596 outliving_reference_t< timer_manager_t::elapsed_timers_collector_t >
597 collector );
598
599 /*!
600 * \since
601 * v.5.5.0
602 *
603 * \brief Create timer manager based on timer_heap mechanism.
604 * \note Parameters must be specified explicitely.
605 */
606 SO_5_FUNC timer_manager_unique_ptr_t
607 create_timer_heap_manager(
608 //! A logger for handling error messages inside timer_manager.
609 error_logger_shptr_t logger,
610 //! A collector for elapsed timers.
611 outliving_reference_t< timer_manager_t::elapsed_timers_collector_t >
612 collector,
613 //! Initical capacity of heap array.
614 std::size_t initial_heap_capacity );
615
616 /*!
617 * \since
618 * v.5.5.0
619 *
620 * \brief Create timer thread based on timer_list mechanism.
621 */
622 SO_5_FUNC timer_manager_unique_ptr_t
623 create_timer_list_manager(
624 //! A logger for handling error messages inside timer_manager.
625 error_logger_shptr_t logger,
626 //! A collector for elapsed timers.
627 outliving_reference_t< timer_manager_t::elapsed_timers_collector_t >
628 collector );
629 /*!
630 * \}
631 */
632
633 /*!
634 * \name Standard timer manager factories.
635 * \{
636 */
637 /*!
638 * \since
639 * v.5.5.0
640 *
641 * \brief Factory for timer_wheel manager with default parameters.
642 */
643 inline timer_manager_factory_t
timer_wheel_manager_factory()644 timer_wheel_manager_factory()
645 {
646 // Use this trick because create_timer_wheel_thread is overloaded.
647 timer_manager_unique_ptr_t (*f)(
648 error_logger_shptr_t,
649 outliving_reference_t<
650 timer_manager_t::elapsed_timers_collector_t > ) =
651 create_timer_wheel_manager;
652
653 return f;
654 }
655
656 /*!
657 * \since
658 * v.5.5.0
659 *
660 * \brief Factory for timer_wheel manager with explicitely specified parameters.
661 */
662 inline timer_manager_factory_t
timer_wheel_manager_factory(unsigned int wheel_size,std::chrono::steady_clock::duration granularity)663 timer_wheel_manager_factory(
664 //! Size of the wheel.
665 unsigned int wheel_size,
666 //! A size of one time step for the wheel.
667 std::chrono::steady_clock::duration granularity )
668 {
669 // Use this trick because create_timer_wheel_thread is overloaded.
670 timer_manager_unique_ptr_t (*f)(
671 error_logger_shptr_t,
672 outliving_reference_t<
673 timer_manager_t::elapsed_timers_collector_t >,
674 unsigned int,
675 std::chrono::steady_clock::duration ) =
676 create_timer_wheel_manager;
677
678 return std::bind(
679 f,
680 std::placeholders::_1,
681 std::placeholders::_2,
682 wheel_size,
683 granularity );
684 }
685
686 /*!
687 * \since
688 * v.5.5.0
689 *
690 * \brief Factory for timer_heap manager with default parameters.
691 */
692 inline timer_manager_factory_t
timer_heap_manager_factory()693 timer_heap_manager_factory()
694 {
695 // Use this trick because create_timer_heap_thread is overloaded.
696 timer_manager_unique_ptr_t (*f)(
697 error_logger_shptr_t,
698 outliving_reference_t<
699 timer_manager_t::elapsed_timers_collector_t > ) =
700 create_timer_heap_manager;
701 return f;
702 }
703
704 /*!
705 * \since
706 * v.5.5.0
707 *
708 * \brief Factory for timer_heap manager with explicitely specified parameters.
709 */
710 inline timer_manager_factory_t
timer_heap_manager_factory(std::size_t initial_heap_capacity)711 timer_heap_manager_factory(
712 //! Initial capacity of heap array.
713 std::size_t initial_heap_capacity )
714 {
715 // Use this trick because create_timer_heap_thread is overloaded.
716 timer_manager_unique_ptr_t (*f)(
717 error_logger_shptr_t,
718 outliving_reference_t<
719 timer_manager_t::elapsed_timers_collector_t >,
720 std::size_t ) =
721 create_timer_heap_manager;
722
723 return std::bind(
724 f,
725 std::placeholders::_1,
726 std::placeholders::_2,
727 initial_heap_capacity );
728 }
729
730 /*!
731 * \since
732 * v.5.5.0
733 *
734 * \brief Factory for timer_list manager with default parameters.
735 */
736 inline timer_manager_factory_t
timer_list_manager_factory()737 timer_list_manager_factory()
738 {
739 return &create_timer_list_manager;
740 }
741 /*!
742 * \}
743 */
744
745 namespace internal_timer_helpers {
746
747 /*!
748 * \brief Helper function for timer_thread creation.
749 *
750 * \since
751 * v.5.5.0
752 */
753 inline timer_thread_unique_ptr_t
create_appropriate_timer_thread(error_logger_shptr_t error_logger,const timer_thread_factory_t & user_factory)754 create_appropriate_timer_thread(
755 error_logger_shptr_t error_logger,
756 const timer_thread_factory_t & user_factory )
757 {
758 if( user_factory )
759 return user_factory( std::move( error_logger ) );
760 else
761 return create_timer_heap_thread( std::move( error_logger ) );
762 }
763
764 } /* namespace internal_timer_helpers */
765
766 #if defined( SO_5_MSVC )
767 #pragma warning(pop)
768 #endif
769
770 } /* namespace so_5 */
771
772