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