E-MailRelay

gtimerlist.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 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.cpp
19///
20
21#include "gdef.h"
22#include "gtimerlist.h"
23#include "gtimer.h"
24#include "gnetdone.h"
25#include "geventloop.h"
26#include "geventloggingcontext.h"
27#include "glog.h"
28#include "gassert.h"
29#include <algorithm>
30#include <functional>
31#include <sstream>
32
33GNet::TimerList::Value::Value()
34= default;
35
36GNet::TimerList::Value::Value( TimerBase * t , ExceptionSink es ) :
37 m_timer(t) ,
38 m_es(es)
39{
40}
41
42 inline bool GNet::TimerList::Value::operator==( const Value & v ) const noexcept
43{
44 return m_timer == v.m_timer ;
45}
46
47 inline void GNet::TimerList::Value::resetIf( TimerBase * p ) noexcept
48{
49 if( m_timer == p )
50 m_timer = nullptr ;
51}
52
53 void GNet::TimerList::Value::disarmIf( ExceptionHandler * eh ) noexcept
54{
55 if( m_es.eh() == eh )
56 m_es.reset() ;
57}
58
59// ==
60
61GNet::TimerList::Lock::Lock( TimerList & timer_list ) :
62 m_timer_list(timer_list)
63{
64 m_timer_list.lock() ;
65}
66
67GNet::TimerList::Lock::~Lock()
68{
69 m_timer_list.unlock() ;
70}
71
72// ==
73
74 GNet::TimerList * GNet::TimerList::m_this = nullptr ;
75
76 GNet::TimerList::TimerList()
77{
78 if( m_this == nullptr )
79 m_this = this ;
80}
81
82 GNet::TimerList::~TimerList()
83{
84 if( m_this == this )
85 m_this = nullptr ;
86}
87
88 void GNet::TimerList::add( TimerBase & t , ExceptionSink es )
89{
90 (m_locked?m_list_added:m_list).push_back( Value(&t,es) ) ;
91}
92
93 void GNet::TimerList::remove( TimerBase & timer ) noexcept
94{
95 m_removed = true ;
96 removeFrom( m_list , &timer ) ;
97 removeFrom( m_list_added , &timer ) ;
98 if( m_soonest == &timer ) m_soonest = nullptr ;
99}
100
101 void GNet::TimerList::removeFrom( List & list , TimerBase * timer_p ) noexcept
102{
103 for( auto & value : list)
104 value.resetIf( timer_p ) ;
105}
106
107 void GNet::TimerList::disarm( ExceptionHandler * eh ) noexcept
108{
109 disarmIn( m_list , eh ) ;
110 disarmIn( m_list_added , eh ) ;
111}
112
113 void GNet::TimerList::disarmIn( List & list , ExceptionHandler * eh ) noexcept
114{
115 for( auto & value : list )
116 value.disarmIf( eh ) ;
117}
118
119 void GNet::TimerList::updateOnStart( TimerBase & timer )
120{
121 if( timer.immediate() )
122 timer.adjust( m_adjust++ ) ; // well-defined t() order for immediate timers
123
124 if( m_soonest == &timer )
125 m_soonest = nullptr ;
126
127 if( m_soonest != nullptr && timer.t() < m_soonest->t() )
128 m_soonest = &timer ;
129}
130
131 void GNet::TimerList::updateOnCancel( TimerBase & timer )
132{
133 if( m_soonest == &timer )
134 m_soonest = nullptr ;
135}
136
137 const GNet::TimerBase * GNet::TimerList::findSoonest() const
138{
139 G_ASSERT( !m_locked ) ;
140 TimerBase * result = nullptr ;
141 for( const auto & t : m_list )
142 {
143 if( t.m_timer != nullptr && t.m_timer->active() && ( result == nullptr || t.m_timer->t() < result->t() ) )
144 result = t.m_timer ;
145 }
146 return result ;
147}
148
149std::pair<G::TimeInterval,bool> GNet::TimerList::interval() const
150{
151 if( m_soonest == nullptr )
152 m_soonest = findSoonest() ;
153
154 if( m_soonest == nullptr )
155 {
156 return std::make_pair( G::TimeInterval(0) , true ) ;
157 }
158 else if( m_soonest->immediate() )
159 {
160 return std::make_pair( G::TimeInterval(0) , false ) ;
161 }
162 else
163 {
164 G::TimerTime now = G::TimerTime::now() ;
165 G::TimerTime then = m_soonest->t() ;
166 return std::make_pair( G::TimeInterval(now,then) , false ) ;
167 }
168}
169
170 GNet::TimerList * GNet::TimerList::ptr() noexcept
171{
172 return m_this ;
173}
174
175 bool GNet::TimerList::exists()
176{
177 return m_this != nullptr ;
178}
179
180 GNet::TimerList & GNet::TimerList::instance()
181{
182 if( m_this == nullptr )
183 throw NoInstance() ;
184 return * m_this ;
185}
186
187 void GNet::TimerList::lock()
188{
189 m_locked = true ;
190}
191
192 void GNet::TimerList::unlock()
193{
194 if( m_locked )
195 {
196 m_locked = false ;
197 mergeAdded() ; // accept any add()ed while locked
198 purgeRemoved() ; // collect garbage created by remove()
199 }
200}
201
202 void GNet::TimerList::mergeAdded()
203{
204 if( !m_list_added.empty() )
205 {
206 m_list.reserve( m_list.size() + m_list_added.size() ) ;
207 m_list.insert( m_list.end() , m_list_added.begin() , m_list_added.end() ) ;
208 m_list_added.clear() ;
209 }
210}
211
212 void GNet::TimerList::purgeRemoved()
213{
214 if( m_removed )
215 {
216 m_removed = false ;
217 m_list.erase( std::remove( m_list.begin() , m_list.end() , Value(nullptr,ExceptionSink()) ) , m_list.end() ) ;
218 }
219}
220
221 void GNet::TimerList::doTimeouts()
222{
223 G_ASSERT( m_list_added.empty() ) ;
224 Lock lock( *this ) ;
225 m_adjust = 0 ;
226 G::TimerTime now = G::TimerTime::zero() ; // lazy initialisation to G::TimerTime::now() in G::Timer::expired()
227 for( auto & value : m_list )
228 {
229 if( value.m_timer != nullptr && value.m_timer->active() && value.m_timer->expired(now) )
230 {
231 EventLoggingContext set_logging_context( value.m_es.esrc() ) ;
232 try
233 {
234 if( value.m_timer == m_soonest ) m_soonest = nullptr ;
235 value.m_timer->doTimeout() ;
236 }
237 catch( GNet::Done & e ) // (caught separately to avoid requiring rtti)
238 {
239 if( value.m_es.set() )
240 value.m_es.call( e , true ) ; // call onException()
241 else
242 throw ; // (new)
243 }
244 catch( std::exception & e )
245 {
246 if( value.m_es.set() )
247 value.m_es.call( e , false ) ; // call onException()
248 else
249 throw ; // (new)
250 }
251 }
252 }
253 unlock() ; // avoid doing possibly-throwing operations in Lock dtor
254}
255
256std::string GNet::TimerList::report() const
257{
258 std::ostringstream ss ;
259 ss << m_list.size() ;
260 return ss.str() ;
261}
262
GNet::Done
An exception class that is detected by GNet::EventHandlerList and results in onException() being call...
Definition: gnetdone.h:40
GNet::EventLoggingContext
A class that sets the G::LogOuput::context() while in scope.
Definition: geventloggingcontext.h:36
GNet::ExceptionHandler
An abstract interface for handling exceptions thrown out of event-loop callbacks (socket/future event...
Definition: gexceptionhandler.h:44
GNet::ExceptionSink
A tuple containing an ExceptionHandler interface pointer and a bound 'exception source' pointer.
Definition: gexceptionsink.h:100
GNet::TimerBase
An interface used by GNet::TimerList to keep track of pending timeouts and to deliver timeout events.
Definition: gtimer.h:41
GNet::TimerBase::immediate
bool immediate() const
Used by TimerList.
Definition: gtimer.cpp:83
GNet::TimerBase::t
G::TimerTime t() const
Used by TimerList to get the expiry epoch time.
Definition: gtimer.cpp:111
GNet::TimerBase::adjust
void adjust(unsigned int us)
Used by TimerList to set the fractional part of the expiry time of immediate() timers so that t() is ...
Definition: gtimer.cpp:88
GNet::TimerList
A singleton which maintains a list of all Timer objects, and interfaces to the event loop on their be...
Definition: gtimerlist.h:76
GNet::TimerList::interval
std::pair< G::TimeInterval, bool > interval() const
Returns the interval to the first timer expiry.
Definition: gtimerlist.cpp:149
GNet::TimerList::remove
void remove(TimerBase &) noexcept
Removes a timer from the list.
Definition: gtimerlist.cpp:93
GNet::TimerList::doTimeouts
void doTimeouts()
Triggers the timeout callbacks of any expired timers.
Definition: gtimerlist.cpp:221
GNet::TimerList::exists
static bool exists()
Returns true if instance() exists.
Definition: gtimerlist.cpp:175
GNet::TimerList::disarm
void disarm(ExceptionHandler *) noexcept
Resets any matching ExceptionHandler pointers.
Definition: gtimerlist.cpp:107
GNet::TimerList::updateOnStart
void updateOnStart(TimerBase &)
Called by Timer when a timer is started.
Definition: gtimerlist.cpp:119
GNet::TimerList::instance
static TimerList & instance()
Singleton access. Throws an exception if none.
Definition: gtimerlist.cpp:180
GNet::TimerList::ptr
static TimerList * ptr() noexcept
Singleton access. Returns nullptr if none.
Definition: gtimerlist.cpp:170
GNet::TimerList::TimerList
TimerList()
Default constructor.
Definition: gtimerlist.cpp:76
GNet::TimerList::updateOnCancel
void updateOnCancel(TimerBase &)
Called by Timer when a timer is cancelled.
Definition: gtimerlist.cpp:131
GNet::TimerList::~TimerList
~TimerList()
Destructor.
Definition: gtimerlist.cpp:82
GNet::TimerList::add
void add(TimerBase &, ExceptionSink)
Adds a timer. Called from the Timer constructor.
Definition: gtimerlist.cpp:88
GNet::TimerList::report
std::string report() const
Returns a line of text reporting the status of the timer list.
Definition: gtimerlist.cpp:256
G::TimeInterval
An interval between two G::SystemTime values or two G::TimerTime values.
Definition: gdatetime.h:289
G::TimerTime
A monotonically increasing subsecond-resolution timestamp, notionally unrelated to time_t.
Definition: gdatetime.h:213
G::TimerTime::now
static TimerTime now()
Factory function for the current steady-clock time.
Definition: gdatetime.cpp:385
G::TimerTime::zero
static TimerTime zero()
Factory function for the start of the epoch.
Definition: gdatetime.cpp:390
gassert.h
geventloggingcontext.h
geventloop.h
glog.h
gnetdone.h
gtimer.h
gtimerlist.h