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