E-MailRelay
geventhandlerlist.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 geventhandlerlist.cpp
19///
20
21#include "gdef.h"
22#include "gnetdone.h"
23#include "geventhandlerlist.h"
25#include "glog.h"
26#include "gassert.h"
27#include <algorithm>
28
29namespace GNet
30{
31 namespace EventHandlerListImp /// An implementation namespace for GNet::EventHandlerList.
32 {
33 struct fdless /// A functor that compares two file descriptors.
34 {
36 bool operator()( const Value & p1 , const Value & p2 ) noexcept
37 {
38 return p1.m_fd < p2.m_fd ;
39 }
40 } ;
41 }
42}
43
44GNet::EventHandlerList::EventHandlerList( const std::string & type ) :
45 m_type(type) ,
46 m_lock(0U) ,
47 m_has_garbage(false)
48{
49}
50
52{
53 G_ASSERT( handler != nullptr ) ; if( handler == nullptr ) return ;
54 G_ASSERT( es.eh() != nullptr ) ; if( es.eh() == nullptr ) return ;
55
56 G_DEBUG( "GNet::EventHandlerList::add: " << m_type << "-list: " << "adding " << fd << (m_lock?" (pending)":"") ) ;
57 addImp( m_lock?m_pending_list:m_list , fd , handler , es ) ;
58}
59
60void GNet::EventHandlerList::addImp( List & list , Descriptor fd , EventHandler * handler , ExceptionSink es )
61{
62 namespace imp = EventHandlerListImp ;
63 using Range = std::pair<List::iterator,List::iterator> ;
64 Range range = std::equal_range( list.begin() , list.end() , List::value_type(fd) , imp::fdless() ) ;
65 if( range.first == range.second )
66 list.insert( range.first , List::value_type(fd,handler,es) ) ;
67 else
68 *range.first = List::value_type(fd,handler,es) ;
69}
70
72{
73 G_DEBUG( "GNet::EventHandlerList::remove: " << m_type << "-list: " << "removing " << fd ) ;
74 if( m_lock )
75 {
76 if( disable(m_list,fd) ) m_has_garbage = true ;
77 disable( m_pending_list , fd ) ;
78 }
79 else
80 {
81 remove( m_list , fd ) ;
82 }
83}
84
85bool GNet::EventHandlerList::disable( List & list , Descriptor fd ) noexcept
86{
87 namespace imp = EventHandlerListImp ;
88 using Range = std::pair<List::iterator,List::iterator> ;
89 Range range = std::equal_range( list.begin() , list.end() , List::value_type(fd) , imp::fdless() ) ;
90 const bool found = range.first != range.second ;
91 if( found )
92 {
93 (*range.first).m_event_handler = nullptr ;
94 }
95 return found ;
96}
97
98bool GNet::EventHandlerList::remove( List & list , Descriptor fd ) noexcept
99{
100 namespace imp = EventHandlerListImp ;
101 using Range = std::pair<List::iterator,List::iterator> ;
102 Range range = std::equal_range( list.begin() , list.end() , List::value_type(fd) , imp::fdless() ) ;
103 const bool found = range.first != range.second ;
104 if( found )
105 list.erase( range.first ) ; // noexcept since Value::op=() does not throw
106 return found ;
107}
108
110{
111 disarm( m_list , eh ) ;
112 disarm( m_pending_list , eh ) ;
113}
114
115void GNet::EventHandlerList::disarm( List & list , ExceptionHandler * eh ) noexcept
116{
117 for( auto & value : list )
118 {
119 if( value.m_es.eh() == eh )
120 value.m_es.reset() ;
121 }
122}
123
125{
126 namespace imp = EventHandlerListImp ;
127 using Range = std::pair<List::const_iterator,List::const_iterator> ;
128 Range range = std::equal_range( m_list.begin() , m_list.end() , List::value_type(fd) , imp::fdless() ) ;
129 return range.first == range.second ? Iterator(m_list.end(),m_list.end()) : Iterator(range.first,m_list.end()) ;
130}
131
133{
134 namespace imp = EventHandlerListImp ;
135 using Range = std::pair<List::const_iterator,List::const_iterator> ;
136 Range range = std::equal_range( m_pending_list.begin() , m_pending_list.end() ,
137 List::value_type(fd) , imp::fdless() ) ;
138 if( range.first == range.second )
139 range = std::equal_range( m_list.begin() , m_list.end() , List::value_type(fd) , imp::fdless() ) ;
140 return range.first != range.second && (*range.first).m_event_handler != nullptr ;
141}
142
143std::size_t GNet::EventHandlerList::size() const noexcept
144{
145 return m_list.size() ;
146}
147
148void GNet::EventHandlerList::getHandles( std::vector<HANDLE> & out ) const
149{
150 getHandles( m_list , out ) ;
151 getHandles( m_pending_list , out ) ;
152}
153
154void GNet::EventHandlerList::getHandles( const List & list , std::vector<HANDLE> & out )
155{
156 using iterator = std::vector<HANDLE>::iterator ;
157 using Range = std::pair<iterator,iterator> ;
158 for( const auto & value : list )
159 {
160 HANDLE h = value.m_fd.h() ;
161 if( !h || value.m_event_handler == nullptr ) continue ;
162 Range range = std::equal_range( out.begin() , out.end() , h ) ;
163 if( range.first == range.second )
164 out.insert( range.first , h ) ;
165 }
166}
167
169{
170 m_lock++ ;
171}
172
174{
175 G_ASSERT( m_lock != 0U ) ;
176 m_lock-- ;
177 bool updated = false ;
178 if( m_lock == 0U )
179 {
180 updated = !m_pending_list.empty() || m_has_garbage ;
181 commitPending() ;
182 collectGarbage() ;
183 }
184 return updated ;
185}
186
187void GNet::EventHandlerList::commitPending()
188{
189 const List::iterator end = m_pending_list.end() ;
190 for( auto p = m_pending_list.begin() ; p != end ; ++p )
191 {
192 if( (*p).m_event_handler != nullptr )
193 {
194 G_DEBUG( "GNet::EventHandlerList::commitPending: " << m_type << "-list: " << "commiting " << (*p).m_fd ) ;
195 addImp( m_list , (*p).m_fd , (*p).m_event_handler , (*p).m_es ) ;
196 }
197 }
198 m_pending_list.clear() ;
199}
200
201void GNet::EventHandlerList::collectGarbage()
202{
203 if( m_has_garbage )
204 {
205 m_has_garbage = false ;
206 for( auto p = m_list.begin() ; p != m_list.end() ; )
207 {
208 if( (*p).m_event_handler == nullptr )
209 p = m_list.erase( p ) ;
210 else
211 ++p ;
212 }
213 }
214}
215
216// ==
217
218void GNet::EventHandlerList::Iterator::raiseEvent( void (EventHandler::*method)() )
219{
220 // TODO use std::make_exception_ptr and std::rethrow_exception
221 EventLoggingContext set_logging_context( (m_p!=m_end&&handler()&&es().set()) ? es().esrc() : nullptr ) ;
222 try
223 {
224 if( m_p != m_end && handler() != nullptr )
225 (handler()->*method)() ;
226 }
227 catch( GNet::Done & e ) // (caught separately to avoid requiring rtti)
228 {
229 if( m_p != m_end && es().set() )
230 es().call( e , true ) ; // call onException()
231 else
232 throw ;
233 }
234 catch( std::exception & e )
235 {
236 if( m_p != m_end && es().set() )
237 es().call( e , false ) ; // call onException()
238 else
239 throw ;
240 }
241}
242
243void GNet::EventHandlerList::Iterator::raiseEvent( void (EventHandler::*method)(EventHandler::Reason) ,
244 EventHandler::Reason reason )
245{
246 EventLoggingContext set_logging_context( (m_p!=m_end&&handler()&&es().set()) ? es().esrc() : nullptr ) ;
247 try
248 {
249 if( m_p != m_end && handler() != nullptr )
250 (handler()->*method)( reason ) ;
251 }
252 catch( GNet::Done & e ) // (caught separately to avoid requiring rtti)
253 {
254 if( m_p != m_end && es().set() )
255 es().call( e , true ) ; // call onException()
256 else
257 throw ;
258 }
259 catch( std::exception & e )
260 {
261 if( m_p != m_end && es().set() )
262 es().call( e , false ) ; // call onException()
263 else
264 throw ;
265 }
266}
267
268// ==
269
270GNet::EventHandlerList::Lock::Lock( EventHandlerList & list , bool * invalid_p ) :
271 m_list(list) ,
272 m_invalid_p(invalid_p)
273{
274 m_list.lock() ;
275}
276
277GNet::EventHandlerList::Lock::~Lock()
278{
279 if( m_list.unlock() && m_invalid_p != nullptr )
280 *m_invalid_p = true ;
281}
282
A class that encapsulates a network socket file descriptor and an associated windows event handle.
Definition: gdescriptor.h:37
An exception class that is detected by GNet::EventHandlerList and results in onException() being call...
Definition: gnetdone.h:40
Iterator find(Descriptor fd) const
Finds an entry in the list.
void lock()
To be called at the start of an begin()/end() iteration if the list might change during the iteration...
bool unlock()
Called at the end of a begin()/end() iteration to match a call to lock().
std::size_t size() const noexcept
Returns the size of the list, ignoring any changes while lock()ed.
EventHandlerList(const std::string &type)
Constructor.
void disarm(ExceptionHandler *) noexcept
Resets any matching ExceptionHandler pointers, so exceptions thrown out of the relevant file descript...
bool contains(Descriptor fd) const noexcept
Returns true if the list, taking account of any pending changes, contains the given descriptor.
void getHandles(std::vector< HANDLE > &) const
Adds unique, non-zero Descriptor handles to the given sorted list, including any add()ed while lock()...
void remove(Descriptor fd) noexcept
Removes a file-descriptor from the list.
void add(Descriptor fd, EventHandler *handler, ExceptionSink)
Adds a file-descriptor/handler tuple to the list.
A base class for classes that handle asynchronous events from the event loop.
Definition: geventhandler.h:48
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.
ExceptionHandler * eh() const noexcept
Returns the exception handler pointer.
void call(std::exception &e, bool done)
Calls the exception handler's onException() method.
bool set() const noexcept
Returns true if eh() is not null.
Network classes.
Definition: gdef.h:1115
A functor that compares two file descriptors.
An iterator for GNet::EventHandlerList.
A tuple for GNet::EventHandlerList.