E-MailRelay
gtimerlist.h
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gtimerlist.h
19///
20
21#ifndef G_NET_TIMER_LIST_H
22#define G_NET_TIMER_LIST_H
23
24#include "gdef.h"
25#include "gdatetime.h"
26#include "gtimer.h"
27#include "geventhandler.h"
28#include "gexception.h"
29#include "geventstate.h"
30#include <utility>
31#include <vector>
32
33namespace GNet
34{
35 class TimerList ;
36 class TimerBase ;
37 class TimerListTest ;
38}
39
40//| \class GNet::TimerList
41/// A singleton which maintains a list of all Timer objects, and interfaces
42/// to the event loop on their behalf.
43///
44/// Event loops should call TimerList::interval() to determine how long to
45/// wait before the first Timer goes off. If this is zero, or after their
46/// event-waiting times-out, they should call TimerList::doTimeouts().
47///
48/// There can be a race where this class incorrectly sees no expired timers
49/// in doTimeouts() if, for example, the system clock is being stretched.
50/// However, the interval() or setTimer() time will be very small and the
51/// race will resolve itself naturally.
52///
53/// Every timer has an associated exception handler, typically a
54/// more long-lived object that has the timer as a sub-object.
55/// If the timer callback throws an exception then timer list catches
56/// it and invokes the exception handler -- and if that throws then the
57/// exception escapes the event loop. This is safe even if the exception
58/// handler object is destroyed by the original exception because the
59/// exception handler base-class destructor uses the timer list's disarm()
60/// mechanism. This is the same behaviour as in the EventLoop.
61///
62/// The implementation maintains a pointer to the timer that will
63/// expire soonest so that interval() is fast and O(1) when the set
64/// of timers is stable and most events are non-timer events.
65///
66/// Zero-length timers expire in the same order as they were started,
67/// which allows them to be used as a mechanism for asynchronous
68/// message-passing.
69///
71{
72public:
73 G_EXCEPTION( NoInstance , tx("no TimerList instance") )
74
75 TimerList() ;
76 ///< Default constructor.
77
78 ~TimerList() ;
79 ///< Destructor.
80
81 void add( TimerBase & , EventState ) ;
82 ///< Adds a timer. Called from the Timer constructor.
83
84 void remove( TimerBase & ) noexcept ;
85 ///< Removes a timer from the list. Called from the
86 ///< Timer destructor.
87
88 void updateOnStart( TimerBase & ) ;
89 ///< Called by Timer when a timer is started.
90
91 void updateOnCancel( TimerBase & ) ;
92 ///< Called by Timer when a timer is cancelled.
93
94 std::pair<G::TimeInterval,bool> interval() const ;
95 ///< Returns the interval to the first timer expiry. The second
96 ///< part is an 'infinite' flag that is set if there are no
97 ///< timers running. In pathological cases the interval
98 ///< will be capped at the type's maximum value.
99
100 void doTimeouts() ;
101 ///< Triggers the timeout callbacks of any expired timers.
102 ///< Called by the event loop (GNet::EventLoop). Any exception
103 ///< thrown out of an expired timer's callback is caught and
104 ///< delivered back to the EventState associated with
105 ///< the timer.
106
107 static bool exists() ;
108 ///< Returns true if instance() exists.
109
110 static TimerList * ptr() noexcept ;
111 ///< Singleton access. Returns nullptr if none.
112
113 static TimerList & instance() ;
114 ///< Singleton access. Throws an exception if none.
115
116 void disarm( ExceptionHandler * ) noexcept ;
117 ///< Resets any matching ExceptionHandler pointers.
118
119public:
120 TimerList( const TimerList & ) = delete ;
121 TimerList( TimerList && ) = delete ;
122 TimerList & operator=( const TimerList & ) = delete ;
123 TimerList & operator=( TimerList && ) = delete ;
124
125private:
126 struct ListItem /// A value type for the GNet::TimerList.
127 {
128 TimerBase * m_timer{nullptr} ; // handler for the timeout event
129 EventState m_es ; // handler for any exception thrown
130 ListItem( TimerBase * t , EventState es ) ;
131 bool operator==( const ListItem & v ) const noexcept ;
132 void resetIf( TimerBase * p ) noexcept ;
133 void disarmIf( ExceptionHandler * eh ) noexcept ;
134 } ;
135 using List = std::vector<ListItem> ;
136 struct Lock /// A RAII class to lock and unlock GNet::TimerList.
137 {
138 explicit Lock( TimerList & ) ;
139 ~Lock() ;
140 Lock( const Lock & ) = delete ;
141 Lock( Lock && ) = delete ;
142 Lock & operator=( const Lock & ) = delete ;
143 Lock & operator=( Lock && ) = delete ;
144 TimerList & m_timer_list ;
145 } ;
146 friend class GNet::TimerListTest ;
147 friend struct Lock ;
148
149private:
150 const TimerBase * findSoonest() const ;
151 void lock() ;
152 void unlock() ;
153 void purgeRemoved() ;
154 void mergeAdded() ;
155 void doTimeout( ListItem & ) ;
156 static void removeFrom( List & , TimerBase * ) noexcept ;
157 static void disarmIn( List & , ExceptionHandler * ) noexcept ;
158
159private:
160 static TimerList * m_this ;
161 mutable const TimerBase * m_soonest{nullptr} ;
162 unsigned int m_adjust{0} ;
163 bool m_locked{false} ;
164 bool m_removed{false} ;
165 List m_list ;
166 List m_list_added ; // temporary list for when add()ed from within doTimeouts()
167} ;
168
169#endif
A lightweight object containing an ExceptionHandler pointer, optional ExceptionSource pointer and opt...
Definition: geventstate.h:131
An abstract interface for handling exceptions thrown out of event-loop callbacks (socket/future event...
An interface used by GNet::TimerList to keep track of pending timeouts and to deliver timeout events.
Definition: gtimer.h:42
A singleton which maintains a list of all Timer objects, and interfaces to the event loop on their be...
Definition: gtimerlist.h:71
std::pair< G::TimeInterval, bool > interval() const
Returns the interval to the first timer expiry.
Definition: gtimerlist.cpp:149
void remove(TimerBase &) noexcept
Removes a timer from the list.
Definition: gtimerlist.cpp:92
void doTimeouts()
Triggers the timeout callbacks of any expired timers.
Definition: gtimerlist.cpp:227
static bool exists()
Returns true if instance() exists.
Definition: gtimerlist.cpp:176
void disarm(ExceptionHandler *) noexcept
Resets any matching ExceptionHandler pointers.
Definition: gtimerlist.cpp:106
void updateOnStart(TimerBase &)
Called by Timer when a timer is started.
Definition: gtimerlist.cpp:118
void add(TimerBase &, EventState)
Adds a timer. Called from the Timer constructor.
Definition: gtimerlist.cpp:87
static TimerList & instance()
Singleton access. Throws an exception if none.
Definition: gtimerlist.cpp:182
static TimerList * ptr() noexcept
Singleton access. Returns nullptr if none.
Definition: gtimerlist.cpp:170
TimerList()
Default constructor.
Definition: gtimerlist.cpp:75
void updateOnCancel(TimerBase &)
Called by Timer when a timer is cancelled.
Definition: gtimerlist.cpp:130
~TimerList()
Destructor.
Definition: gtimerlist.cpp:81
Network classes.
Definition: gdef.h:1243
constexpr const char * tx(const char *p) noexcept
A briefer alternative to G::gettext_noop().
Definition: ggettext.h:84