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"
24#include "geventloggingcontext.h"
25#include "glog.h"
26#include "gassert.h"
27#include <algorithm>
28
29 namespace GNet
30{
31 namespace EventHandlerListImp /// An implementation namespace for GNet::EventHandlerList.
32 {
33 struct fdless /// A functor that compares two file descriptors.
34 {
35 using Value = GNet::EventHandlerList::Value ;
36 bool operator()( const Value & p1 , const Value & p2 ) noexcept
37 {
38 return p1.m_fd < p2.m_fd ;
39 }
40 } ;
41 }
42}
43
44 GNet::EventHandlerList::EventHandlerList( const std::string & type ) :
45 m_type(type) ,
46 m_lock(0U) ,
47 m_has_garbage(false)
48{
49}
50
51 void GNet::EventHandlerList::add( Descriptor fd , EventHandler * handler , ExceptionSink es )
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
60 void 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
71 void GNet::EventHandlerList::remove( Descriptor fd ) noexcept
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
85 bool 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
98 bool 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
109 void GNet::EventHandlerList::disarm( ExceptionHandler * eh ) noexcept
110{
111 disarm( m_list , eh ) ;
112 disarm( m_pending_list , eh ) ;
113}
114
115 void 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
124 GNet::EventHandlerList::Iterator GNet::EventHandlerList::find( Descriptor fd ) const
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
132 bool GNet::EventHandlerList::contains( Descriptor fd ) const noexcept
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
148 void GNet::EventHandlerList::getHandles( std::vector<HANDLE> & out ) const
149{
150 getHandles( m_list , out ) ;
151 getHandles( m_pending_list , out ) ;
152}
153
154 void 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
168 void GNet::EventHandlerList::lock()
169{
170 m_lock++ ;
171}
172
173 bool GNet::EventHandlerList::unlock()
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
187 void 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
201 void 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
218 void 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
243 void 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
GNet::Descriptor
A class that encapsulates a network socket file descriptor and an associated windows event handle.
Definition: gdescriptor.h:37
GNet::Done
An exception class that is detected by GNet::EventHandlerList and results in onException() being call...
Definition: gnetdone.h:40
GNet::EventHandlerList::find
Iterator find(Descriptor fd) const
Finds an entry in the list.
Definition: geventhandlerlist.cpp:124
GNet::EventHandlerList::lock
void lock()
To be called at the start of an begin()/end() iteration if the list might change during the iteration...
Definition: geventhandlerlist.cpp:168
GNet::EventHandlerList::unlock
bool unlock()
Called at the end of a begin()/end() iteration to match a call to lock().
Definition: geventhandlerlist.cpp:173
GNet::EventHandlerList::size
std::size_t size() const noexcept
Returns the size of the list, ignoring any changes while lock()ed.
Definition: geventhandlerlist.cpp:143
GNet::EventHandlerList::EventHandlerList
EventHandlerList(const std::string &type)
Constructor.
Definition: geventhandlerlist.cpp:44
GNet::EventHandlerList::disarm
void disarm(ExceptionHandler *) noexcept
Resets any matching ExceptionHandler pointers, so exceptions thrown out of the relevant file descript...
Definition: geventhandlerlist.cpp:109
GNet::EventHandlerList::contains
bool contains(Descriptor fd) const noexcept
Returns true if the list, taking account of any pending changes, contains the given descriptor.
Definition: geventhandlerlist.cpp:132
GNet::EventHandlerList::getHandles
void getHandles(std::vector< HANDLE > &) const
Adds unique, non-zero Descriptor handles to the given sorted list, including any add()ed while lock()...
Definition: geventhandlerlist.cpp:148
GNet::EventHandlerList::remove
void remove(Descriptor fd) noexcept
Removes a file-descriptor from the list.
Definition: geventhandlerlist.cpp:71
GNet::EventHandlerList::add
void add(Descriptor fd, EventHandler *handler, ExceptionSink)
Adds a file-descriptor/handler tuple to the list.
Definition: geventhandlerlist.cpp:51
GNet::EventHandler
A base class for classes that handle asynchronous events from the event loop.
Definition: geventhandler.h:48
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::ExceptionSink::eh
ExceptionHandler * eh() const noexcept
Returns the exception handler pointer.
Definition: gexceptionsink.cpp:58
GNet::ExceptionSink::call
void call(std::exception &e, bool done)
Calls the exception handler's onException() method.
Definition: gexceptionsink.cpp:68
GNet::ExceptionSink::set
bool set() const noexcept
Returns true if eh() is not null.
Definition: gexceptionsink.cpp:82
gassert.h
geventhandlerlist.h
geventloggingcontext.h
glog.h
gnetdone.h
GNet
Network classes.
Definition: gdef.h:1115
GNet::EventHandlerListImp::fdless
A functor that compares two file descriptors.
Definition: geventhandlerlist.cpp:34
GNet::EventHandlerList::Iterator
An iterator for GNet::EventHandlerList.
Definition: geventhandlerlist.h:87
GNet::EventHandlerList::Value
A tuple for GNet::EventHandlerList.
Definition: geventhandlerlist.h:79