- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

geventloop_unix.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2013 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 // geventloop_unix.cpp
19 //
20
21 #include "gdef.h"
22 #include "gnet.h"
23 #include "gnoncopyable.h"
24 #include "gevent.h"
25 #include "gexception.h"
26 #include "gstr.h"
27 #include "gsetter.h"
28 #include "gtimer.h"
29 #include "gtest.h"
30 #include "gdebug.h"
31 #include <sys/types.h>
32 #include <sys/time.h>
33
34 typedef struct timeval Timeval ; // std:: ??
35
36 namespace GNet
37 {
38 class Select ;
39 class Lock ;
40 class FdSet ;
41 }
42
46 class GNet::FdSet
47 {
48 public:
49 FdSet() ;
50 void init( const EventHandlerList & ) ;
51 void raiseEvents( EventHandlerList & , void (EventHandler::*method)() ) ;
52 void raiseEvent( EventHandler * , void (EventHandler::*method)() ) ;
53 void invalidate() ;
54 int fdmax( int = 0 ) const ;
55 fd_set * operator()() ;
56 private:
57 bool m_valid ;
58 int m_fdmax ;
59 fd_set m_set_internal ;
60 fd_set m_set_external ;
61 } ;
62
67 class GNet::Select : public GNet::EventLoop , public G::noncopyable
68 {
69 public:
70 G_EXCEPTION( Error , "select() error" ) ;
71 Select() ;
72 virtual ~Select() ;
73 virtual bool init() ;
74 virtual std::string run() ;
75 virtual bool running() const ;
76 virtual void quit( std::string ) ;
77 virtual void addRead( Descriptor fd , EventHandler &handler ) ;
78 virtual void addWrite( Descriptor fd , EventHandler &handler ) ;
79 virtual void addException( Descriptor fd , EventHandler &handler ) ;
80 virtual void dropRead( Descriptor fd ) ;
81 virtual void dropWrite( Descriptor fd ) ;
82 virtual void dropException( Descriptor fd ) ;
83
84 private:
85 void runOnce() ;
86 virtual void setTimeout( G::DateTime::EpochTime t , bool & ) ;
87
88 private:
89 bool m_quit ;
90 std::string m_quit_reason ;
91 bool m_running ;
92 EventHandlerList m_read_list ;
93 FdSet m_read_set ;
94 EventHandlerList m_write_list ;
95 FdSet m_write_set ;
96 EventHandlerList m_exception_list ;
97 FdSet m_exception_set ;
98 } ;
99
104 class GNet::Lock
105 {
106 public:
107 EventHandlerList & m_list ;
108 explicit Lock( EventHandlerList & list ) ;
109 ~Lock() ;
110
111 private:
112 Lock( const Lock & ) ; // not implemented
113 void operator=( const Lock & ) ; // not implemented
114 } ;
115
116 // ===
117
118 GNet::Lock::Lock( EventHandlerList & list ) :
119 m_list(list)
120 {
121 m_list.lock() ;
122 }
123
124 GNet::Lock::~Lock()
125 {
126 m_list.unlock() ;
127 }
128
129 // ===
130
131 GNet::FdSet::FdSet() :
132 m_valid(false) ,
133 m_fdmax(1)
134 {
135 }
136
137 fd_set * GNet::FdSet::operator()()
138 {
139 return &m_set_external ;
140 }
141
142 void GNet::FdSet::invalidate()
143 {
144 m_valid = false ;
145 }
146
147 void GNet::FdSet::init( const EventHandlerList & list )
148 {
149 if( !m_valid )
150 {
151 // copy the event-handler-list into the internal fd-set
152 m_fdmax = 1 ;
153 FD_ZERO( &m_set_internal ) ;
154 const EventHandlerList::Iterator end = list.end() ;
155 for( EventHandlerList::Iterator p = list.begin() ; p != end ; ++p )
156 {
157 Descriptor fd = EventHandlerList::fd( p ) ;
158 FD_SET( fd.fd() , &m_set_internal ) ;
159 if( (fd.fd()+1) > m_fdmax )
160 m_fdmax = (fd.fd()+1) ;
161 }
162 m_valid = true ;
163 }
164 m_set_external = m_set_internal ; // hopefully fast, depending on the definition of fd_set
165 }
166
167 int GNet::FdSet::fdmax( int n ) const
168 {
169 return n > m_fdmax ? n : m_fdmax ;
170 }
171
172 void GNet::FdSet::raiseEvents( EventHandlerList & list , void (EventHandler::*method)() )
173 {
174 // call the event-handler for fds in fd-set which are ISSET()
175
176 GNet::Lock lock( list ) ; // since event handlers may change the list while we iterate
177 const EventHandlerList::Iterator end = list.end() ;
178 for( EventHandlerList::Iterator p = list.begin() ; p != end ; ++p )
179 {
180 Descriptor fd = EventHandlerList::fd( p ) ;
181 if( FD_ISSET( fd.fd() , &m_set_external ) )
182 {
183 EventHandler * h = EventHandlerList::handler( p ) ;
184 if( h != NULL )
185 raiseEvent( h , method ) ;
186 }
187 }
188 }
189
190 void GNet::FdSet::raiseEvent( EventHandler * h , void (EventHandler::*method)() )
191 {
192 try
193 {
194 (h->*method)() ;
195 }
196 catch( std::exception & e )
197 {
198 h->onException( e ) ;
199 }
200 }
201
202 // ===
203
204 GNet::EventLoop * GNet::EventLoop::create()
205 {
206 // factory-method pattern
207 return new Select ;
208 }
209
210 // ===
211
212 GNet::Select::Select() :
213 m_quit(false) ,
214 m_running(false) ,
215 m_read_list("read") ,
216 m_write_list("write") ,
217 m_exception_list("exception")
218 {
219 }
220
221 GNet::Select::~Select()
222 {
223 }
224
225 bool GNet::Select::init()
226 {
227 return true ;
228 }
229
230 std::string GNet::Select::run()
231 {
232 G::Setter setter( m_running ) ;
233 do
234 {
235 runOnce() ;
236 } while( !m_quit ) ;
237 std::string quit_reason = m_quit_reason ;
238 m_quit_reason.clear() ;
239 m_quit = false ;
240 return quit_reason ;
241 }
242
243 bool GNet::Select::running() const
244 {
245 return m_running ;
246 }
247
248 void GNet::Select::quit( std::string reason )
249 {
250 m_quit = true ;
251 m_quit_reason = reason ;
252 }
253
254 void GNet::Select::runOnce()
255 {
256 // build fd-sets from handler lists
257 //
258 m_read_set.init( m_read_list ) ;
259 m_write_set.init( m_write_list ) ;
260 m_exception_set.init( m_exception_list ) ;
261 int n = m_read_set.fdmax( m_write_set.fdmax(m_exception_set.fdmax()) ) ;
262
263 // get a timeout interval() from TimerList
264 //
265 Timeval timeout ;
266 Timeval * timeout_p = NULL ;
267 if( TimerList::instance(TimerList::NoThrow()) != NULL )
268 {
269 bool infinite = false ;
270 timeout.tv_sec = TimerList::instance().interval( infinite ) ;
271 timeout.tv_usec = 0 ; // micro seconds
272 timeout_p = infinite ? NULL : &timeout ;
273 }
274
275 // do the select()
276 //
277 int rc = ::select( n , m_read_set() , m_write_set() , m_exception_set() , timeout_p ) ;
278 if( rc < 0 )
279 throw Error() ;
280
281 // call the event handlers
282 //
283 if( rc == 0 || ( timeout_p != NULL && timeout_p->tv_sec == 0 ) )
284 {
285 G_DEBUG( "GNet::Select::runOnce: select() timeout" ) ;
286 TimerList::instance().doTimeouts() ;
287 }
288 if( rc > 0 )
289 {
290 G_DEBUG( "GNet::Select::runOnce: detected event(s) on " << rc << " fd(s)" ) ;
291 m_read_set.raiseEvents( m_read_list , & EventHandler::readEvent ) ;
292 m_write_set.raiseEvents( m_write_list , & EventHandler::writeEvent ) ;
293 m_exception_set.raiseEvents( m_exception_list , & EventHandler::exceptionEvent ) ;
294 }
295
296 if( G::Test::enabled("slow-event-loop") )
297 {
298 Timeval timeout_slow ;
299 timeout_slow.tv_sec = 0 ;
300 timeout_slow.tv_usec = 100000 ;
301 ::select( 0 , NULL , NULL , NULL , &timeout_slow ) ;
302 }
303 }
304
305 void GNet::Select::addRead( Descriptor fd , EventHandler & handler )
306 {
307 m_read_list.add( fd , & handler ) ;
308 m_read_set.invalidate() ;
309 }
310
311 void GNet::Select::addWrite( Descriptor fd , EventHandler & handler )
312 {
313 m_write_list.add( fd , & handler ) ;
314 m_write_set.invalidate() ;
315 }
316
317 void GNet::Select::addException( Descriptor fd , EventHandler & handler )
318 {
319 m_exception_list.add( fd , & handler ) ;
320 m_exception_set.invalidate() ;
321 }
322
323 void GNet::Select::dropRead( Descriptor fd )
324 {
325 m_read_list.remove( fd ) ;
326 m_read_set.invalidate() ;
327 }
328
329 void GNet::Select::dropWrite( Descriptor fd )
330 {
331 m_write_list.remove( fd ) ;
332 m_write_set.invalidate() ;
333 }
334
335 void GNet::Select::dropException( Descriptor fd )
336 {
337 m_exception_list.remove( fd ) ;
338 m_exception_set.invalidate() ;
339 }
340
341 void GNet::Select::setTimeout( G::DateTime::EpochTime , bool & empty_hint )
342 {
343 // does nothing -- interval() in runOnce() suffices
344 empty_hint = true ;
345 }
346
G::noncopyable
A noncopyable base class (a la boost).
Definition: gnoncopyable.h:35
GNet::Select
A concrete implementation of GNet::EventLoop using select() in the implementation.
Definition: geventloop_unix.cpp:67
G::DateTime::EpochTime
std::time_t EpochTime
Definition: gdatetime.h:41
GNet
Network classes.
GNet::Lock::m_list
EventHandlerList & m_list
Definition: geventloop_unix.cpp:107
GNet::FdSet::invalidate
void invalidate()
Definition: geventloop_unix.cpp:142
GNet::FdSet::fdmax
int fdmax(int=0) const
Definition: geventloop_unix.cpp:167
GNet::EventHandlerList::begin
Iterator begin() const
Returns a forward iterator.
Definition: geventhandler.cpp:59
GNet::EventLoop
An abstract base class for a singleton that keeps track of open sockets and their associated handlers...
Definition: geventloop.h:51
GNet::FdSet::raiseEvents
void raiseEvents(EventHandlerList &, void(EventHandler::*method)())
Definition: geventloop_unix.cpp:172
GNet::EventHandler::readEvent
virtual void readEvent()
Called for a read event.
Definition: geventhandler.cpp:35
GNet::TimerList::NoThrow
Overload discriminator class for TimerList.
Definition: gtimerlist.h:47
GNet::Descriptor
A network file descriptor.
Definition: gdescriptor.h:37
G::Setter
A class to manage a boolean flag while in scope.
Definition: gsetter.h:36
gtest.h
GNet::TimerList::interval
unsigned int interval(bool &infinite) const
Returns the interval to the next timer expiry.
Definition: gtimerlist.cpp:141
GNet::FdSet::FdSet
FdSet()
Definition: geventloop_unix.cpp:131
Timeval
struct timeval Timeval
Definition: geventloop_unix.cpp:34
GNet::EventHandlerList::lock
void lock()
Called at the start of an iteration which might change the list.
Definition: geventhandler.cpp:106
GNet::FdSet::init
void init(const EventHandlerList &)
Definition: geventloop_unix.cpp:147
GNet::Select::Select
Select()
Definition: geventloop_unix.cpp:212
GNet::TimerList::instance
static TimerList & instance()
Singleton access. Throws an exception if none.
Definition: gtimerlist.cpp:161
GNet::FdSet
An "fd_set" wrapper type.
Definition: geventloop_unix.cpp:46
GNet::Descriptor::fd
SOCKET fd() const
Returns the low-level descriptor.
Definition: gdescriptor.cpp:35
GNet::EventHandler::onException
virtual void onException(std::exception &)=0
Called when an exception is thrown out of readEvent(), writeEvent() or exceptionEvent().
gstr.h
gtimer.h
GNet::EventHandler::writeEvent
virtual void writeEvent()
Called for a write event.
Definition: geventhandler.cpp:40
G::Test::enabled
static bool enabled()
Returns true if test features are enabled.
Definition: gtest.cpp:46
GNet::TimerList::doTimeouts
void doTimeouts()
Triggers the timeout callbacks of any expired timers.
Definition: gtimerlist.cpp:169
GNet::EventHandlerList::Iterator
Map::const_iterator Iterator
Definition: geventhandler.h:100
GNet::EventHandlerList::fd
static Descriptor fd(Iterator i)
Returns the iterator's file descriptor.
Definition: geventhandler.h:156
GNet::Select::quit
virtual void quit(std::string)
Causes run() to return (once the call stack has unwound).
Definition: geventloop_unix.cpp:248
gevent.h
GNet::EventHandler
A base class for classes that handle asynchronous socket events.
Definition: geventhandler.h:54
GNet::EventHandlerList::handler
static EventHandler * handler(Iterator i)
Returns the iterator's handler.
Definition: geventhandler.h:162
GNet::Select::dropWrite
virtual void dropWrite(Descriptor fd)
Removes the given event source descriptor from the list of write sources.
Definition: geventloop_unix.cpp:329
G_DEBUG
#define G_DEBUG(expr)
Definition: glog.h:95
gexception.h
gnoncopyable.h
G_EXCEPTION
#define G_EXCEPTION(class_name, description)
define as a function rather than a type if optimising for size
Definition: gexception.h:93
GNet::EventHandlerList::end
Iterator end() const
Returns an end iterator.
Definition: geventhandler.cpp:64
GNet::Select::addWrite
virtual void addWrite(Descriptor fd, EventHandler &handler)
Adds the given event source descriptor and associated handler to the write list.
Definition: geventloop_unix.cpp:311
GNet::Select::init
virtual bool init()
Initialises the object.
Definition: geventloop_unix.cpp:225
GNet::FdSet::operator()
fd_set * operator()()
Definition: geventloop_unix.cpp:137
GNet::Select::running
virtual bool running() const
Returns true if called from within run().
Definition: geventloop_unix.cpp:243
GNet::Select::~Select
virtual ~Select()
Definition: geventloop_unix.cpp:221
GNet::Lock::Lock
Lock(EventHandlerList &list)
Definition: geventloop_unix.cpp:118
GNet::EventLoop::create
static EventLoop * create()
A factory method which creates an instance of a derived class on the heap.
Definition: geventloop_unix.cpp:204
GNet::Lock::~Lock
~Lock()
Definition: geventloop_unix.cpp:124
GNet::Lock
A private implementation class used by GNet::Select to lock data structures in the face of reentrancy...
Definition: geventloop_unix.cpp:104
GNet::Select::run
virtual std::string run()
Runs the main event loop.
Definition: geventloop_unix.cpp:230
GNet::Select::addException
virtual void addException(Descriptor fd, EventHandler &handler)
Adds the given event source descriptor and associated handler to the exception list.
Definition: geventloop_unix.cpp:317
GNet::Select::dropRead
virtual void dropRead(Descriptor fd)
Removes the given event source descriptor from the list of read sources.
Definition: geventloop_unix.cpp:323
gsetter.h
GNet::Select::addRead
virtual void addRead(Descriptor fd, EventHandler &handler)
Adds the given event source descriptor and associated handler to the read list.
Definition: geventloop_unix.cpp:305
GNet::FdSet::raiseEvent
void raiseEvent(EventHandler *, void(EventHandler::*method)())
Definition: geventloop_unix.cpp:190
GNet::Select::dropException
virtual void dropException(Descriptor fd)
Removes the given event source descriptor from the list of exception sources.
Definition: geventloop_unix.cpp:335
gdebug.h
GNet::EventHandler::exceptionEvent
virtual void exceptionEvent()
Called for an exception event.
Definition: geventhandler.cpp:45
GNet::EventHandlerList
A class which can be used in the implemention of classes derived from GNet::EventLoop.
Definition: geventhandler.h:96