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 
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 
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 
105 {
106 public:
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 
119  m_list(list)
120 {
121  m_list.lock() ;
122 }
123 
125 {
126  m_list.unlock() ;
127 }
128 
129 // ===
130 
132  m_valid(false) ,
133  m_fdmax(1)
134 {
135 }
136 
138 {
139  return &m_set_external ;
140 }
141 
143 {
144  m_valid = false ;
145 }
146 
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  {
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 
205 {
206  // factory-method pattern
207  return new Select ;
208 }
209 
210 // ===
211 
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 
222 {
223 }
224 
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 
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" ) ;
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 
306 {
307  m_read_list.add( fd , & handler ) ;
308  m_read_set.invalidate() ;
309 }
310 
312 {
313  m_write_list.add( fd , & handler ) ;
314  m_write_set.invalidate() ;
315 }
316 
318 {
319  m_exception_list.add( fd , & handler ) ;
320  m_exception_set.invalidate() ;
321 }
322 
324 {
325  m_read_list.remove( fd ) ;
326  m_read_set.invalidate() ;
327 }
328 
330 {
331  m_write_list.remove( fd ) ;
332  m_write_set.invalidate() ;
333 }
334 
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 
A noncopyable base class (a la boost).
Definition: gnoncopyable.h:35
A concrete implementation of GNet::EventLoop using select() in the implementation.
std::time_t EpochTime
Definition: gdatetime.h:41
Network classes.
EventHandlerList & m_list
int fdmax(int=0) const
Iterator begin() const
Returns a forward iterator.
An abstract base class for a singleton that keeps track of open sockets and their associated handlers...
Definition: geventloop.h:51
void raiseEvents(EventHandlerList &, void(EventHandler::*method)())
virtual void readEvent()
Called for a read event.
Overload discriminator class for TimerList.
Definition: gtimerlist.h:47
A network file descriptor.
Definition: gdescriptor.h:37
A class to manage a boolean flag while in scope.
Definition: gsetter.h:36
unsigned int interval(bool &infinite) const
Returns the interval to the next timer expiry.
Definition: gtimerlist.cpp:141
struct timeval Timeval
void lock()
Called at the start of an iteration which might change the list.
void init(const EventHandlerList &)
static TimerList & instance()
Singleton access. Throws an exception if none.
Definition: gtimerlist.cpp:161
An "fd_set" wrapper type.
SOCKET fd() const
Returns the low-level descriptor.
Definition: gdescriptor.cpp:35
virtual void onException(std::exception &)=0
Called when an exception is thrown out of readEvent(), writeEvent() or exceptionEvent().
virtual void writeEvent()
Called for a write event.
static bool enabled()
Returns true if test features are enabled.
Definition: gtest.cpp:46
void doTimeouts()
Triggers the timeout callbacks of any expired timers.
Definition: gtimerlist.cpp:169
Map::const_iterator Iterator
static Descriptor fd(Iterator i)
Returns the iterator's file descriptor.
virtual void quit(std::string)
Causes run() to return (once the call stack has unwound).
A base class for classes that handle asynchronous socket events.
Definition: geventhandler.h:54
static EventHandler * handler(Iterator i)
Returns the iterator's handler.
virtual void dropWrite(Descriptor fd)
Removes the given event source descriptor from the list of write sources.
#define G_DEBUG(expr)
Definition: glog.h:95
#define G_EXCEPTION(class_name, description)
define as a function rather than a type if optimising for size
Definition: gexception.h:93
Iterator end() const
Returns an end iterator.
virtual void addWrite(Descriptor fd, EventHandler &handler)
Adds the given event source descriptor and associated handler to the write list.
virtual bool init()
Initialises the object.
fd_set * operator()()
virtual bool running() const
Returns true if called from within run().
Lock(EventHandlerList &list)
static EventLoop * create()
A factory method which creates an instance of a derived class on the heap.
A private implementation class used by GNet::Select to lock data structures in the face of reentrancy...
virtual std::string run()
Runs the main event loop.
virtual void addException(Descriptor fd, EventHandler &handler)
Adds the given event source descriptor and associated handler to the exception list.
virtual void dropRead(Descriptor fd)
Removes the given event source descriptor from the list of read sources.
virtual void addRead(Descriptor fd, EventHandler &handler)
Adds the given event source descriptor and associated handler to the read list.
void raiseEvent(EventHandler *, void(EventHandler::*method)())
virtual void dropException(Descriptor fd)
Removes the given event source descriptor from the list of exception sources.
virtual void exceptionEvent()
Called for an exception event.
A class which can be used in the implemention of classes derived from GNet::EventLoop.
Definition: geventhandler.h:96