SObjectizer  5.8
Loading...
Searching...
No Matches
3rd_party/timertt/all.hpp
Go to the documentation of this file.
1/*
2 * TimerThreadTemplate
3 */
4
5/*!
6 * \file timertt/all.hpp
7 * \brief All project's stuff.
8 */
9
10#pragma once
11
12#include <array>
13#include <atomic>
14#include <chrono>
15#include <condition_variable>
16#include <cstdint>
17#include <cstdlib>
18#include <ctime>
19#include <functional>
20#include <iostream>
21#include <map>
22#include <memory>
23#include <mutex>
24#include <sstream>
25#include <stdexcept>
26#include <string>
27#include <thread>
28#include <tuple>
29#include <unordered_map>
30#include <vector>
31
32#if defined(_MSC_VER) && (_MSC_VER < 1900)
33 // Under VS++12 the alignment will always be 8 bytes.
34 #define TIMERTT_ALIGNAS_WORKAROUND(T) __declspec(align(8))
35
36 // VS++12 doesn't support noexcept keyword.
37 #define TIMERTT_NOEXCEPT
38 #define TIMERTT_HAS_NOEXCEPT 0
39#else
40 #define TIMERTT_ALIGNAS_WORKAROUND(T) alignas(T)
41 #define TIMERTT_NOEXCEPT noexcept
42 #define TIMERTT_HAS_NOEXCEPT 1
43#endif
44
45/*!
46 * \brief The current version of timertt.
47 *
48 * Please note that this macro was added only in version 1.2.1.
49 * It means that it is better to check presense of TIMERTT_VERSION and
50 * only then try to check its value:
51 * \code
52 * #include <timertt/all.hpp>
53 *
54 * #if defined(TIMERTT_VERSION)
55 * #if TIMERTT_VERSION > 1002002
56 * #endif
57 * #endif
58 * \endcode
59 *
60 * The value of TIMERTT_VERSION has YXXXZZZ format in decimal. For
61 * example: 1002001 or 1003014. 'Y' means the major version number
62 * (e.g. 1 for 1.2.1), 'XXX' means minor version number
63 * (e.g. 002 for 1.2.1) and 'ZZZ' means patch number
64 * (e.g. 001 for 1.2.1). It means that version 1.2.1 will be
65 * represented as 1002001 and 1.3.14 will be represented as 1003014.
66 *
67 * \since
68 * v.1.2.1
69 */
70#define TIMERTT_VERSION 1002003u
71
72/*!
73 * \brief Top-level project's namespace.
74 */
75namespace timertt
76{
77
78/*!
79 * \brief Container for thread safety flags.
80 *
81 * \since
82 * v.1.1.0
83 */
85{
86 /*!
87 * \brief Indicator for not-thread-safe implementation.
88 *
89 * \since
90 * v.1.1.0
91 */
92 struct unsafe {};
93
94 /*!
95 * \brief Indicator for thread-safe implemetation.
96 *
97 * \since
98 * v.1.1.0
99 */
100 struct safe {};
101};
102
103namespace details
104{
105
106//! Status of timer.
107enum class timer_status : unsigned int
108{
109 //! Timer is deactivated.
110 /*! It can be activated or destroyed safely. */
112 //! Timer is activated.
113 /*! It can be safely deactivated and destroyed. */
114 active,
115 //! Timer is in execution list and is waiting for execution.
116 /*!
117 * It cannot be deactivated and destroyed right now.
118 * Status of timer can only be changed to wait_for_deactivation.
119 * And actual deactivation will be performed later, after
120 * processing of execution list.
121 */
123 //! Timer must be deactivated after processing of execution list.
124 /*!
125 * The only possible switch for the timer is to deactivated status.
126 */
128};
129
130} /* namespace details */
131
132/*!
133 * \brief Container for thread-safety-specific type declarations.
134 *
135 * \note Will be specialized for every thread-safety case.
136 *
137 * \tparam Thread_Safety must be thread_safety::unsafe or thread_safety::safe.
138 *
139 * \since
140 * v.1.1.0
141 */
142template< typename Thread_Safety >
144
145/*!
146 * \brief Specialization for not-thread-safe case.
147 *
148 * \since
149 * v.1.1.0
150 */
151template<>
153{
154 //! Type for reference counters.
155 typedef unsigned int reference_counter_type;
156
157 //! Type for holding timer status inside a timer object.
159};
160
161/*!
162 * \brief Specialization for thread-safe case.
163 *
164 * \since
165 * v.1.1.0
166 */
167template<>
169{
170 //! Type for reference counters.
172
173 //! Type for holding timer status inside a timer object.
175};
176
177//
178// timer_object
179//
180
181/*!
182 * \brief Base type for timer demands.
183 *
184 * \tparam Thread_Safety Thread-safety indicator. Must be thread_safety::unsafe
185 * or thread_safety::safe type.
186 */
187template< typename Thread_Safety >
189{
190 //! Reference counter for the demand.
192
193 //! Deafault constructor.
195 {
196 m_references = 0;
197 }
198
199 inline virtual ~timer_object()
200 {}
201
202 //! Increment reference counter for the demand.
203 static inline void
205 {
206 ++(t->m_references);
207 }
208
209 //! Decrement reference counter for the demand and destroy
210 //! demand if there is no more references.
211 static inline void
213 {
214 if( 0 == --(t->m_references) )
215 delete t;
216 }
217};
218
219//
220// scoped_timer_object_holder
221//
222/*!
223 * \brief A special wrapper to be used to hold an actual timer object which
224 * is not allocated dynamically.
225 *
226 * This class is a part of support for scoped timer objects. Every timer
227 * engine will define its own `scoped_timer_object` by using this
228 * template class. Something like:
229 * \code
230 * class some_engine
231 * {
232 * struct timer_type { ... };
233 * public :
234 * using scoped_timer_object = scoped_timer_object_holder<timer_type>;
235 * ...
236 * };
237 * \endcode
238 *
239 * \par Some implementation details.
240 * Version 1.2.0 doesn't change way of working with actual timer objects.
241 * They are still reference countable. It means that when a scoped timer object
242 * is passed to engine's `activate` method a reference count will be
243 * incremented. When this object is passed to `deactivate` method then
244 * reference counter will be decremented. If reference counter becomes zero
245 * then the timer object will be deallocated by calling `delete`.
246 * To prevent this scoped_timer_object_holder automatically incremented
247 * reference counter by 1 in the constructor. It means that the reference
248 * counter will not be zero (in normal scenarios).
249 *
250 * \note
251 * This type is not Copyable nor Moveable.
252 *
253 * \since
254 * v.1.2.0
255 */
256template< typename Actual_Object >
258{
259 Actual_Object m_object;
260
261public :
263 {
264 // Actual object must have yet another reference to prevent its deletion.
266 }
267
270
271 Actual_Object *
272 ptr() { return &m_object; }
273};
274
275//
276// timer_object_holder
277//
278/*!
279 * \brief An intrusive smart pointer to timer demand.
280 *
281 * \tparam Thread_Safety Thread-safety indicator. Must be thread_safety::unsafe
282 * or thread_safety::safe type.
283 */
284template< typename Thread_Safety >
286{
287public :
288 //! Default constructor.
289 /*!
290 * Constructs a null pointer.
291 */
293 : m_timer( nullptr )
294 {}
295 //! Constructor for a raw pointer.
296 inline timer_object_holder( timer_object< Thread_Safety > * t )
297 : m_timer( t )
298 {
299 take_object();
300 }
301 //! Copy constructor.
303 : m_timer( o.m_timer )
304 {
305 take_object();
306 }
307 //! Move constructor.
309 : m_timer( o.m_timer )
310 {
311 o.m_timer = nullptr;
312 }
313
314 //! Constructor for the case when timer object is a scoped timer.
315 template< typename Actual_Object >
317 scoped_timer_object_holder<Actual_Object> & scoped )
319 {}
320
321 //! Destructor.
323 {
325 }
326
327 //! Copy operator.
328 inline timer_object_holder &
330 {
332 swap( t );
333 return *this;
334 }
335
336 //! Move operator.
337 inline timer_object_holder &
339 {
341 swap( t );
342 return *this;
343 }
344
345 //! Swap values.
346 inline void
348 {
349 auto t = m_timer;
350 m_timer = o.m_timer;
351 o.m_timer = t;
352 }
353
354 /*!
355 * \brief Drop controlled object.
356 */
357 inline void
359 {
361 }
362
363 //! Is this a null pointer?
364 /*!
365 i.e. whether get() != 0.
366
367 \retval true if *this manages an object.
368 \retval false otherwise.
369 */
370 inline operator bool() const
371 {
372 return nullptr != m_timer;
373 }
374
375 /*!
376 * \name Access to object.
377 * \{
378 */
379 inline timer_object< Thread_Safety > *
380 get() const
381 {
382 return m_timer;
383 }
384
385 template< class O >
386 O *
388 {
389 if( !m_timer )
390 throw std::runtime_error( "timer is nullptr" );
391
392 return static_cast< O * >(m_timer);
393 }
394 /*!
395 * \}
396 */
397
398private :
399 //! Timer controlled by a smart pointer.
400 timer_object< Thread_Safety > * m_timer;
401
402 //! Increment reference count to object if it's not null.
403 inline void
409
410 //! Decrement reference count to object and delete it if needed.
411 inline void
413 {
414 if( m_timer )
415 {
417 m_timer = nullptr;
418 }
419 }
420};
421
422
423//
424// default_error_logger
425//
426
427/*!
428 * \brief Class of default error logger.
429 *
430 * This class uses std::cerr as the stream for logging errors.
431 */
433{
434 //! Logs error message to std::cerr.
435 inline void
437 //! The text of error message.
438 const std::string & what )
439 {
440 std::cerr << what << std::endl;
441 }
442};
443
444//
445// default_actor_exception_handler
446//
447
448/*!
449 * \brief Class of default handler for exceptions thrown from timer actors.
450 *
451 * Calls std::abort() to terminate application execution.
452 */
454{
455 //! Handles exception.
456 inline void
458 //! An exception from timer actor.
459 const std::exception & )
460 {
461 std::abort();
462 }
463};
464
465//
466// default_timer_action_type
467//
468/*!
469 * \brief Defaulf type of timer action.
470 *
471 * \since
472 * v.1.2.0
473 */
475
476//
477// monotonic_clock
478//
479/*!
480 * \brief Type of clock used by all threads.
481 */
483
484//
485// timer_quantities
486//
487/*!
488 * \brief Information about quantities of various timer types.
489 *
490 * \since
491 * v.1.1.1
492 */
494{
495 //! Quantity of single-shot timers.
497
498 //! Quantity of periodic timers.
500};
501
502/*!
503 * \brief An internal namespace with implementation details.
504 */
505namespace details
506{
507
508// NOTE: invoke_noexcept_code_block was added because some compilers
509// do not have support for noexcept (for example VC++2013). So it's impossible
510// to write code like that:
511//
512// [&]() noexcept {
513// some_code();
514// }();
515//
516// Instead we have to write:
517//
518// invoke_noexcept_code_block([&] {
519// some_code();
520// });
521//
523
524template< typename Lambda >
525void invoke_noexcept_code_block(Lambda && lambda) noexcept
526{
527 lambda();
528}
529
530#else
531
532template< typename Lambda >
534{
535 try { lambda(); } catch(...) { std::abort(); }
536}
537
538#endif
539
540//
541// buffer_allocated_object
542//
543/*!
544 * \brief A special storage to be used for holding non-default constructible
545 * objects which are created by demand.
546 *
547 * \note
548 * In C++17 std::optional can be used instead of this class.
549 *
550 * \since
551 * v.1.2.0
552 */
553template<typename T>
555{
556 TIMERTT_ALIGNAS_WORKAROUND(T) std::array<char, sizeof(T)> buffer_;
557 bool allocated_{ false };
558
560 {
561 if(allocated_)
562 {
563 get()->~T();
564 allocated_ = false;
565 }
566 }
567
568public :
569 using pointer = T*;
570 using element_type = T;
572
576
581
582 template<typename... Args>
583 void allocate(Args &&...args)
584 {
586 new(buffer_.data()) T(std::forward<Args>(args)...);
587 allocated_ = true;
588 }
589
590 void destroy()
591 {
593 }
594
596 {
597 return allocated_;
598 }
599
600 pointer get() const TIMERTT_NOEXCEPT
601 {
602 return reinterpret_cast<pointer>(const_cast<char *>(buffer_.data()));
603 }
604
605 pointer operator->() const TIMERTT_NOEXCEPT
606 {
607 return get();
608 }
609
611 {
612 return *get();
613 }
614};
615
616//
617// timer_action_holder
618//
619/*!
620 * \brief A special storage for holding timer actions.
621 *
622 * If a timer action is represented by std::function<void()> then
623 * a simple std::function can be used for holding this timer action.
624 * But if a timer action is represented by some user type then
625 * we must use a internal buffer and should construct timer action
626 * instance inplace only when it is necessary.
627 *
628 * \since
629 * v.1.2.0
630 */
631template< typename Action_Type >
633{
635
636public:
640
641 void
642 assign( Action_Type && action )
643 {
645 }
646
647 void
648 exec() const
649 {
650 (*m_action)();
651 }
652};
653
654template<>
656{
658
659public :
663
664 void
666 {
667 m_action = std::move(action);
668 }
669
670 void
671 exec() const
672 {
673 m_action();
674 }
675};
676
677//
678// timer_kind
679//
680/*!
681 * \brief Type of the timer (single-shot or periodic).
682 *
683 * \since
684 * v.1.1.1
685 */
686enum class timer_kind
687{
688 //! Timer is a single-shot timer.
690 //! Timer is a periodic timer.
692};
693
694//
695// engine_common
696//
697
698/*!
699 * \brief A common part for all timer engines.
700 *
701 * Will be used by concrete engines for storing instances of
702 * Error_Logger and Actor_Exception_Handler.
703 *
704 * Also defines type \a thread_safety to be used later.
705 *
706 * \tparam Thread_Safety Thread-safety indicator.
707 * Must be timertt::thread_safety::unsafe or timertt::thread_safety::safe.
708 *
709 * \tparam Timer_Action type of functor to perform an user-defined
710 * action when timer expires. This must be Moveable and MoveConstructible
711 * type.
712 *
713 * \tparam Error_Logger type of logger for errors detected during
714 * timer handling. Interface for error logger is defined
715 * by default_error_logger class.
716 *
717 * \tparam Actor_Exception_Handler type of handler for dealing with
718 * exceptions thrown from timer actors. Interface for exception handler
719 * is defined by default_actor_exception_handler.
720 *
721 * \since
722 * v.1.1.0
723 */
724template<
725 typename Thread_Safety,
726 typename Timer_Action,
727 typename Error_Logger,
728 typename Actor_Exception_Handler >
730{
731public :
732 //! Indicator of thread-safety.
733 using thread_safety = Thread_Safety;
734
735 //! Alias for Timer_Action.
736 using timer_action = Timer_Action;
737
738 //! Initializing constructor.
740 Error_Logger error_logger,
741 Actor_Exception_Handler exception_handler )
744 {}
745
746 /*!
747 * \brief Get the quantities of timers of various types.
748 *
749 * \since
750 * v.1.1.1
751 */
754 {
755 return this->m_timer_quantities;
756 }
757
758protected :
759 //! Error logger.
760 Error_Logger m_error_logger;
761
762 //! Exception handler.
763 Actor_Exception_Handler m_exception_handler;
764
765 /*!
766 * \brief Quantities of timers of various types.
767 *
768 * \since
769 * v.1.1.1
770 */
772
773 /*!
774 * \brief Helper method for increment the count of timers of
775 * the specific type.
776 *
777 * \since
778 * v.1.1.1
779 */
780 void
788
789 /*!
790 * \brief Helper method for decrement the count of timers of
791 * the specific type.
792 *
793 * \since
794 * v.1.1.1
795 */
796 void
804
805 /*!
806 * \brief Helper method for reseting quantities of timers to zero.
807 *
808 * \since
809 * v.1.1.1
810 */
811 void
816};
817
818//
819// timer_wheel_engine_defaults
820//
821/*!
822 * \brief Container for static method with default values for
823 * timer_wheel engine.
824 *
825 * \since
826 * v.1.1.0
827 */
829{
830 //! Default wheel size.
831 inline static unsigned int
832 default_wheel_size() { return 1000; }
833
834 //! Default tick duration.
835 inline static monotonic_clock::duration
836 default_granularity() { return std::chrono::milliseconds( 10 ); }
837};
838
839//
840// timer_wheel_engine
841//
842
843/*!
844 * \brief A engine for timer wheel mechanism.
845 *
846 * This class uses <a href="http://www.cs.columbia.edu/~nahum/w6998/papers/ton97-timing-wheels.pdf">timer_wheel</a>
847 * mechanism to work with timers.
848 * This mechanism is efficient for working with big amount of timers.
849 * But it requires that timer thread is working always, even in case
850 * when there is no timers. Another price for timer_wheel is the
851 * granularity of timer steps.
852 *
853 * Timer wheel data structure consists from one fixed size vector
854 * (the wheel) and several double-linked list (one list for every wheel
855 * slot).
856 *
857 * At the start of next time step thread switches to next wheel position
858 * and handles timers from this position list. After processing
859 * all elapsed single-shot timers will be removed and deactivated, and
860 * all elapsed periodic timers will be rescheduled for the new time steps.
861 *
862 * \note At the beginnig of time step thread detects elapsed timers, then
863 * unblocks object mutex and calls timer actors for those timers. It means
864 * that actors call call timer thread object. And there won't be frequent
865 * mutex locking/unlocking operations for building and processing
866 * list of elapsed timers. This allows to process millions of timer actor
867 * per second.
868 *
869 * \tparam Thread_Safety Thread-safety indicator.
870 * Must be timertt::thread_safety::unsafe or timertt::thread_safety::safe.
871 *
872 * \tparam Timer_Action type of functor to perform an user-defined
873 * action when timer expires. This must be Moveable and MoveConstructible
874 * type.
875 *
876 * \tparam Error_Logger type of logger for errors detected during
877 * timer thread execution. Interface for error logger is defined
878 * by default_error_logger class.
879 *
880 * \tparam Actor_Exception_Handler type of handler for dealing with
881 * exceptions thrown from timer actors. Interface for exception handler
882 * is defined by default_actor_exception_handler.
883 */
884template<
885 typename Thread_Safety,
886 typename Timer_Action,
887 typename Error_Logger,
888 typename Actor_Exception_Handler >
890 : public engine_common<
891 Thread_Safety, Timer_Action, Error_Logger, Actor_Exception_Handler >
892{
893 //! An alias for base class.
894 using base_type = engine_common<
895 Thread_Safety, Timer_Action, Error_Logger, Actor_Exception_Handler >;
896
897 struct timer_type;
898
899public :
900 //! Type with default parameters for this engine.
901 using defaults_type = timer_wheel_engine_defaults;
902
903 //! Alias for timer_action type.
904 using timer_action = typename base_type::timer_action;
905
906 //! Alias for scoped timer object.
907 using scoped_timer_object =
909
910 //! Constructor with all parameters.
912 //! Size of the wheel.
913 unsigned int wheel_size,
914 //! Size of time step for the timer_wheel.
915 monotonic_clock::duration granularity,
916 //! An error logger for timer thread.
917 Error_Logger error_logger,
918 //! An actor exception handler for timer thread.
919 Actor_Exception_Handler exception_handler )
923 {
925
927 }
928
929 //! Destructor.
931 {
932 clear_all();
933 }
934
935 //! Create timer to be activated later.
936 timer_object_holder< Thread_Safety >
938 {
940 }
941
942 //! Activate timer and schedule it for execution.
943 /*!
944 * \return Value \a true is returned only when the first timer is added to
945 * the empty wheel.
946 *
947 * \throw std::exception If timer thread is not started.
948 * \throw std::exception If \a timer is already activated.
949 *
950 * \tparam Duration_1 actual type which represents time duration.
951 * \tparam Duration_2 actual type which represents time duration.
952 */
953 template< class Duration_1, class Duration_2 >
954 bool
956 //! Timer to be activated.
957 timer_object_holder< Thread_Safety > timer,
958 //! Pause for timer execution.
959 Duration_1 pause,
960 //! Repetition period.
961 //! If <tt>Duration_2::zero() == period</tt> then timer will be
962 //! single-shot.
963 Duration_2 period,
964 //! Action for the timer.
965 timer_action action )
966 {
967 auto * wheel_timer = timer.template cast_to< timer_type >();
969
971
972 // Timer must be taken under control.
974 // It is an active timer now.
976
978
979 // If wheel was empty and this is the first timer added
980 // the value of timer_count must be exactly 1.
981 return 1 == this->m_timer_quantities.m_single_shot_count +
983 }
984
985 /*!
986 * \brief Perform an attempt to reschedule a timer.
987 *
988 * Before v.1.2.1 there was just one way to reschedule a timer:
989 * method deactivate() must be called first and then method
990 * activate() must be called for the same timer. This approach is
991 * not fast because in the case of thread-safe engines it requires
992 * two operations on a mutex.
993 *
994 * Since v.1.2.1 there is a reschedule() method which does deactivation
995 * of a timer (if it is active) and then new activation of this timer.
996 * All actions are performed by using just one operation on a mutex.
997 *
998 * \note
999 * This operation can fail if the timer to be rescheduled is in processing.
1000 * Because of that it is recommended to use such operation for
1001 * timer_managers only. But even with timer_managers this operation
1002 * should be used with care.
1003 *
1004 * \attention
1005 * It move operator for a timer_action throws then timer will be
1006 * deactivated. The state for a timer_action itself will be unknown.
1007 *
1008 * \throw std::exception If timer thread is not started.
1009 * \throw std::exception If \a timer is in processing right now.
1010 *
1011 * \tparam Duration_1 actual type which represents time duration.
1012 * \tparam Duration_2 actual type which represents time duration.
1013 *
1014 * \since
1015 * v.1.2.1
1016 */
1017 template< class Duration_1, class Duration_2 >
1018 bool
1020 //! Timer to be rescheduled. Must be in activated or deactivated state.
1021 timer_object_holder< Thread_Safety > timer,
1022 //! Pause for timer execution.
1023 Duration_1 pause,
1024 //! Repetition period.
1025 //! If <tt>Duration_2::zero() == period</tt> then timer will be
1026 //! single-shot.
1027 Duration_2 period,
1028 //! Action for the timer.
1029 timer_action action )
1030 {
1031 auto * wheel_timer = timer.template cast_to< timer_type >();
1032 // If timer is deactivated the usual activation logic can be used.
1034 return this->activate(
1036 else if( timer_status::active != wheel_timer->m_status )
1037 {
1038 // Timer which is in processing now can't be reactivated.
1039 throw std::runtime_error( "timer is in processing now, "
1040 "it can't be rescheduled" );
1041 }
1042
1043 // Timer must be removed from the wheel first.
1045 this->dec_timer_count( wheel_timer->kind() );
1046
1047 // If this assigment throws then we must deactivate the timer.
1048 try
1049 {
1051 }
1052 catch(...)
1053 {
1056 // Exception must be rethrown;
1057 throw;
1058 }
1059
1061
1062 return false;
1063 }
1064
1065 //! Deactivate timer and remove it from the wheel.
1066 void
1067 deactivate( timer_object_holder< Thread_Safety > timer )
1068 {
1069 auto wheel_timer = timer.template cast_to< timer_type >();
1071 {
1072 // This is normal active timer. It can be safely
1073 // deactivated and destroyed.
1075
1077
1078 // Release timer object.
1079 this->dec_timer_count( wheel_timer->kind() );
1081 }
1083 {
1084 // This timer is in execution list right now.
1085 // We can only changed its status.
1086 // Final deactivation will be done after execution of
1087 // timers actions.
1089 }
1090 }
1091
1092 /*!
1093 * \brief Build sublist of elapsed timers and process them all.
1094 */
1095 template< typename Unique_Lock >
1096 void
1098 //! Object's lock.
1099 Unique_Lock & lock )
1100 {
1101 /*
1102 * NOTE: It is possible that period between consequtive
1103 * calls to process_expired_timers will be longer than
1104 * m_granuality (it is possible when engine is used inside
1105 * timer_manager). In that case it is necessary to process
1106 * several wheel positions at once.
1107 */
1108 const auto now = monotonic_clock::now();
1109 for(;;)
1110 {
1112 {
1114
1115 // After processing all current demands and rescheduling
1116 // all periodic demands the current_position must be
1117 // advanced.
1118 m_current_position += 1;
1121
1123 }
1124
1125 if( now >= m_current_tick_border )
1126 {
1127 // A switch to next tick is necessary.
1130 }
1131 else
1132 break;
1133 }
1134 }
1135
1136 /*!
1137 * \brief Is empty timer list?
1138 */
1139 bool
1140 empty() const
1141 {
1142 return 0 == this->m_timer_quantities.m_single_shot_count &&
1144 }
1145
1146 /*!
1147 * \brief Get time point of the next timer.
1148 *
1149 * \attention Must be called only when \a !empty().
1150 */
1153 {
1155 return monotonic_clock::now();
1156 else
1157 return m_current_tick_border;
1158 }
1159
1160 /*!
1161 * \brief Deactivate all timers and cleanup internal data structures.
1162 */
1163 void
1165 {
1166 for( auto & item : m_wheel )
1167 {
1169 item = wheel_item();
1170
1171 while( timer )
1172 {
1173 timer_type * t = timer;
1174 timer = timer->m_next;
1175
1178 }
1179 }
1180
1181 // For the case of timer_engine restart.
1182 this->reset_timer_count();
1184 this->m_current_position = 0;
1185 }
1186
1187private :
1188 //! Type of wheel timer.
1189 struct timer_type : public timer_object< Thread_Safety >
1190 {
1191 //! Status of the timer.
1193
1194 //! Position in the wheel.
1195 unsigned int m_position = 0;
1196 //! Full rolls of wheel before execution of demand.
1197 unsigned int m_full_rolls_left = 0;
1198
1199 //! Period in ticks.
1200 /*!
1201 * Zero means that demand is single shot.
1202 */
1203 unsigned int m_period = 0;
1204
1205 //! Timer action.
1207
1208 //! Previous demand in the list.
1209 timer_type * m_prev = nullptr;
1210 //! Next demand in the list.
1211 timer_type * m_next = nullptr;
1212
1217
1218 /*!
1219 * \brief Detect type of the timer (single-shot or periodic).
1220 *
1221 * \since
1222 * v.1.1.1
1223 */
1225 kind() const
1226 {
1228 }
1229 };
1230
1231 //! Type of wheel's item.
1233 {
1234 //! Head of the demand's list.
1235 timer_type * m_head = nullptr;
1236 //! Tail of the demand's list.
1237 timer_type * m_tail = nullptr;
1238 };
1239
1240 /*!
1241 * \name Object's attributes.
1242 * \{
1243 */
1244 //! Size of the wheel.
1245 const unsigned int m_wheel_size;
1246
1247 //! Granularity of one time step.
1249
1250 //! Index of the current position in the wheel.
1251 unsigned int m_current_position = 0;
1252
1253 //! Right border of the current tick.
1254 /*!
1255 * This is the time point at which new tick must be started.
1256 */
1258
1259 //! Has the current tick been processed?
1261
1262 //! The wheel data.
1264 /*!
1265 * \}
1266 */
1267
1268 /*!
1269 * \brief Hard check for deactivation state of the timer.
1270 *
1271 * \throw std::runtimer_error if timer is not deactivated.
1272 */
1273 static void
1275 {
1277 throw std::runtime_error( "timer is not in 'deactivated' state" );
1278 }
1279
1280 /*!
1281 * \brief Perform insertion of a timer into wheel data structure.
1282 *
1283 * This method added to remove the duplication of code in
1284 * activate() and reschedule() methods.
1285 *
1286 * \note
1287 * This method doesn't change reference count to timer object.
1288 *
1289 * \since
1290 * v.1.2.1
1291 */
1292 template< class Duration_1, class Duration_2 >
1293 void
1295 //! Timer to be inserted.
1296 timer_type * wheel_timer,
1297 //! Pause for timer execution.
1298 Duration_1 pause,
1299 //! Repetition period.
1300 //! If <tt>Duration_2::zero() == period</tt> then timer will be
1301 //! single-shot.
1302 Duration_2 period )
1303 {
1304 // Calculate the demand position in the wheel.
1308
1309 // Special calculations for the periodic demand.
1310 if( monotonic_clock::duration::zero() != period )
1312 else
1313 wheel_timer->m_period = 0;
1314
1315 // Timer now can be reinserted into the wheel.
1317
1318 // Count of timers changed.
1319 this->inc_timer_count( wheel_timer->kind() );
1320 }
1321
1322 /*!
1323 * \brief Converion of duration to number of time steps.
1324 *
1325 * \note This implementation performs rounding up for duration
1326 * values. For example if granularity is 10ms and duration is
1327 * 15ms then result will be 2 time steps.
1328 *
1329 * \note Never return 0. If duration is less then granularity (even
1330 * after rounding up) the value 1 will be returned. E.g. timer
1331 * will be scheduled for the next time step.
1332 *
1333 * \tparam Duration actual type for duration representation.
1334 */
1335 template< class Duration >
1336 unsigned int
1338 //! Time duration to be converted in time steps count.
1339 Duration d ) const
1340 {
1341 auto d_units =
1343 .count();
1344 auto g_units = m_granularity.count();
1345
1346 unsigned int r = static_cast< unsigned int >(
1347 /*
1348 * Add g_units/2 for rounding up.
1349 * For example, if d is 24ms and granularity is 10
1350 * it will be (24+5)=29, and result will be 2.
1351 * But if d is 25ms then (25+5)=30 and result will be 3.
1352 */
1353 (d_units + g_units/2) / g_units );
1354 if( !r )
1355 r = 1;
1356 return r;
1357 }
1358
1359 /*!
1360 * \brief Calculate and fill up wheel position for the timer.
1361 *
1362 * timer_type::m_position and timer_type::m_full_rolls_left
1363 * will be set for \a wheel_timer.
1364 */
1365 void
1367 //! Timer to modify.
1368 timer_type * wheel_timer,
1369 //! Timeout for the timer is time steps.
1370 unsigned int pause_in_ticks ) const
1371 {
1375 }
1376
1377 /*!
1378 * \brief Insert timer to the wheel.
1379 *
1380 * If there is a non-empty timer list for the timer wheel position
1381 * the \a wheel_timer will be added to the end of that list.
1382 */
1383 void
1385 {
1387 if( item.m_head )
1388 {
1389 // There is a list of demands for the wheel position.
1390 // New demand must be added to the end of that list.
1392 wheel_timer->m_next = nullptr;
1395 }
1396 else
1397 {
1398 // There is no list of demands for this wheel position yet.
1399 // New list must be started.
1400 wheel_timer->m_prev = wheel_timer->m_next = nullptr;
1403 }
1404 }
1405
1406 /*!
1407 * \brief Remove timer from the timer_wheel.
1408 */
1409 void
1422
1423 /*!
1424 * \brief Detect elapsed timers for the current time step and
1425 * process them all.
1426 *
1427 * Object \a lock will be unlocked and then locked back.
1428 */
1429 template< class Unique_Lock >
1430 void
1432 Unique_Lock & lock )
1433 {
1435
1436 if( exec_list_head )
1437 {
1439
1441 }
1442 }
1443
1444 /*!
1445 * \brief Make list of elapsed timers to be executed.
1446 */
1447 timer_type *
1449 {
1450 timer_type * head = nullptr;
1451 timer_type * tail = nullptr;
1452
1454 while( timer )
1455 {
1457 {
1459 timer = timer->m_next;
1460 }
1461 else
1462 {
1463 timer_type * t = timer;
1464 timer = timer->m_next;
1465
1468
1469 if( head )
1470 {
1471 tail->m_next = t;
1472 t->m_prev = tail;
1473 t->m_next = nullptr;
1474 tail = t;
1475 }
1476 else
1477 {
1478 head = tail = t;
1479 t->m_prev = t->m_next = nullptr;
1480 }
1481 }
1482 }
1483
1484 return head;
1485 }
1486
1487 /*!
1488 * \brief Execute all active timers from the list.
1489 */
1490 template< class Unique_Lock >
1491 void
1493 //! Object lock.
1494 //! This lock will be unlocked before execution of actions
1495 //! and locked back after.
1496 Unique_Lock & lock,
1497 //! Head of execution list.
1498 //! Cannot be nullptr.
1500 {
1501 lock.unlock();
1502
1503 while( head )
1504 {
1505 try
1506 {
1507 // Status of timer can be changed. So it must be checked
1508 // just before execution. If timer is waiting for
1509 // deregistration it must not be executed.
1511 head->m_action.exec();
1512 }
1513 catch( const std::exception & x )
1514 {
1515 // Note this invoke_noexcept_code_block() is not needed
1516 // if compiler supports noexcept.
1517 invoke_noexcept_code_block( [this, &x] {
1518 this->m_exception_handler( x );
1519 } );
1520 }
1521 catch( ... )
1522 {
1523 // Logging should not throw exceptions.
1526 ss << __FILE__ << "(" << __LINE__
1527 << "): an unknown exception from timer action";
1528 this->m_error_logger( ss.str() );
1529 } );
1530
1531 std::abort();
1532 }
1533
1534 head = head->m_next;
1535 }
1536
1537 lock.lock();
1538 }
1539
1540 /*!
1541 * \brief Process list of elapsed timers after execution of
1542 * its actions.
1543 *
1544 * Active periodic timers will be rescheduled. All other timers
1545 * will be deactivated and removed.
1546 */
1547 void
1549 //! Head of execution list.
1550 //! Cannot be null.
1551 timer_type * head )
1552 {
1553 while( head )
1554 {
1555 timer_type * t = head;
1556 head = head->m_next;
1557
1558 // Actual periodic timer must be rescheduled.
1560 t->m_period )
1561 {
1562 // Timer is active again.
1564
1566
1568 }
1569 else
1570 {
1571 // Timer must be utilized.
1573 this->dec_timer_count( t->kind() );
1575 }
1576 }
1577 }
1578};
1579
1580//
1581// timer_list_engine_defaults
1582//
1583/*!
1584 * \brief Container for static method with default values for
1585 * timer_list engine.
1586 *
1587 * \note At this moment timer_list engine doesn't not need any
1588 * default parameter. Because of that this type is empty.
1589 * But it must be declared because of definition of
1590 * timer_list_engine::defaults_type.
1591 *
1592 * \since
1593 * v.1.1.0
1594 */
1596{
1597};
1598
1599//
1600// timer_list_engine
1601//
1602
1603/*!
1604 * \brief An engine for timer list mechanism.
1605 *
1606 * This engine uses double-linked list of timers as timer mechanism.
1607 * This list is ordered. The head of the list is the timer with the
1608 * minimum time point.
1609 *
1610 * When used with timer thread then the thread sleeps until the first timer in
1611 * the list elapsed. Then thread build sublist of elapsed timers and process
1612 * them. Single-shot timers are removed after processing. Periodic timers
1613 * rescheduled (inserted into appropriate places in the list).
1614 *
1615 * \note After building sublist of elapsed timers thread
1616 * unblocks object mutex and calls timer actors for timers from the sublist.
1617 * And locks object back right after processing. It means
1618 * that actors call call timer thread object. And there won't be frequent
1619 * mutex locking/unlocking operations for building and processing
1620 * sublist of elapsed timers. This allows to process millions of timer actor
1621 * per second.
1622 *
1623 * \attention This type of timer thread is good for situations
1624 * where there are many timers with equal pauses and repetition periods.
1625 * In that cases almost all timers will be added to the end of the
1626 * list. But if there are many timers with very different pauses then
1627 * operation of activating and rescheduling of timers will be too
1628 * expensive. Timer thread based on timer_wheel or timer_heap is
1629 * more appropriate for that scenario.
1630 *
1631 * \tparam Thread_Safety Thread-safety indicator.
1632 * Must be timertt::thread_safety::unsafe or timertt::thread_safety::safe.
1633 *
1634 * \tparam Timer_Action type of functor to perform an user-defined
1635 * action when timer expires. This must be Moveable and MoveConstructible
1636 * type.
1637 *
1638 * \tparam Error_Logger type of logger for errors detected during
1639 * timer thread execution. Interface for error logger is defined
1640 * by default_error_logger class.
1641 *
1642 * \tparam Actor_Exception_Handler type of handler for dealing with
1643 * exceptions thrown from timer actors. Interface for exception handler
1644 * is defined by default_actor_exception_handler.
1645 */
1646template<
1647 typename Thread_Safety,
1648 typename Time_Action,
1649 typename Error_Logger,
1650 typename Actor_Exception_Handler >
1652 : public engine_common<
1653 Thread_Safety, Time_Action, Error_Logger, Actor_Exception_Handler >
1654{
1655 //! An alias for base class.
1656 using base_type = engine_common<
1657 Thread_Safety, Time_Action, Error_Logger, Actor_Exception_Handler >;
1658
1659 // Forward declaration.
1660 struct timer_type;
1661
1662public :
1663 //! Type with default parameters for this engine.
1665
1666 //! Alias for timer_action type.
1667 using timer_action = typename base_type::timer_action;
1668
1669 //! Alias for scoped timer object.
1670 using scoped_timer_object =
1672
1673 //! Default constructor.
1679
1680 //! Constructor with all parameters.
1682 //! An error logger for timer thread.
1683 Error_Logger error_logger,
1684 //! An actor exception handler for timer thread.
1685 Actor_Exception_Handler exception_handler )
1687 {
1688 }
1689
1691 {
1692 clear_all();
1693 }
1694
1695 //! Create timer to be activated later.
1696 timer_object_holder< Thread_Safety >
1698 {
1699 return timer_object_holder< Thread_Safety >( new timer_type() );
1700 }
1701
1702 //! Activate timer and schedule it for execution.
1703 /*!
1704 *
1705 * \return true if the new timer is the first timer in the list.
1706 *
1707 * \throw std::exception If timer thread is not started.
1708 * \throw std::exception If \a timer is already activated.
1709 *
1710 * \tparam Duration_1 actual type which represents time duration.
1711 * \tparam Duration_2 actual type which represents time duration.
1712 */
1713 template< class Duration_1, class Duration_2 >
1714 bool
1716 //! Timer to be activated.
1717 timer_object_holder< Thread_Safety > timer,
1718 //! Pause for timer execution.
1719 Duration_1 pause,
1720 //! Repetition period.
1721 //! If <tt>Duration_2::zero() == period</tt> then timer will be
1722 //! single-shot.
1723 Duration_2 period,
1724 //! Action for the timer.
1725 timer_action action )
1726 {
1727 auto list_timer = timer.template cast_to< timer_type >();
1729
1730 // Timer object must be correctly (re)initialized.
1735
1736 // Timer must be taken under control.
1738 // It is an active timer now.
1740
1742 // Count of timers in the list changed.
1743 this->inc_timer_count( list_timer->kind() );
1744
1745 return list_timer == m_head;
1746 }
1747
1748 /*!
1749 * \brief Perform an attempt to reschedule a timer.
1750 *
1751 * Before v.1.2.1 there was just one way to reschedule a timer:
1752 * method deactivate() must be called first and then method
1753 * activate() must be called for the same timer. This approach is
1754 * not fast because in the case of thread-safe engines it requires
1755 * two operations on a mutex.
1756 *
1757 * Since v.1.2.1 there is a reschedule() method which does deactivation
1758 * of a timer (if it is active) and then new activation of this timer.
1759 * All actions are performed by using just one operation on a mutex.
1760 *
1761 * \note
1762 * This operation can fail if the timer to be rescheduled is in processing.
1763 * Because of that it is recommended to use such operation for
1764 * timer_managers only. But even with timer_managers this operation
1765 * should be used with care.
1766 *
1767 * \attention
1768 * It move operator for a timer_action throws then timer will be
1769 * deactivated. The state for a timer_action itself will be unknown.
1770 *
1771 * \throw std::exception If timer thread is not started.
1772 * \throw std::exception If \a timer is in processing right now.
1773 *
1774 * \tparam Duration_1 actual type which represents time duration.
1775 * \tparam Duration_2 actual type which represents time duration.
1776 *
1777 * \since
1778 * v.1.2.1
1779 */
1780 template< class Duration_1, class Duration_2 >
1781 bool
1783 //! Timer to be rescheduled.
1784 timer_object_holder< Thread_Safety > timer,
1785 //! Pause for timer execution.
1786 Duration_1 pause,
1787 //! Repetition period.
1788 //! If <tt>Duration_2::zero() == period</tt> then timer will be
1789 //! single-shot.
1790 Duration_2 period,
1791 //! Action for the timer.
1792 timer_action action )
1793 {
1794 auto list_timer = timer.template cast_to< timer_type >();
1795 // If timer is deactivated the usual activation logic can be used.
1797 return this->activate(
1799 else if( timer_status::active != list_timer->m_status )
1800 {
1801 // Timer which is in processing now can't be reactivated.
1802 throw std::runtime_error( "timer is in processing now, "
1803 "it can't be rescheduled" );
1804 }
1805
1806 // Timer must be removed from the list first.
1808 this->dec_timer_count( list_timer->kind() );
1809
1810 // Timer object must be correctly (re)initialized.
1811 // If this assigment throws then we must deactivate the timer.
1812 try
1813 {
1815 }
1816 catch(...)
1817 {
1820 // Exception must be rethrown;
1821 throw;
1822 }
1826
1827 // Updated timer must be placed into the list.
1829 // Count of timers in the list changed.
1830 this->inc_timer_count( list_timer->kind() );
1831
1832 return list_timer == m_head;
1833 }
1834
1835 //! Deactivate timer and remove it from the list.
1836 void
1838 //! Timer to be deactivated.
1839 timer_object_holder< Thread_Safety > timer )
1840 {
1841 auto list_timer = timer.template cast_to< timer_type >();
1843 {
1844 // This is normal active timer. It can be safely
1845 // deactivated and destroyed.
1847 // Count of timers in the list changed.
1848 this->dec_timer_count( list_timer->kind() );
1849
1851
1852 // Release timer object.
1854 }
1856 {
1857 // This timer is in execution list right now.
1858 // We can only change its status.
1859 // Final deactivation will be done after execution of
1860 // timers actions.
1862 }
1863 }
1864
1865 /*!
1866 * \brief Build sublist of elapsed timers and process them all.
1867 *
1868 * Object is unlocked and then locked back.
1869 */
1870 template< typename Unique_Lock >
1871 void
1873 //! Object's lock.
1874 Unique_Lock & lock )
1875 {
1877
1878 if( exec_list_head )
1879 {
1881
1883 }
1884 }
1885
1886 /*!
1887 * \brief Is empty timer list?
1888 *
1889 * \return true if there is no more timers.
1890 */
1891 bool
1892 empty() const
1893 {
1894 return nullptr == m_head;
1895 }
1896
1897 /*!
1898 * \brief Get time point of the next timer.
1899 *
1900 * \attention Must be called only when \a !empty().
1901 */
1904 {
1905 return m_head->m_when;
1906 }
1907
1908 /*!
1909 * \brief Deactivate all timers and cleanup internal data structures.
1910 */
1911 void
1913 {
1914 while( m_head )
1915 {
1916 auto t = m_head;
1917 m_head = m_head->m_next;
1918
1921 }
1922
1923 // There are no more timers in the list.
1924 this->reset_timer_count();
1925 m_tail = nullptr;
1926 }
1927
1928private :
1929 //! Type of list timer.
1930 struct timer_type : public timer_object< Thread_Safety >
1931 {
1932 //! Status of the timer.
1934
1935 //! Time of execution for this timer.
1937
1938 //! Period in ticks.
1939 /*!
1940 * Zero means that demand is single shot.
1941 */
1943
1944 //! Timer action.
1946
1947 //! Previous demand in the list.
1948 timer_type * m_prev = nullptr;
1949 //! Next demand in the list.
1950 timer_type * m_next = nullptr;
1951
1956
1957 /*!
1958 * \brief Detect type of timer (single-shot or periodic).
1959 *
1960 * \since
1961 * v.1.1.1
1962 */
1964 kind() const
1965 {
1966 return monotonic_clock::duration::zero() == m_period ?
1968 }
1969 };
1970
1971 /*!
1972 * \name Object's attributes.
1973 * \{
1974 */
1975 //! Head of the list of timers.
1976 timer_type * m_head = nullptr;
1977
1978 //! Tail of the list of timers.
1979 timer_type * m_tail = nullptr;
1980 /*!
1981 * \}
1982 */
1983
1984 /*!
1985 * \brief Hard check for deactivation state of the timer.
1986 *
1987 * \throw std::runtimer_error if timer is not deactivated.
1988 */
1989 static void
1991 {
1993 throw std::runtime_error( "timer is not in 'deactivated' state" );
1994 }
1995
1996 //! Insert timer to the list.
1997 /*!
1998 * Insertion starts from the tail of the list. And if \a timer
1999 * has lower timer_type::m_whan value then the last list item
2000 * there is an loop of searching appropriate place by going to
2001 * the head of the list.
2002 *
2003 * Doesn't increment reference count for \a timer.
2004 */
2005 void
2007 //! Timer to be inserted.
2008 timer_type * timer )
2009 {
2011 while( point )
2012 {
2013 if( point->m_when > timer->m_when )
2014 point = point->m_prev;
2015 else
2016 {
2017 // This is a point to insertion (new timer must be
2018 // next to 'point' item).
2020
2021 if( point->m_next )
2023 point->m_next = timer;
2024
2025 timer->m_prev = point;
2026
2027 if( point == m_tail )
2028 // timer must become a new tail for the list.
2029 m_tail = timer;
2030
2031 return;
2032 }
2033 }
2034
2035 // Timer must go to the head of the list.
2036 timer->m_prev = nullptr;
2037 timer->m_next = m_head;
2038 if( m_head )
2039 m_head->m_prev = timer;
2040 m_head = timer;
2041
2042 if( !m_tail )
2043 // List was empty. So there must be new tail of the list.
2044 m_tail = timer;
2045 }
2046
2047 //! Remove the timer from the list.
2048 /*!
2049 * Doesn't decrement reference count for \a timer.
2050 */
2051 void
2053 timer_type * timer )
2054 {
2055 if( timer->m_prev )
2057 else
2058 m_head = timer->m_next;
2059
2060 if( timer->m_next )
2062 else
2063 m_tail = timer->m_prev;
2064 }
2065
2066 /*!
2067 * \brief Build sublist of elapsed timers.
2068 *
2069 * All timers in the sublist receive timer_status::wait_for_execution
2070 * status.
2071 */
2072 timer_type *
2074 {
2075 // If there is no timer return empty list immidiately.
2076 if( !m_head )
2077 return nullptr;
2078
2079 auto tail = m_head;
2080
2081 const auto now = monotonic_clock::now();
2082
2083 // Search the first not-elapsed-yet timer.
2084 while( tail && now >= tail->m_when )
2085 {
2087 tail = tail->m_next;
2088 }
2089
2090 if( tail == m_head )
2091 // There is no elapsed timers.
2092 return nullptr;
2093
2094 auto exec_list_head = m_head;
2095 if( tail )
2096 {
2097 // This item must be the new head of the list.
2098 m_head = tail;
2099 tail->m_prev->m_next = nullptr;
2100 tail->m_prev = nullptr;
2101 }
2102 else
2103 {
2104 // Whole timer list is the execution list.
2105 m_head = m_tail = nullptr;
2106 }
2107
2108 return exec_list_head;
2109 }
2110
2111 /*!
2112 * \brief Execute all active timers in the sublist.
2113 *
2114 * Object is unlocked and locked back after sublist processing.
2115 */
2116 template< class Unique_Lock >
2117 void
2119 //! Object lock.
2120 //! This lock will be unlocked before execution of actions
2121 //! and locked back after.
2122 Unique_Lock & lock,
2123 //! Head of execution list.
2124 //! Cannot be nullptr.
2126 {
2127 lock.unlock();
2128
2129 while( head )
2130 {
2131 try
2132 {
2133 // Status of timer can be changed. So it must be checked
2134 // just before execution. If timer is waiting for
2135 // deregistration it must not be executed.
2137 head->m_action.exec();
2138 }
2139 catch( const std::exception & x )
2140 {
2141 // Note this invoke_noexcept_code_block() is not needed
2142 // if compiler supports noexcept.
2143 invoke_noexcept_code_block( [this, &x] {
2144 this->m_exception_handler( x );
2145 } );
2146 }
2147 catch( ... )
2148 {
2149 // Logging should not throw exceptions.
2152 ss << __FILE__ << "(" << __LINE__
2153 << "): an unknown exception from timer action";
2154 this->m_error_logger( ss.str() );
2155 } );
2156
2157 std::abort();
2158 }
2159
2160 head = head->m_next;
2161 }
2162
2163 lock.lock();
2164 }
2165
2166 /*!
2167 * \brief Process list of elapsed timers after execution of
2168 * its actions.
2169 *
2170 * Active periodic timers will be rescheduled. All other timers
2171 * will be deactivated and removed.
2172 */
2173 void
2175 //! Head of execution list.
2176 //! Cannot be null.
2177 timer_type * head )
2178 {
2179 while( head )
2180 {
2181 auto t = head;
2182 head = head->m_next;
2183
2184 // Actual periodic timer must be rescheduled.
2187 {
2188 t->m_when += t->m_period;
2190
2192 }
2193 else
2194 {
2195 // Timer must be utilized.
2196 this->dec_timer_count( t->kind() );
2199 }
2200 }
2201 }
2202};
2203
2204//
2205// timer_heap_engine_defaults
2206//
2207/*!
2208 * \brief Container for static method with default values for
2209 * timer_heap engine.
2210 *
2211 * \since
2212 * v.1.1.0
2213 */
2215{
2216 //! Default initial capacity of heap-array.
2217 inline static std::size_t
2219};
2220
2221//
2222// timer_heap_engine
2223//
2224
2225/*!
2226 * \brief An engine for timer heap mechanism.
2227 *
2228 * This timer engine uses timer mechanism based on
2229 * <a href="http://en.wikipedia.org/wiki/Heap_%28data_structure%29">heap data structure</a>.
2230 * The timer with the earlier time point is on the top of
2231 * the heap. When this timer elapsed and removed next timer with the
2232 * eralier time point is going to the top of the heap.
2233 *
2234 * This implementation uses array-based <a
2235 * href="http://en.wikipedia.org/wiki/Binary_heap">binary heap</a>. The array
2236 * is growing as necessary to hold all the timers. The initial size of that
2237 * array can be specified in the constructor.
2238 *
2239 * \note Unlike timer_wheel and timer_list threads this thread unlock and
2240 * lock its mutex for processing every timers. It means that processing
2241 * speed of this thread will be slower then for timer_wheel or
2242 * timer_list threads. But this type of thread doesn't consume resources
2243 * when there is no timers (unlike timer_wheel thread). And has very
2244 * efficient activation and deactivation procedures (unlike timer_list
2245 * thread).
2246 *
2247 * \tparam Thread_Safety Thread-safety indicator.
2248 * Must be timertt::thread_safety::unsafe or timertt::thread_safety::safe.
2249 *
2250 * \tparam Timer_Action type of functor to perform an user-defined
2251 * action when timer expires. This must be Moveable and MoveConstructible
2252 * type.
2253 *
2254 * \tparam Error_Logger type of logger for errors detected during
2255 * timer thread execution. Interface for error logger is defined
2256 * by default_error_logger class.
2257 *
2258 * \tparam Actor_Exception_Handler type of handler for dealing with
2259 * exceptions thrown from timer actors. Interface for exception handler
2260 * is defined by default_actor_exception_handler.
2261 */
2262template<
2263 typename Thread_Safety,
2264 typename Timer_Action,
2265 typename Error_Logger,
2266 typename Actor_Exception_Handler >
2268 : public engine_common<
2269 Thread_Safety, Timer_Action, Error_Logger, Actor_Exception_Handler >
2270{
2271 //! An alias for base class.
2272 using base_type = engine_common<
2273 Thread_Safety, Timer_Action, Error_Logger, Actor_Exception_Handler >;
2274
2275 // Forward declaration.
2276 struct timer_type;
2277
2278public :
2279 //! Type with default parameters for this engine.
2281
2282 //! Alias for timer_action type.
2283 using timer_action = typename base_type::timer_action;
2284
2285 //! Alias for scoped timer object.
2286 using scoped_timer_object =
2288
2289 //! Constructor with all parameters.
2291 //! An initial size for heap array.
2292 std::size_t initial_heap_capacity,
2293 //! An error logger for timer thread.
2294 Error_Logger error_logger,
2295 //! An actor exception handler for timer thread.
2296 Actor_Exception_Handler exception_handler )
2298 {
2300 }
2301
2303 {
2304 clear_all();
2305 }
2306
2307 //! Create timer to be activated later.
2308 timer_object_holder< Thread_Safety >
2310 {
2311 return timer_object_holder< Thread_Safety >( new timer_type() );
2312 }
2313
2314 //! Activate timer and schedule it for execution.
2315 /*!
2316 * \return true is new timer is a timer on the top of the heap
2317 * (has earlier expiration time).
2318 *
2319 * \throw std::exception If timer thread is not started.
2320 * \throw std::exception If \a timer is already activated.
2321 *
2322 * \tparam Duration_1 actual type which represents time duration.
2323 * \tparam Duration_2 actual type which represents time duration.
2324 */
2325 template< class Duration_1, class Duration_2 >
2326 bool
2328 //! Timer to be activated.
2329 timer_object_holder< Thread_Safety > timer,
2330 //! Pause for timer execution.
2331 Duration_1 pause,
2332 //! Repetition period.
2333 //! If <tt>Duration_2::zero() == period</tt> then timer will be
2334 //! single-shot.
2335 Duration_2 period,
2336 //! Action for the timer.
2337 timer_action action )
2338 {
2339 auto heap_timer = timer.template cast_to< timer_type >();
2341
2342 // Timer object must be correctly (re)initialized.
2347
2348 // Timer must be taken under control.
2350
2351 // Timer will be marked as active during insertion into
2352 // heap structure.
2354
2355 // Count of timers must be incremented.
2356 this->inc_timer_count( heap_timer->kind() );
2357
2358 return heap_timer == heap_head();
2359 }
2360
2361 /*!
2362 * \brief Perform an attempt to reschedule a timer.
2363 *
2364 * Before v.1.2.1 there was just one way to reschedule a timer:
2365 * method deactivate() must be called first and then method
2366 * activate() must be called for the same timer. This approach is
2367 * not fast because in the case of thread-safe engines it requires
2368 * two operations on a mutex.
2369 *
2370 * Since v.1.2.1 there is a reschedule() method which does deactivation
2371 * of a timer (if it is active) and then new activation of this timer.
2372 * All actions are performed by using just one operation on a mutex.
2373 *
2374 * \note
2375 * This operation can fail if the timer to be rescheduled is in processing.
2376 * Because of that it is recommended to use such operation for
2377 * timer_managers only. But even with timer_managers this operation
2378 * should be used with care.
2379 *
2380 * \attention
2381 * It move operator for a timer_action throws then timer will be
2382 * deactivated. The state for a timer_action itself will be unknown.
2383 *
2384 * \throw std::exception If timer thread is not started.
2385 * \throw std::exception If \a timer is in processing right now.
2386 *
2387 * \tparam Duration_1 actual type which represents time duration.
2388 * \tparam Duration_2 actual type which represents time duration.
2389 *
2390 * \since
2391 * v.1.2.1
2392 */
2393 template< class Duration_1, class Duration_2 >
2394 bool
2396 //! Timer to be rescheduled.
2397 timer_object_holder< Thread_Safety > timer,
2398 //! Pause for timer execution.
2399 Duration_1 pause,
2400 //! Repetition period.
2401 //! If <tt>Duration_2::zero() == period</tt> then timer will be
2402 //! single-shot.
2403 Duration_2 period,
2404 //! Action for the timer.
2405 timer_action action )
2406 {
2407 auto heap_timer = timer.template cast_to< timer_type >();
2408 // If timer is deactivated the usual activation logic can be used.
2409 if( heap_timer->deactivated() )
2410 return this->activate(
2412 else if( heap_timer == m_timer_in_processing )
2413 {
2414 // Timer which is in processing now can't be reactivated.
2415 throw std::runtime_error( "timer is in processing now, "
2416 "it can't be rescheduled" );
2417 }
2418
2419 // Timer must be removed from heap first.
2421 // Count of timers changed.
2422 this->dec_timer_count( heap_timer->kind() );
2423
2424 // Timer object must be correctly (re)initialized.
2425 // If this assigment throws then we must deactivate the timer.
2426 try
2427 {
2429 }
2430 catch(...)
2431 {
2434 // Exception must be rethrown;
2435 throw;
2436 }
2437
2441
2442 // Timer will be marked as active during insertion into
2443 // heap structure.
2445
2446 // Count of timers must be incremented.
2447 this->inc_timer_count( heap_timer->kind() );
2448
2449 return heap_timer == heap_head();
2450 }
2451
2452 //! Deactivate timer and remove it from the list.
2453 void
2455 //! Timer to be deactivated.
2456 timer_object_holder< Thread_Safety > timer )
2457 {
2458 auto heap_timer = timer.template cast_to< timer_type >();
2459 if( !heap_timer->deactivated() )
2460 {
2461 // If this timer is not in processing now it can
2462 // be safely destroyed.
2464 {
2466
2467 // Count of timers changed.
2468 this->dec_timer_count( heap_timer->kind() );
2469
2470 // We can deactivate timer only after removing.
2471 // Because deactivation drops actual timer position.
2473
2474 // Release timer object.
2476 }
2477 else
2478 {
2479 // Otherwise m_timer_in_processing will be destroyed
2480 // after end of timer action processing.
2481 // But it must be deactivated right now.
2483 }
2484 }
2485 }
2486
2487 /*!
2488 * \brief Process all expired timers from the heap.
2489 *
2490 * \note time_point for timers expiration detection is got
2491 * only once at the begining on the method.
2492 *
2493 * \note \a lock unlocked and then locked back for every
2494 * timer action execution.
2495 */
2496 template< typename Unique_Lock >
2497 void
2499 //! Object's lock.
2500 Unique_Lock & lock )
2501 {
2502 // Process timers in loop until there are elapsed timers.
2503 const auto now = monotonic_clock::now();
2504 while( !heap_empty() && now > heap_head()->m_when )
2505 {
2508
2510
2511 // If timer has become deactive it must be removed even
2512 // it is periodic timer.
2515 {
2516 // Count of timers changed.
2518
2522 }
2523 else
2524 {
2525 // This is active periodic timer and it must be resheduled.
2529 }
2530
2531 m_timer_in_processing = nullptr;
2532 }
2533 }
2534
2535 /*!
2536 * \brief Is empty timer list?
2537 *
2538 * \return true if there is no more timers.
2539 */
2540 bool
2541 empty() const
2542 {
2543 return heap_empty();
2544 }
2545
2546 /*!
2547 * \brief Get time point of the next timer.
2548 *
2549 * \attention Must be called only when \a !empty().
2550 */
2553 {
2554 return heap_head()->m_when;
2555 }
2556
2557 //! Clear all timer demands.
2558 void
2560 {
2561 for( auto t : m_heap )
2562 {
2563 t->deactivate();
2565 }
2566
2567 this->reset_timer_count();
2568 m_heap.clear();
2569 }
2570
2571private :
2572 //! Type of heap timer.
2573 struct timer_type : public timer_object< Thread_Safety >
2574 {
2575 //! A special value which means that timer is deactivated.
2576 /*!
2577 * This value is illegal index in heap-array because
2578 * position numbers in heap-array are started from 1, not from 0.
2579 */
2581
2582 //! Time of execution for this timer.
2584
2585 //! Period in ticks.
2586 /*!
2587 * Zero means that demand is single shot.
2588 */
2590
2591 //! Timer action.
2593
2594 //! Position in the heap-array.
2596
2597 //! Is timer deactivated.
2598 bool
2600 {
2602 }
2603
2604 //! Set deactivation indicator on.
2605 void
2610
2611 //! Is this is single shot timer?
2612 bool
2614 {
2615 return monotonic_clock::duration::zero() == m_period;
2616 }
2617
2618 /*!
2619 * \brief Detect type of timer (single-shot or periodic).
2620 *
2621 * \since
2622 * v.1.1.1
2623 */
2625 kind() const
2626 {
2627 return single_shot() ?
2629 }
2630 };
2631
2632 /*!
2633 * \name Object's attributes.
2634 * \{
2635 */
2636 //! Array for holding heap data structure.
2638
2639 //! Timer which is currently in processing.
2641 /*!
2642 * \}
2643 */
2644
2645 /*!
2646 * \brief Hard check for deactivation state of the timer.
2647 *
2648 * \throw std::runtimer_error if timer is not deactivated.
2649 */
2650 static void
2652 {
2653 if( !timer->deactivated() )
2654 throw std::runtime_error( "timer is not in 'deactivated' state" );
2655 }
2656
2657 //! Execute the current timer.
2658 template< class Unique_Lock >
2659 void
2661 //! Object lock.
2662 //! This lock will be unlocked before execution of actions
2663 //! and locked back after.
2664 Unique_Lock & lock ) TIMERTT_NOEXCEPT
2665 {
2666 lock.unlock();
2667
2668 try
2669 {
2671 }
2672 catch( const std::exception & x )
2673 {
2674 // Note this invoke_noexcept_code_block() is not needed
2675 // if compiler supports noexcept.
2676 invoke_noexcept_code_block( [this, &x] {
2677 this->m_exception_handler( x );
2678 } );
2679 }
2680 catch( ... )
2681 {
2682 // Logging should not throw exceptions.
2685 ss << __FILE__ << "(" << __LINE__
2686 << "): an unknown exception from timer action";
2687 this->m_error_logger( ss.str() );
2688 } );
2689 std::abort();
2690 }
2691
2692 lock.lock();
2693 }
2694
2695 /*!
2696 * \name Methods for work with heap data structure.
2697 * \{
2698 */
2699 //! Is heap data structure empty?
2700 bool
2702 {
2703 return m_heap.empty();
2704 }
2705
2706 //! Get the minimal timer.
2707 /*!
2708 * \attention This method must be called only on non-empty heap.
2709 */
2710 timer_type *
2712 {
2713 return m_heap.front();
2714 }
2715
2716 //! Add new timer to the heap data structure.
2717 void
2719 {
2720 timer->m_position = m_heap.size() + 1;
2722
2723 while( 1 != timer->m_position )
2724 {
2725 auto parent = heap_item( timer->m_position / 2 );
2726 if( parent->m_when > timer->m_when )
2727 {
2728 // timer must be heap-up on the place of the parent node.
2730 }
2731 else
2732 // There is no need to modify heap structure anymore.
2733 break;
2734 }
2735 }
2736
2737 //! Remove timer from the heap data structure.
2738 void
2740 {
2741 if( timer->m_position == m_heap.size() )
2742 // A special case: timer to remove is a last added item
2743 // in the heap. It could be simply removed from heap
2744 // without any other actions.
2745 m_heap.pop_back();
2746 else
2747 {
2748 auto last_item = m_heap.back();
2750 m_heap.pop_back();
2751
2752 // last_item must be heap-down to the appropriate place.
2753 while( true )
2754 {
2755 auto left_index = last_item->m_position * 2;
2756 auto right_index = left_index + 1;
2758
2759 if( left_index <= m_heap.size() &&
2763
2764 if( right_index <= m_heap.size() &&
2768
2771 else
2772 // Heap structure is correct.
2773 break;
2774 }
2775 }
2776 }
2777
2778 //! Swap two heap nodes.
2779 void
2781 {
2782 m_heap[ a->m_position - 1 ] = b;
2783 m_heap[ b->m_position - 1 ] = a;
2784
2786 }
2787
2788 //! Get timer by it index.
2789 /*!
2790 * This accessor work with respect that positions are started from 1.
2791 */
2792 timer_type *
2793 heap_item( std::size_t position ) const
2794 {
2795 return m_heap[ position - 1 ];
2796 }
2797 /*!
2798 * \}
2799 */
2800};
2801
2802//
2803// thread_unsafe_manager_mixin
2804//
2805
2806/*!
2807 * \brief A mixin which must be used as base class for not-thread-safe
2808 * timer managers.
2809 *
2810 * \since
2811 * v.1.1.0
2812 */
2814{
2815 //! A empty class for an object's lock emulation.
2816 /*!
2817 * Instance of that class will be used in not-thread-safe
2818 * code in places where object's lock is necessary.
2819 *
2820 * Because this class is empty its usage will be removed by
2821 * optimized compiler.
2822 */
2824 {
2825 public :
2827
2828 void lock() {}
2829 void unlock() {}
2830 };
2831
2832 void
2834
2835 void
2837};
2838
2839//
2840// thread_safe_manager_mixin
2841//
2842/*!
2843 * \brief A mixin which must be used as base class for thread-safe
2844 * timer managers.
2845 *
2846 * \since
2847 * v.1.1.0
2848 */
2850{
2851 //! Timer manager's lock.
2853
2854 //! A special wrapper around actual std::unique_lock.
2856 {
2858
2859 public :
2863
2864 void lock() { m_lock.lock(); }
2865 void unlock() { m_lock.unlock(); }
2866 };
2867
2868 void
2870
2871 void
2873};
2874
2875//
2876// thread_mixin
2877//
2878
2879/*!
2880 * \brief A mixin which must be used as base class for timer threads.
2881 *
2882 * \since
2883 * v.1.1.0
2884 */
2886{
2887 //! Timer thread's lock.
2889
2890 //! Condition variable for waiting for next event.
2892
2893 //! Underlying thread object.
2894 /*!
2895 * \note Will be created during timer thread start and
2896 * destroyed after timer thread shutdown.
2897 */
2899
2900 //! A special wrapper around actual std::unique_lock.
2902 {
2904
2905 public :
2907 : m_lock( self.m_lock )
2908 {}
2909
2910 void lock() { m_lock.lock(); }
2911 void unlock() { m_lock.unlock(); }
2912
2913 //! Accessor for actual std::unique_lock object.
2914 std::unique_lock< std::mutex > &
2915 actual_lock() { return m_lock; }
2916 };
2917
2918 //! Checks that timer thread is really started.
2919 /*!
2920 * \throw std::exception if thread is not started.
2921 */
2922 void
2924 {
2925 if( !m_thread )
2926 throw std::runtime_error( "timer thread is not started" );
2927 }
2928
2929 //! Sends notification to timer thread.
2930 void
2932 {
2933 m_condition.notify_one();
2934 }
2935};
2936
2937/*!
2938 * \brief A type-container for types of engine-consumers.
2939 *
2940 * \since
2941 * v.1.1.0
2942 */
2944{
2945 /*!
2946 * \brief Indicator that an engine will be owned by timer manager.
2947 *
2948 * \since
2949 * v.1.1.0
2950 */
2951 struct manager {};
2952 /*!
2953 * \brief Indicator that an engine will be owned by timer thread.
2954 *
2955 * \since
2956 * v.1.1.0
2957 */
2958 struct thread {};
2959};
2960
2961/*!
2962 * \brief A selector of actual mixin type for timer manager or timer thread.
2963 *
2964 * \attention This type is empty because all actual content will be
2965 * defined in specializations.
2966 *
2967 * \since
2968 * v.1.1.0
2969 */
2970template< typename Thread_Safety, typename Consumer >
2972{
2973};
2974
2975/*!
2976 * \brief A selector of actual mixin type for not-thread-safe timer manager.
2977 *
2978 * \since
2979 * v.1.1.0
2980 */
2981template<>
2983{
2984 //! Actual type of the mixin.
2986};
2987
2988/*!
2989 * \brief A selector of actual mixin type for thread-safe timer manager.
2990 *
2991 * \since
2992 * v.1.1.0
2993 */
2994template<>
2996{
2997 //! Actual type of the mixin.
2999};
3000
3001/*!
3002 * \brief A selector of actual mixin type for timer thread.
3003 *
3004 * \since
3005 * v.1.1.0
3006 */
3007template<>
3009{
3010 //! Actual type of the mixin.
3011 using type = thread_mixin;
3012};
3013
3014//
3015// basic_methods_impl_mixin
3016//
3017
3018/*!
3019 * \brief A implementation of basic methods for timer managers and
3020 * timer threads.
3021 *
3022 * \tparam Engine actual type of engine to be used.
3023 * \tparam Consumer type of engine consumer (e.g. consumer_type::manager or
3024 * consumer_type::thread).
3025 *
3026 * \since
3027 * v.1.1.0
3028 */
3029template<
3030 typename Engine,
3031 typename Consumer >
3033 : protected mixin_selector< typename Engine::thread_safety, Consumer >::type
3034 , public Engine::defaults_type
3035{
3036 //! Shorthand for actual mixin type.
3037 using mixin_type = typename mixin_selector<
3038 typename Engine::thread_safety, Consumer >::type;
3039
3040public :
3041 /*!
3042 * \brief A typedef for thread safety type from Engine.
3043 *
3044 * \since
3045 * v.1.1.2
3046 */
3047 using thread_safety = typename Engine::thread_safety;
3048
3049 //! An alias for timer_action type.
3050 using timer_action = typename Engine::timer_action;
3051
3052 //! An alias for scoped timer objects.
3053 using scoped_timer_object = typename Engine::scoped_timer_object;
3054
3055 //! Shorthand for timer objects' smart pointer.
3056 /*!
3057 * \note
3058 * Since v.1.2.1 it is a public type name.
3059 */
3060 using timer_holder = timer_object_holder< typename Engine::thread_safety >;
3061
3062 //! Constructor with all parameters.
3063 template< typename... Args >
3065 Args && ... args )
3066 : m_engine( std::forward< Args >(args)... )
3067 {
3068 }
3069
3070 /*!
3071 * \brief Allocate of new timer object.
3072 *
3073 * \note This object must be activated by activate() methods.
3074 */
3075 timer_holder
3077 {
3078 return m_engine.allocate();
3079 }
3080
3081 //! Activate timer and schedule it for execution.
3082 /*!
3083 *
3084 * \throw std::exception If timer thread is not started.
3085 * \throw std::exception If \a timer is already activated.
3086 *
3087 * \tparam Duration_1 actual type which represents time duration.
3088 */
3089 template< class Duration_1 >
3090 void
3092 //! Timer to be activated.
3093 timer_holder timer,
3094 //! Pause for timer execution.
3095 Duration_1 pause,
3096 //! Action for the timer.
3097 timer_action action )
3098 {
3099 activate(
3100 std::move( timer ),
3101 pause,
3103 std::move( action ) );
3104 }
3105
3106 /*!
3107 * \brief Perform an attempt to reschedule a timer.
3108 *
3109 * Before v.1.2.1 there was just one way to reschedule a timer:
3110 * method deactivate() must be called first and then method
3111 * activate() must be called for the same timer. This approach is
3112 * not fast because in the case of thread-safe engines it requires
3113 * two operations on a mutex.
3114 *
3115 * Since v.1.2.1 there is a reschedule() method which does deactivation
3116 * of a timer (if it is active) and then new activation of this timer.
3117 * All actions are performed by using just one operation on a mutex.
3118 *
3119 * \note
3120 * This operation can fail if the timer to be rescheduled is in processing.
3121 * Because of that it is recommended to use such operation for
3122 * timer_managers only. But even with timer_managers this operation
3123 * should be used with care.
3124 *
3125 * \attention
3126 * It move operator for a timer_action throws then timer will be
3127 * deactivated. The state for a timer_action itself will be unknown.
3128 *
3129 * \throw std::exception If timer thread is not started.
3130 * \throw std::exception If \a timer is in processing right now.
3131 *
3132 * \tparam Duration_1 actual type which represents time duration.
3133 *
3134 * \since
3135 * v.1.2.1
3136 */
3137 template< class Duration_1 >
3138 void
3140 //! Timer to be rescheduled.
3141 timer_holder timer,
3142 //! Pause for timer execution.
3143 Duration_1 pause,
3144 //! Action for the timer.
3145 timer_action action )
3146 {
3147 reschedule(
3148 std::move( timer ),
3149 pause,
3151 std::move( action ) );
3152 }
3153
3154 //! Activate a scoped timer and schedule it for execution.
3155 /*!
3156 *
3157 * \note
3158 * A proper lifetime of this timer must be controlled by user.
3159 *
3160 * \throw std::exception If timer thread is not started.
3161 * \throw std::exception If \a timer is already activated.
3162 *
3163 * \tparam Duration_1 actual type which represents time duration.
3164 *
3165 * \since
3166 * v.1.2.0
3167 */
3168 template< class Duration_1 >
3169 void
3171 //! Timer to be activated.
3172 scoped_timer_object & timer,
3173 //! Pause for timer execution.
3174 Duration_1 pause,
3175 //! Action for the timer.
3176 timer_action action )
3177 {
3179 }
3180
3181 //! Activate timer and schedule it for execution.
3182 /*!
3183 * There is no need to preallocate timer object. It will
3184 * be allocated automatically, but not be shown to user.
3185 *
3186 * \throw std::exception If timer thread is not started.
3187 *
3188 * \tparam Duration_1 actual type which represents time duration.
3189 */
3190 template< class Duration_1 >
3191 void
3193 //! Pause for timer execution.
3194 Duration_1 pause,
3195 //! Action for the timer.
3196 timer_action action )
3197 {
3198 activate(
3199 allocate(),
3200 pause,
3202 std::move( action ) );
3203 }
3204
3205 //! Activate timer and schedule it for execution.
3206 /*!
3207 *
3208 * \throw std::exception If timer thread is not started.
3209 * \throw std::exception If \a timer is already activated.
3210 *
3211 * \tparam Duration_1 actual type which represents time duration.
3212 * \tparam Duration_2 actual type which represents time duration.
3213 */
3214 template< class Duration_1, class Duration_2 >
3215 void
3217 //! Timer to be activated.
3218 timer_holder timer,
3219 //! Pause for timer execution.
3220 Duration_1 pause,
3221 //! Repetition period.
3222 //! If <tt>Duration_2::zero() == period</tt> then timer will be
3223 //! single-shot.
3224 Duration_2 period,
3225 //! Action for the timer.
3226 timer_action action )
3227 {
3228 typename mixin_type::lock_guard locker{ *this };
3229
3230 this->ensure_started();
3231
3232 if( m_engine.activate(
3233 std::move( timer ), pause, period, std::move( action ) ) )
3234 this->notify();
3235 }
3236
3237 /*!
3238 * \brief Perform an attempt to reschedule a timer.
3239 *
3240 * Before v.1.2.1 there was just one way to reschedule a timer:
3241 * method deactivate() must be called first and then method
3242 * activate() must be called for the same timer. This approach is
3243 * not fast because in the case of thread-safe engines it requires
3244 * two operations on a mutex.
3245 *
3246 * Since v.1.2.1 there is a reschedule() method which does deactivation
3247 * of a timer (if it is active) and then new activation of this timer.
3248 * All actions are performed by using just one operation on a mutex.
3249 *
3250 * \note
3251 * This operation can fail if the timer to be rescheduled is in processing.
3252 * Because of that it is recommended to use such operation for
3253 * timer_managers only. But even with timer_managers this operation
3254 * should be used with care.
3255 *
3256 * \attention
3257 * It move operator for a timer_action throws then timer will be
3258 * deactivated. The state for a timer_action itself will be unknown.
3259 *
3260 * \throw std::exception If timer thread is not started.
3261 * \throw std::exception If \a timer is in processing right now.
3262 *
3263 * \tparam Duration_1 actual type which represents time duration.
3264 * \tparam Duration_2 actual type which represents time duration.
3265 *
3266 * \since
3267 * v.1.2.1
3268 */
3269 template< class Duration_1, class Duration_2 >
3270 void
3272 //! Timer to be activated.
3273 timer_holder timer,
3274 //! Pause for timer execution.
3275 Duration_1 pause,
3276 //! Repetition period.
3277 //! If <tt>Duration_2::zero() == period</tt> then timer will be
3278 //! single-shot.
3279 Duration_2 period,
3280 //! Action for the timer.
3281 timer_action action )
3282 {
3283 typename mixin_type::lock_guard locker{ *this };
3284
3285 this->ensure_started();
3286
3287 if( m_engine.reschedule(
3288 std::move( timer ), pause, period, std::move( action ) ) )
3289 this->notify();
3290 }
3291
3292 //! Activate a scoped timer and schedule it for execution.
3293 /*!
3294 *
3295 * \note
3296 * A proper lifetime of this timer must be controlled by user.
3297 *
3298 * \throw std::exception If timer thread is not started.
3299 * \throw std::exception If \a timer is already activated.
3300 *
3301 * \tparam Duration_1 actual type which represents time duration.
3302 * \tparam Duration_2 actual type which represents time duration.
3303 *
3304 * \since
3305 * v.1.2.0
3306 */
3307 template< class Duration_1, class Duration_2 >
3308 void
3310 //! Timer to be activated.
3311 scoped_timer_object & timer,
3312 //! Pause for timer execution.
3313 Duration_1 pause,
3314 //! Repetition period.
3315 //! If <tt>Duration_2::zero() == period</tt> then timer will be
3316 //! single-shot.
3317 Duration_2 period,
3318 //! Action for the timer.
3319 timer_action action )
3320 {
3322 }
3323
3324 //! Activate timer and schedule it for execution.
3325 /*!
3326 * There is no need to preallocate timer object. It will
3327 * be allocated automatically, but not be shown to user.
3328 *
3329 * \throw std::exception If timer thread is not started.
3330 *
3331 * \tparam Duration_1 actual type which represents time duration.
3332 * \tparam Duration_2 actual type which represents time duration.
3333 */
3334 template< class Duration_1, class Duration_2 >
3335 void
3337 //! Pause for timer execution.
3338 Duration_1 pause,
3339 //! Repetition period.
3340 //! If <tt>Duration_2::zero() == period</tt> then timer will be
3341 //! single-shot.
3342 Duration_2 period,
3343 //! Action for the timer.
3344 timer_action action )
3345 {
3347 }
3348
3349 //! Deactivate timer and remove it from the list.
3350 void
3352 //! Timer to be deactivated.
3353 timer_holder timer )
3354 {
3355 typename mixin_type::lock_guard locker{ *this };
3356
3358 }
3359
3360 //! Deactivate timer and remove it from the list.
3361 /*!
3362 * \since
3363 * v.1.2.0
3364 */
3365 void
3367 //! Timer to be deactivated.
3368 scoped_timer_object & timer )
3369 {
3370 this->deactivate( timer_holder{timer} );
3371 }
3372
3373 /*!
3374 * \brief Count of timers of various types.
3375 *
3376 * \note This are quantities of all timers known to manager/thread.
3377 * Some of them can be already deactivated but not removed yet.
3378 *
3379 * \since
3380 * v.1.1.1
3381 */
3384 {
3385 typename mixin_type::lock_guard locker{ *this };
3386
3388 }
3389
3390 /*!
3391 * \brief Check for emptiness.
3392 *
3393 * \since
3394 * v.1.1.3
3395 */
3396 bool
3398 {
3399 typename mixin_type::lock_guard locker{ *this };
3400
3401 return m_engine.empty();
3402 }
3403
3404protected :
3405 //! Actual timer engine instance.
3406 Engine m_engine;
3407};
3408
3409//
3410// manager_impl_template
3411//
3412
3413/*!
3414 * \brief Template-based implementation of timer manager.
3415 *
3416 * \tparam Engine actual type of engine to be used.
3417 *
3418 * \since
3419 * v.1.1.0
3420 */
3421template< typename Engine >
3424{
3425 //! Shorthand for base type.
3426 using base_type = basic_methods_impl_mixin<
3427 Engine,
3429
3430public :
3431 //! Constructor with all parameters.
3432 template< typename... Args >
3434 Args && ... args )
3435 : base_type( std::forward< Args >(args)... )
3436 {
3437 }
3438
3439 //! Reset all timers and return manager to the initial state.
3440 void
3442 {
3443 typename manager_impl_template::lock_guard locker{ *this };
3444 this->m_engine.clear_all();
3445 }
3446
3447 //! Perform processing of expired timers.
3448 void
3450 {
3451 typename manager_impl_template::lock_guard locker{ *this };
3452
3454 }
3455
3456 //! Get the time for next process_expired_timers invocation.
3457 /*!
3458 * \return tuple<true,timepoint> if there is a timer to process. Or
3459 * tuple<false,undefined> if there is no timers to be processed.
3460 */
3463 {
3464 typename manager_impl_template::lock_guard locker{ *this };
3465 auto e = this->m_engine.empty();
3466 if( !e )
3467 return std::make_tuple( true, this->m_engine.nearest_time_point() );
3468 else
3469 return std::make_tuple( false, monotonic_clock::time_point() );
3470 }
3471
3472 //! Get the sleeping time before the earlist timer expiration.
3473 /*!
3474 * \return actual sleeping time if there is at least one timer.
3475 * Or \a default_timeout if there is no any timers.
3476 *
3477 * \tparam Duration type for \a default_timeout
3478 */
3479 template< typename Duration >
3482 //! Default timeout value which will be used if there is no any timers.
3484 {
3485 auto r = this->nearest_time_point();
3486 if( std::get<0>( r ) )
3487 {
3488 auto now = monotonic_clock::now();
3489 const auto & f = std::get<1>( r );
3490 if( now > f )
3491 return monotonic_clock::duration( 0 );
3492 else
3493 return (f - now);
3494 }
3495 else
3496 return default_timeout;
3497 }
3498};
3499
3500//
3501// thread_impl_template
3502//
3503
3504/*!
3505 * \brief Template-based implementation of timer thread.
3506 *
3507 * \tparam Engine actual type of engine to be used.
3508 *
3509 * \since
3510 * v.1.1.0
3511 */
3512template< typename Engine >
3514 : public basic_methods_impl_mixin< Engine, consumer_type::thread >
3515{
3516 //! Shorthand for base type.
3517 using base_type = basic_methods_impl_mixin<
3518 Engine,
3520
3521public :
3522 //! Constructor with all parameters.
3523 template< typename... Args >
3525 Args && ... args )
3526 : base_type( std::forward< Args >(args)... )
3527 {
3528 }
3529
3530 //! Destructor.
3531 /*!
3532 * Calls shutdown_and_join()
3533 */
3535 {
3537 }
3538
3539 //! Start timer thread.
3540 /*!
3541 * \throw std::runtime_error if thread is already started.
3542 */
3543 void
3545 {
3546 typename base_type::lock_guard locker{ *this };
3547
3548 if( this->m_thread )
3549 throw std::runtime_error( "timer thread is already started" );
3550 else
3551 this->m_shutdown = false;
3552
3553 this->m_thread = std::make_shared< std::thread >(
3554 std::bind( &thread_impl_template::body, this ) );
3555 }
3556
3557 //! Initiate shutdown for the timer thread without waiting for completion.
3558 void
3560 {
3561 typename base_type::lock_guard locker{ *this };
3562
3563 if( this->m_thread && !this->m_shutdown )
3564 {
3565 this->m_shutdown = true;
3566 this->notify();
3567 }
3568 }
3569
3570 //! Wait for completion of timer thread.
3571 /*!
3572 * Method shutdown() must be called somewhere else.
3573 */
3574 void
3576 {
3577 std::shared_ptr< std::thread > t;
3578 {
3579 typename base_type::lock_guard locker{ *this };
3580 t = this->m_thread;
3581 }
3582 if( t )
3583 {
3584 t->join();
3585
3586 typename base_type::lock_guard locker{ *this };
3587 this->m_thread.reset();
3588 }
3589 }
3590
3591 //! Initiate shutdown and wait for completion.
3592 void
3594 {
3595 shutdown();
3596 join();
3597 }
3598
3599protected :
3600 /*!
3601 * \name Object's attributes.
3602 * \{
3603 */
3604 //! Shutdown flag.
3605 bool m_shutdown = false;
3606 /*!
3607 * \}
3608 */
3609
3610 //! Thread body.
3611 void
3613 {
3614 typename base_type::lock_guard locker{ *this };
3615
3616 while( !this->m_shutdown )
3617 {
3619
3621 }
3622
3623 this->m_engine.clear_all();
3624 }
3625
3626 /*!
3627 * \brief Waiting for next event to process.
3628 *
3629 * If the list is not emply the thread will sleep until
3630 * time point of the first timer in the list.
3631 */
3632 void
3634 //! Object's lock.
3635 //! The lock is necessary for waiting on condition variable.
3636 typename base_type::lock_guard & lock )
3637 {
3638 if( !this->m_shutdown )
3639 {
3640 if( !this->m_engine.empty() )
3641 {
3642 auto time_point = this->m_engine.nearest_time_point();
3644 }
3645 else
3646 this->m_condition.wait( lock.actual_lock() );
3647 }
3648 }
3649};
3650
3651} /* namespace details */
3652
3653//
3654// timer_wheel_thread_template
3655//
3656
3657/*!
3658 * \brief A timer wheel thread template.
3659 *
3660 * Please see description of details::timer_wheel_engine for the details
3661 * of the timer wheel mechanism.
3662 *
3663 * \tparam Timer_Action type of functor to perform an user-defined
3664 * action when timer expires. This must be Moveable and MoveConstructible
3665 * type.
3666 *
3667 * \tparam Error_Logger type of logger for errors detected during
3668 * timer thread execution. Interface for error logger is defined
3669 * by default_error_logger class.
3670 *
3671 * \tparam Actor_Exception_Handler type of handler for dealing with
3672 * exceptions thrown from timer actors. Interface for exception handler
3673 * is defined by default_actor_exception_handler.
3674 */
3675template<
3676 typename Timer_Action,
3677 typename Error_Logger,
3678 typename Actor_Exception_Handler >
3680 : public
3684 Timer_Action,
3685 Error_Logger,
3686 Actor_Exception_Handler > >
3687{
3688 using base_type =
3692 Timer_Action,
3693 Error_Logger,
3694 Actor_Exception_Handler > >;
3695
3696public :
3697 //! Default constructor.
3705
3706 //! Constructor with wheel size and granularity parameters.
3708 //! Size of the wheel.
3709 unsigned int wheel_size,
3710 //! Size of time step for the timer_wheel.
3711 monotonic_clock::duration granularity )
3713 wheel_size,
3715 Error_Logger(),
3717 {}
3718
3719 //! Constructor with all parameters.
3721 //! Size of the wheel.
3722 unsigned int wheel_size,
3723 //! Size of time step for the timer_wheel.
3724 monotonic_clock::duration granularity,
3725 //! An error logger for timer thread.
3726 Error_Logger error_logger,
3727 //! An actor exception handler for timer thread.
3728 Actor_Exception_Handler exception_handler )
3729 : base_type(
3730 wheel_size,
3734 {}
3735};
3736
3737//
3738// timer_wheel_manager_template
3739//
3740
3741/*!
3742 * \brief A timer wheel manager template.
3743 *
3744 * \note Please see description of details::timer_wheel_engine for the details
3745 * of the timer wheel mechanism.
3746 *
3747 * \tparam Thread_Safety Thread-safety indicator.
3748 * Must be timertt::thread_safety::unsafe or timertt::thread_safety::safe.
3749 *
3750 * \tparam Timer_Action type of functor to perform an user-defined
3751 * action when timer expires. This must be Moveable and MoveConstructible
3752 * type.
3753 *
3754 * \tparam Error_Logger type of logger for errors detected during
3755 * timer handling. Interface for error logger is defined
3756 * by default_error_logger class.
3757 *
3758 * \tparam Actor_Exception_Handler type of handler for dealing with
3759 * exceptions thrown from timer actors. Interface for exception handler
3760 * is defined by default_actor_exception_handler.
3761 *
3762 * \since
3763 * v.1.1.0
3764 */
3765template<
3766 typename Thread_Safety,
3767 typename Timer_Action = default_timer_action_type,
3768 typename Error_Logger = default_error_logger,
3769 typename Actor_Exception_Handler = default_actor_exception_handler >
3771 : public
3774 Thread_Safety,
3775 Timer_Action,
3776 Error_Logger,
3777 Actor_Exception_Handler > >
3778{
3779 //! Shorthand for base type.
3780 using base_type =
3783 Thread_Safety,
3784 Timer_Action,
3785 Error_Logger,
3786 Actor_Exception_Handler > >;
3787
3788public :
3789 //! Default constructor.
3797
3798 //! Constructor with wheel size and granularity parameters.
3800 //! Size of the wheel.
3801 unsigned int wheel_size,
3802 //! Size of time step for the timer_wheel.
3803 monotonic_clock::duration granularity )
3805 wheel_size,
3807 Error_Logger(),
3809 {}
3810
3811 //! Constructor with all parameters.
3813 //! Size of the wheel.
3814 unsigned int wheel_size,
3815 //! Size of time step for the timer_wheel.
3816 monotonic_clock::duration granularity,
3817 //! An error logger for timer thread.
3818 Error_Logger error_logger,
3819 //! An actor exception handler for timer thread.
3820 Actor_Exception_Handler exception_handler )
3821 : base_type(
3822 wheel_size,
3826 {}
3827};
3828
3829//
3830// default_timer_wheel_thread
3831//
3832/*!
3833 * \brief Alias for timer_wheel_thread_template with the default
3834 * parameters.
3835 *
3836 * \since
3837 * v.1.2.0
3838 */
3839using default_timer_wheel_thread =
3844
3845//
3846// timer_list_thread_template
3847//
3848
3849/*!
3850 * \brief A timer list thread template.
3851 *
3852 * \note Please see description of details::timer_list_engine for the
3853 * details of this timer mechanism.
3854 *
3855 * \tparam Timer_Action type of functor to perform an user-defined
3856 * action when timer expires. This must be Moveable and MoveConstructible
3857 * type.
3858 *
3859 * \tparam Error_Logger type of logger for errors detected during
3860 * timer thread execution. Interface for error logger is defined
3861 * by default_error_logger class.
3862 *
3863 * \tparam Actor_Exception_Handler type of handler for dealing with
3864 * exceptions thrown from timer actors. Interface for exception handler
3865 * is defined by default_actor_exception_handler.
3866 */
3867template<
3868 typename Timer_Action,
3869 typename Error_Logger,
3870 typename Actor_Exception_Handler >
3872 : public
3876 Timer_Action,
3877 Error_Logger,
3878 Actor_Exception_Handler > >
3879{
3880 using base_type =
3884 Timer_Action,
3885 Error_Logger,
3886 Actor_Exception_Handler > >;
3887
3888public :
3889 //! Default constructor.
3892
3893 //! Constructor with all parameters.
3895 Error_Logger error_logger,
3896 Actor_Exception_Handler actor_exception_handler )
3898 {
3899 }
3900};
3901
3902//
3903// default_timer_list_thread
3904//
3905/*!
3906 * \brief Alias for timer_list_thread_template with the default
3907 * parameters.
3908 *
3909 * \since
3910 * v.1.2.0
3911 */
3912using default_timer_list_thread =
3917
3918//
3919// timer_list_manager_template
3920//
3921
3922/*!
3923 * \brief A timer list thread template.
3924 *
3925 * \note Please see description of details::timer_list_engine for the
3926 * details of this timer mechanism.
3927 *
3928 * \tparam Thread_Safety Thread-safety indicator.
3929 * Must be timertt::thread_safety::unsafe or timertt::thread_safety::safe.
3930 *
3931 * \tparam Timer_Action type of functor to perform an user-defined
3932 * action when timer expires. This must be Moveable and MoveConstructible
3933 * type.
3934 *
3935 * \tparam Error_Logger type of logger for errors detected during
3936 * timer handling. Interface for error logger is defined
3937 * by default_error_logger class.
3938 *
3939 * \tparam Actor_Exception_Handler type of handler for dealing with
3940 * exceptions thrown from timer actors. Interface for exception handler
3941 * is defined by default_actor_exception_handler.
3942 *
3943 * \since
3944 * v.1.1.0
3945 */
3946template<
3947 typename Thread_Safety,
3948 typename Timer_Action = default_timer_action_type,
3949 typename Error_Logger = default_error_logger,
3950 typename Actor_Exception_Handler = default_actor_exception_handler >
3952 : public
3955 Thread_Safety,
3956 Timer_Action,
3957 Error_Logger,
3958 Actor_Exception_Handler > >
3959{
3960 using base_type =
3963 Thread_Safety,
3964 Timer_Action,
3965 Error_Logger,
3966 Actor_Exception_Handler > >;
3967
3968public :
3969 //! Default constructor.
3972
3973 //! Constructor with all parameters.
3975 Error_Logger error_logger,
3976 Actor_Exception_Handler actor_exception_handler )
3978 {
3979 }
3980};
3981
3982//
3983// timer_heap_thread_template
3984//
3985
3986/*!
3987 * \brief A timer heap thread template.
3988 *
3989 * \note Please see description of details::timer_heap_engine for the
3990 * details about this timer mechanism.
3991 *
3992 * \tparam Error_Logger type of logger for errors detected during
3993 * timer thread execution. Interface for error logger is defined
3994 * by default_error_logger class.
3995 *
3996 * \tparam Timer_Action type of functor to perform an user-defined
3997 * action when timer expires. This must be Moveable and MoveConstructible
3998 * type.
3999 *
4000 * \tparam Actor_Exception_Handler type of handler for dealing with
4001 * exceptions thrown from timer actors. Interface for exception handler
4002 * is defined by default_actor_exception_handler.
4003 */
4004template<
4005 typename Timer_Action,
4006 typename Error_Logger,
4007 typename Actor_Exception_Handler >
4009 : public
4013 Timer_Action,
4014 Error_Logger,
4015 Actor_Exception_Handler > >
4016{
4017 //! Shorthand for base type.
4018 using base_type =
4022 Timer_Action,
4023 Error_Logger,
4024 Actor_Exception_Handler > >;
4025
4026public :
4027 //! Default constructor.
4028 /*!
4029 * Value default_initial_heap_capacity() is used as initial
4030 * heap array size.
4031 */
4038
4039 //! Constructor to specify initial capacity of heap-array.
4041 //! An initial size for heap array.
4042 std::size_t initial_heap_capacity )
4045 Error_Logger(),
4047 {}
4048
4049 //! Constructor with all parameters.
4051 //! An initial size for heap array.
4052 std::size_t initial_heap_capacity,
4053 //! An error logger for timer thread.
4054 Error_Logger error_logger,
4055 //! An actor exception handler for timer thread.
4056 Actor_Exception_Handler exception_handler )
4057 : base_type(
4061 {}
4062};
4063
4064//
4065// default_timer_heap_thread
4066//
4067/*!
4068 * \brief Alias for timer_heap_thread_template with the default
4069 * parameters.
4070 *
4071 * \since
4072 * v.1.2.0
4073 */
4074using default_timer_heap_thread =
4079
4080//
4081// timer_heap_manager_template
4082//
4083
4084/*!
4085 * \brief A timer heap manager template.
4086 *
4087 * \note Please see description of details::timer_heap_engine for the
4088 * details about this timer mechanism.
4089 *
4090 * \tparam Thread_Safety Thread-safety indicator.
4091 * Must be timertt::thread_safety::unsafe or timertt::thread_safety::safe.
4092 *
4093 * \tparam Timer_Action type of functor to perform an user-defined
4094 * action when timer expires. This must be Moveable and MoveConstructible
4095 * type.
4096 *
4097 * \tparam Error_Logger type of logger for errors detected during
4098 * timer handling. Interface for error logger is defined
4099 * by default_error_logger class.
4100 *
4101 * \tparam Actor_Exception_Handler type of handler for dealing with
4102 * exceptions thrown from timer actors. Interface for exception handler
4103 * is defined by default_actor_exception_handler.
4104 */
4105template<
4106 typename Thread_Safety,
4107 typename Timer_Action = default_timer_action_type,
4108 typename Error_Logger = default_error_logger,
4109 typename Actor_Exception_Handler = default_actor_exception_handler >
4111 : public
4114 Thread_Safety,
4115 Timer_Action,
4116 Error_Logger,
4117 Actor_Exception_Handler > >
4118{
4119 //! Shorthand for base type.
4120 using base_type =
4123 Thread_Safety,
4124 Timer_Action,
4125 Error_Logger,
4126 Actor_Exception_Handler > >;
4127
4128public :
4129 //! Default constructor.
4130 /*!
4131 * Value default_initial_heap_capacity() is used as initial
4132 * heap array size.
4133 */
4140
4141 //! Constructor to specify initial capacity of heap-array.
4143 //! An initial size for heap array.
4144 std::size_t initial_heap_capacity )
4147 Error_Logger(),
4149 {}
4150
4151 //! Constructor with all parameters.
4153 //! An initial size for heap array.
4154 std::size_t initial_heap_capacity,
4155 //! An error logger for timer thread.
4156 Error_Logger error_logger,
4157 //! An actor exception handler for timer thread.
4158 Actor_Exception_Handler exception_handler )
4159 : base_type(
4163 {}
4164};
4165
4166} /* namespace timertt */
#define TIMERTT_HAS_NOEXCEPT
#define TIMERTT_ALIGNAS_WORKAROUND(T)
#define TIMERTT_NOEXCEPT
Helper class for indication of long-lived reference via its type.
Definition outliving.hpp:98
An indentificator for the timer.
Definition timers.hpp:82
An interface for collector of elapsed timers.
Definition timers.hpp:452
Timer manager interface.
Definition timers.hpp:425
A base class for timer identificator.
Definition timers.hpp:47
Timer thread interface.
Definition timers.hpp:162
An actual implementation of timer_manager.
Definition timers.cpp:262
virtual timer_thread_stats_t query_stats() override
Get statistics for run-time monitoring.
Definition timers.cpp:331
virtual void schedule_anonymous(const std::type_index &type_index, const mbox_t &mbox, const message_ref_t &msg, std::chrono::steady_clock::duration pause, std::chrono::steady_clock::duration period) override
Push anonymous delayed/periodic message to the timer queue.
Definition timers.cpp:310
actual_manager_t(std::unique_ptr< Timer_Manager > manager, outliving_reference_t< timer_manager_t::elapsed_timers_collector_t > collector)
Initializing constructor.
Definition timers.cpp:267
outliving_reference_t< timer_manager_t::elapsed_timers_collector_t > m_collector
Definition timers.cpp:344
virtual bool empty() override
Definition timers.cpp:325
virtual void process_expired_timers() override
Translation of expired timers into message sends.
Definition timers.cpp:278
virtual timer_id_t schedule(const std::type_index &type_index, const mbox_t &mbox, const message_ref_t &msg, std::chrono::steady_clock::duration pause, std::chrono::steady_clock::duration period) override
Push delayed/periodic message to the timer queue.
Definition timers.cpp:291
std::unique_ptr< Timer_Manager > m_manager
Definition timers.cpp:342
virtual std::chrono::steady_clock::duration timeout_before_nearest_timer(std::chrono::steady_clock::duration default_timer) override
Calculate time before the nearest timer (if any).
Definition timers.cpp:284
An actual implementation of timer thread.
Definition timers.cpp:141
std::unique_ptr< Timer_Thread > m_thread
Definition timers.cpp:208
virtual timer_thread_stats_t query_stats() override
Get statistics for run-time monitoring.
Definition timers.cpp:197
virtual void start() override
Launch timer.
Definition timers.cpp:153
actual_thread_t(std::unique_ptr< Timer_Thread > thread)
Initializing constructor.
Definition timers.cpp:146
virtual void finish() override
Finish timer and wait for full stop.
Definition timers.cpp:159
virtual timer_id_t schedule(const std::type_index &type_index, const mbox_t &mbox, const message_ref_t &msg, std::chrono::steady_clock::duration pause, std::chrono::steady_clock::duration period) override
Push delayed/periodic message to the timer queue.
Definition timers.cpp:165
virtual void schedule_anonymous(const std::type_index &type_index, const mbox_t &mbox, const message_ref_t &msg, std::chrono::steady_clock::duration pause, std::chrono::steady_clock::duration period) override
Push anonymous delayed/periodic message to the timer queue.
Definition timers.cpp:183
An actual implementation of timer interface.
Definition timers.cpp:43
timer_holder_t & timer_holder() noexcept
Definition timers.cpp:61
actual_timer_t(Timer *thread)
Initialized constructor.
Definition timers.cpp:50
virtual void release() noexcept override
Release the timer event.
Definition timers.cpp:73
timer_holder_t m_timer
Underlying timer object reference.
Definition timers.cpp:91
virtual ~actual_timer_t() noexcept override
Definition timers.cpp:55
virtual bool is_active() const noexcept override
Is this timer event is active?
Definition timers.cpp:67
Timer * m_thread
Timer thread for the timer.
Definition timers.cpp:88
A functor to be used as timer action in implementation of timer thread.
Definition timers.cpp:222
timer_action_for_timer_manager_t(outliving_reference_t< timer_manager_t::elapsed_timers_collector_t > collector, std::type_index type_index, mbox_t mbox, message_ref_t msg)
Definition timers.cpp:230
outliving_reference_t< timer_manager_t::elapsed_timers_collector_t > m_collector
Definition timers.cpp:224
A functor to be used as timer action in implementation of timer thread.
Definition timers.cpp:105
timer_action_for_timer_thread_t(std::type_index type_index, mbox_t mbox, message_ref_t msg)
Definition timers.cpp:111
A implementation of basic methods for timer managers and timer threads.
void deactivate(timer_holder timer)
Deactivate timer and remove it from the list.
void activate(Duration_1 pause, timer_action action)
Activate timer and schedule it for execution.
void activate(timer_holder timer, Duration_1 pause, Duration_2 period, timer_action action)
Activate timer and schedule it for execution.
void reschedule(timer_holder timer, Duration_1 pause, timer_action action)
Perform an attempt to reschedule a timer.
void activate(scoped_timer_object &timer, Duration_1 pause, timer_action action)
Activate a scoped timer and schedule it for execution.
void deactivate(scoped_timer_object &timer)
Deactivate timer and remove it from the list.
timer_quantities get_timer_quantities()
Count of timers of various types.
void reschedule(timer_holder timer, Duration_1 pause, Duration_2 period, timer_action action)
Perform an attempt to reschedule a timer.
basic_methods_impl_mixin(Args &&... args)
Constructor with all parameters.
Engine m_engine
Actual timer engine instance.
void activate(Duration_1 pause, Duration_2 period, timer_action action)
Activate timer and schedule it for execution.
void activate(scoped_timer_object &timer, Duration_1 pause, Duration_2 period, timer_action action)
Activate a scoped timer and schedule it for execution.
timer_holder allocate()
Allocate of new timer object.
void activate(timer_holder timer, Duration_1 pause, timer_action action)
Activate timer and schedule it for execution.
A special storage to be used for holding non-default constructible objects which are created by deman...
buffer_allocated_object(const buffer_allocated_object &)=delete
TIMERTT_ALIGNAS_WORKAROUND(T) std bool allocated_
reference operator*() const TIMERTT_NOEXCEPT
buffer_allocated_object(buffer_allocated_object &&)=delete
buffer_allocated_object() TIMERTT_NOEXCEPT=default
A common part for all timer engines.
timer_quantities m_timer_quantities
Quantities of timers of various types.
void inc_timer_count(timer_kind kind)
Helper method for increment the count of timers of the specific type.
Actor_Exception_Handler m_exception_handler
Exception handler.
void reset_timer_count()
Helper method for reseting quantities of timers to zero.
void dec_timer_count(timer_kind kind)
Helper method for decrement the count of timers of the specific type.
timer_quantities get_timer_quantities() const
Get the quantities of timers of various types.
Error_Logger m_error_logger
Error logger.
engine_common(Error_Logger error_logger, Actor_Exception_Handler exception_handler)
Initializing constructor.
Template-based implementation of timer manager.
void process_expired_timers()
Perform processing of expired timers.
void reset()
Reset all timers and return manager to the initial state.
monotonic_clock::duration timeout_before_nearest_timer(Duration default_timeout)
Get the sleeping time before the earlist timer expiration.
manager_impl_template(Args &&... args)
Constructor with all parameters.
std::tuple< bool, monotonic_clock::time_point > nearest_time_point()
Get the time for next process_expired_timers invocation.
Template-based implementation of timer thread.
void shutdown()
Initiate shutdown for the timer thread without waiting for completion.
void sleep_for_next_event(typename base_type::lock_guard &lock)
Waiting for next event to process.
void shutdown_and_join()
Initiate shutdown and wait for completion.
void join()
Wait for completion of timer thread.
thread_impl_template(Args &&... args)
Constructor with all parameters.
A special wrapper around actual std::unique_lock.
std::unique_lock< std::mutex > & actual_lock()
Accessor for actual std::unique_lock object.
A special wrapper around actual std::unique_lock.
A special storage for holding timer actions.
buffer_allocated_object< Action_Type > m_action
timer_action_holder(timer_action_holder &&)=delete
timer_action_holder(const timer_action_holder &)=delete
An engine for timer heap mechanism.
timer_type * m_timer_in_processing
Timer which is currently in processing.
void clear_all()
Clear all timer demands.
void deactivate(timer_object_holder< Thread_Safety > timer)
Deactivate timer and remove it from the list.
void heap_swap(timer_type *a, timer_type *b)
Swap two heap nodes.
static void ensure_timer_deactivated(const timer_type *timer)
Hard check for deactivation state of the timer.
void execute_timer_in_processing(Unique_Lock &lock) TIMERTT_NOEXCEPT
Execute the current timer.
void process_expired_timers(Unique_Lock &lock)
Process all expired timers from the heap.
timer_object_holder< Thread_Safety > allocate()
Create timer to be activated later.
std::vector< timer_type * > m_heap
Array for holding heap data structure.
timer_type * heap_item(std::size_t position) const
Get timer by it index.
bool heap_empty() const
Is heap data structure empty?
timer_heap_engine_defaults defaults_type
Type with default parameters for this engine.
bool empty() const
Is empty timer list?
monotonic_clock::time_point nearest_time_point() const
Get time point of the next timer.
bool reschedule(timer_object_holder< Thread_Safety > timer, Duration_1 pause, Duration_2 period, timer_action action)
Perform an attempt to reschedule a timer.
void heap_add(timer_type *timer)
Add new timer to the heap data structure.
timer_heap_engine(std::size_t initial_heap_capacity, Error_Logger error_logger, Actor_Exception_Handler exception_handler)
Constructor with all parameters.
timer_type * heap_head() const
Get the minimal timer.
bool activate(timer_object_holder< Thread_Safety > timer, Duration_1 pause, Duration_2 period, timer_action action)
Activate timer and schedule it for execution.
void heap_remove(timer_type *timer)
Remove timer from the heap data structure.
An engine for timer list mechanism.
void exec_actions(Unique_Lock &lock, timer_type *head) TIMERTT_NOEXCEPT
Execute all active timers in the sublist.
void clear_all()
Deactivate all timers and cleanup internal data structures.
static void ensure_timer_deactivated(const timer_type *timer)
Hard check for deactivation state of the timer.
bool activate(timer_object_holder< Thread_Safety > timer, Duration_1 pause, Duration_2 period, timer_action action)
Activate timer and schedule it for execution.
void process_expired_timers(Unique_Lock &lock)
Build sublist of elapsed timers and process them all.
monotonic_clock::time_point nearest_time_point() const
Get time point of the next timer.
bool reschedule(timer_object_holder< Thread_Safety > timer, Duration_1 pause, Duration_2 period, timer_action action)
Perform an attempt to reschedule a timer.
timer_list_engine(Error_Logger error_logger, Actor_Exception_Handler exception_handler)
Constructor with all parameters.
bool empty() const
Is empty timer list?
timer_type * m_head
Head of the list of timers.
timer_list_engine_defaults defaults_type
Type with default parameters for this engine.
void deactivate(timer_object_holder< Thread_Safety > timer)
Deactivate timer and remove it from the list.
void remove_timer_from_list(timer_type *timer)
Remove the timer from the list.
timer_type * make_exec_list()
Build sublist of elapsed timers.
timer_object_holder< Thread_Safety > allocate()
Create timer to be activated later.
void utilize_exec_list(timer_type *head)
Process list of elapsed timers after execution of its actions.
timer_type * m_tail
Tail of the list of timers.
void insert_timer_to_list(timer_type *timer)
Insert timer to the list.
A engine for timer wheel mechanism.
timer_object_holder< Thread_Safety > allocate()
Create timer to be activated later.
monotonic_clock::time_point nearest_time_point() const
Get time point of the next timer.
void insert_demand_to_wheel(timer_type *wheel_timer)
Insert timer to the wheel.
unsigned int m_current_position
Index of the current position in the wheel.
void process_expired_timers(Unique_Lock &lock)
Build sublist of elapsed timers and process them all.
bool m_current_tick_processed
Has the current tick been processed?
timer_wheel_engine(unsigned int wheel_size, monotonic_clock::duration granularity, Error_Logger error_logger, Actor_Exception_Handler exception_handler)
Constructor with all parameters.
bool empty() const
Is empty timer list?
unsigned int duration_to_ticks(Duration d) const
Converion of duration to number of time steps.
bool activate(timer_object_holder< Thread_Safety > timer, Duration_1 pause, Duration_2 period, timer_action action)
Activate timer and schedule it for execution.
std::vector< wheel_item > m_wheel
The wheel data.
const monotonic_clock::duration m_granularity
Granularity of one time step.
void process_current_wheel_position(Unique_Lock &lock)
Detect elapsed timers for the current time step and process them all.
void clear_all()
Deactivate all timers and cleanup internal data structures.
void remove_timer_from_wheel(timer_type *wheel_timer)
Remove timer from the timer_wheel.
monotonic_clock::time_point m_current_tick_border
Right border of the current tick.
static void ensure_timer_deactivated(const timer_type *timer)
Hard check for deactivation state of the timer.
bool reschedule(timer_object_holder< Thread_Safety > timer, Duration_1 pause, Duration_2 period, timer_action action)
Perform an attempt to reschedule a timer.
void perform_insertion_info_wheel(timer_type *wheel_timer, Duration_1 pause, Duration_2 period)
Perform insertion of a timer into wheel data structure.
void utilize_exec_list(timer_type *head)
Process list of elapsed timers after execution of its actions.
const unsigned int m_wheel_size
Size of the wheel.
void set_position_in_the_wheel(timer_type *wheel_timer, unsigned int pause_in_ticks) const
Calculate and fill up wheel position for the timer.
timer_type * make_exec_list()
Make list of elapsed timers to be executed.
void deactivate(timer_object_holder< Thread_Safety > timer)
Deactivate timer and remove it from the wheel.
void exec_actions(Unique_Lock &lock, timer_type *head) TIMERTT_NOEXCEPT
Execute all active timers from the list.
A special wrapper to be used to hold an actual timer object which is not allocated dynamically.
scoped_timer_object_holder(scoped_timer_object_holder &&)=delete
scoped_timer_object_holder(const scoped_timer_object_holder &)=delete
timer_heap_manager_template(std::size_t initial_heap_capacity, Error_Logger error_logger, Actor_Exception_Handler exception_handler)
Constructor with all parameters.
timer_heap_manager_template(std::size_t initial_heap_capacity)
Constructor to specify initial capacity of heap-array.
timer_heap_thread_template(std::size_t initial_heap_capacity)
Constructor to specify initial capacity of heap-array.
timer_heap_thread_template(std::size_t initial_heap_capacity, Error_Logger error_logger, Actor_Exception_Handler exception_handler)
Constructor with all parameters.
timer_list_manager_template(Error_Logger error_logger, Actor_Exception_Handler actor_exception_handler)
Constructor with all parameters.
timer_list_thread_template(Error_Logger error_logger, Actor_Exception_Handler actor_exception_handler)
Constructor with all parameters.
An intrusive smart pointer to timer demand.
void swap(timer_object_holder &o)
Swap values.
timer_object_holder(timer_object< Thread_Safety > *t)
Constructor for a raw pointer.
void reset()
Drop controlled object.
void dismiss_object()
Decrement reference count to object and delete it if needed.
void take_object()
Increment reference count to object if it's not null.
timer_object_holder(timer_object_holder &&o)
Move constructor.
timer_object< Thread_Safety > * m_timer
Timer controlled by a smart pointer.
timer_object_holder()
Default constructor.
timer_object_holder & operator=(const timer_object_holder &o)
Copy operator.
timer_object< Thread_Safety > * get() const
timer_object_holder(const timer_object_holder &o)
Copy constructor.
timer_object_holder & operator=(timer_object_holder &&o)
Move operator.
operator bool() const
Is this a null pointer?
timer_object_holder(scoped_timer_object_holder< Actual_Object > &scoped)
Constructor for the case when timer object is a scoped timer.
timer_wheel_manager_template(unsigned int wheel_size, monotonic_clock::duration granularity)
Constructor with wheel size and granularity parameters.
timer_wheel_manager_template(unsigned int wheel_size, monotonic_clock::duration granularity, Error_Logger error_logger, Actor_Exception_Handler exception_handler)
Constructor with all parameters.
timer_wheel_thread_template(unsigned int wheel_size, monotonic_clock::duration granularity, Error_Logger error_logger, Actor_Exception_Handler exception_handler)
Constructor with all parameters.
timer_wheel_thread_template(unsigned int wheel_size, monotonic_clock::duration granularity)
Constructor with wheel size and granularity parameters.
#define SO_5_FUNC
Definition declspec.hpp:48
#define SO_5_LOG_ERROR(logger, var_name)
A special macro for helping error logging.
Timers implementation details.
Definition timers.cpp:25
exception_handler_for_timertt_t create_exception_handler_for_timertt_thread(const error_logger_shptr_t &logger)
Definition timers.cpp:408
exception_handler_for_timertt_t create_exception_handler_for_timertt_manager(const error_logger_shptr_t &logger)
Definition timers.cpp:423
error_logger_for_timertt_t create_error_logger_for_timertt(const error_logger_shptr_t &logger)
Definition timers.cpp:368
Private part of message limit implementation.
Definition agent.cpp:33
SO_5_FUNC timer_manager_unique_ptr_t create_timer_heap_manager(error_logger_shptr_t logger, outliving_reference_t< timer_manager_t::elapsed_timers_collector_t > collector, std::size_t initial_heap_capacity)
Create timer manager based on timer_heap mechanism.
Definition timers.cpp:618
SO_5_FUNC timer_thread_unique_ptr_t create_timer_wheel_thread(error_logger_shptr_t logger)
Create timer thread based on timer_wheel mechanism.
Definition timers.cpp:490
SO_5_FUNC timer_thread_unique_ptr_t create_timer_heap_thread(error_logger_shptr_t logger, std::size_t initial_heap_capacity)
Create timer thread based on timer_heap mechanism.
Definition timers.cpp:533
SO_5_FUNC timer_thread_unique_ptr_t create_timer_list_thread(error_logger_shptr_t logger)
Create timer thread based on timer_list mechanism.
Definition timers.cpp:551
SO_5_FUNC timer_manager_unique_ptr_t create_timer_list_manager(error_logger_shptr_t logger, outliving_reference_t< timer_manager_t::elapsed_timers_collector_t > collector)
Create timer thread based on timer_list mechanism.
Definition timers.cpp:638
SO_5_FUNC timer_manager_unique_ptr_t create_timer_wheel_manager(error_logger_shptr_t logger, outliving_reference_t< timer_manager_t::elapsed_timers_collector_t > collector)
Create timer manager based on timer_wheel mechanism.
Definition timers.cpp:567
SO_5_FUNC timer_manager_unique_ptr_t create_timer_wheel_manager(error_logger_shptr_t logger, outliving_reference_t< timer_manager_t::elapsed_timers_collector_t > collector, unsigned int wheel_size, std::chrono::steady_clock::duration granuality)
Create timer manager based on timer_wheel mechanism.
Definition timers.cpp:582
SO_5_FUNC timer_manager_unique_ptr_t create_timer_heap_manager(error_logger_shptr_t logger, outliving_reference_t< timer_manager_t::elapsed_timers_collector_t > collector)
Create timer manager based on timer_heap mechanism.
Definition timers.cpp:604
SO_5_FUNC timer_thread_unique_ptr_t create_timer_wheel_thread(error_logger_shptr_t logger, unsigned int wheel_size, std::chrono::steady_clock::duration granuality)
Create timer thread based on timer_wheel mechanism.
Definition timers.cpp:502
SO_5_FUNC timer_thread_unique_ptr_t create_timer_heap_thread(error_logger_shptr_t logger)
Create timer thread based on timer_heap mechanism.
Definition timers.cpp:522
An internal namespace with implementation details.
timer_kind
Type of the timer (single-shot or periodic).
@ single_shot
Timer is a single-shot timer.
@ periodic
Timer is a periodic timer.
@ deactivated
Timer is deactivated.
@ wait_for_execution
Timer is in execution list and is waiting for execution.
@ wait_for_deactivation
Timer must be deactivated after processing of execution list.
@ active
Timer is activated.
Top-level project's namespace.
std::function< void() > default_timer_action_type
Defaulf type of timer action.
std::chrono::steady_clock monotonic_clock
Type of clock used by all threads.
Statistics for run-time monitoring.
Definition timers.hpp:136
Class of default handler for exceptions thrown from timer actors.
void operator()(const std::exception &)
Handles exception.
Class of default error logger.
void operator()(const std::string &what)
Logs error message to std::cerr.
Indicator that an engine will be owned by timer manager.
Indicator that an engine will be owned by timer thread.
A type-container for types of engine-consumers.
A selector of actual mixin type for timer manager or timer thread.
A mixin which must be used as base class for timer threads.
void ensure_started()
Checks that timer thread is really started.
std::shared_ptr< std::thread > m_thread
Underlying thread object.
std::condition_variable m_condition
Condition variable for waiting for next event.
void notify()
Sends notification to timer thread.
std::mutex m_lock
Timer thread's lock.
A mixin which must be used as base class for thread-safe timer managers.
A mixin which must be used as base class for not-thread-safe timer managers.
static const std::size_t deactivation_indicator
A special value which means that timer is deactivated.
timer_action_holder< timer_action > m_action
Timer action.
std::size_t m_position
Position in the heap-array.
monotonic_clock::duration m_period
Period in ticks.
timer_kind kind() const
Detect type of timer (single-shot or periodic).
bool single_shot() const
Is this is single shot timer?
monotonic_clock::time_point m_when
Time of execution for this timer.
Container for static method with default values for timer_heap engine.
static std::size_t default_initial_heap_capacity()
Default initial capacity of heap-array.
timer_action_holder< timer_action > m_action
Timer action.
monotonic_clock::time_point m_when
Time of execution for this timer.
timer_type * m_prev
Previous demand in the list.
threading_traits< Thread_Safety >::status_holder_type m_status
Status of the timer.
timer_kind kind() const
Detect type of timer (single-shot or periodic).
monotonic_clock::duration m_period
Period in ticks.
Container for static method with default values for timer_list engine.
threading_traits< Thread_Safety >::status_holder_type m_status
Status of the timer.
timer_kind kind() const
Detect type of the timer (single-shot or periodic).
unsigned int m_full_rolls_left
Full rolls of wheel before execution of demand.
timer_action_holder< timer_action > m_action
Timer action.
timer_type * m_prev
Previous demand in the list.
Container for static method with default values for timer_wheel engine.
static unsigned int default_wheel_size()
Default wheel size.
static monotonic_clock::duration default_granularity()
Default tick duration.
Indicator for thread-safe implemetation.
Indicator for not-thread-safe implementation.
Container for thread safety flags.
std::atomic_uint reference_counter_type
Type for reference counters.
std::atomic< details::timer_status > status_holder_type
Type for holding timer status inside a timer object.
details::timer_status status_holder_type
Type for holding timer status inside a timer object.
unsigned int reference_counter_type
Type for reference counters.
Container for thread-safety-specific type declarations.
Base type for timer demands.
static void decrement_references(timer_object *t)
static void increment_references(timer_object *t)
Increment reference counter for the demand.
threading_traits< Thread_Safety >::reference_counter_type m_references
Reference counter for the demand.
timer_object()
Deafault constructor.
Information about quantities of various timer types.
std::size_t m_periodic_count
Quantity of periodic timers.
std::size_t m_single_shot_count
Quantity of single-shot timers.