39class GNet::EventLoopImp :
public EventLoop
42 G_EXCEPTION( Error ,
tx(
"epoll error") )
44 ~EventLoopImp() override ;
47 std::
string run() override ;
48 bool running() const override ;
49 void quit( const
std::
string & ) override ;
50 void quit( const
G::SignalSafe & ) override ;
51 void addRead( Descriptor , EventHandler & , EventState ) override ;
52 void addWrite( Descriptor , EventHandler & , EventState ) override ;
53 void addOther( Descriptor , EventHandler & , EventState ) override ;
54 void dropRead( Descriptor ) noexcept override ;
55 void dropWrite( Descriptor ) noexcept override ;
56 void dropOther( Descriptor ) noexcept override ;
57 void drop( Descriptor ) noexcept override ;
58 void disarm( ExceptionHandler * ) noexcept override ;
61 EventLoopImp( const EventLoopImp & ) = delete ;
62 EventLoopImp( EventLoopImp && ) = delete ;
63 EventLoopImp & operator=( const EventLoopImp & ) = delete ;
64 EventLoopImp & operator=( EventLoopImp && ) = delete ;
69 unsigned int m_events {0U} ;
70 EventHandler * m_handler {
nullptr} ;
71 EventState m_es {EventState::Private(),
nullptr,
nullptr} ;
72 int m_suppress_read {-1} ;
73 int m_suppress_write {-1} ;
74 void update( EventHandler * handler , EventState es )
noexcept { m_handler = handler ; m_es = es ; }
75 void disarm( ExceptionHandler * eh )
noexcept {
if( m_es.eh() == eh ) m_es.disarm() ; }
76 void reset() noexcept { m_handler = nullptr ; }
78 using List = std::vector<ListItem> ;
82 ListItem * find( Descriptor ) noexcept ;
83 ListItem & findOrCreate( Descriptor ) ;
85 static int ms(
unsigned int ,
unsigned int ) noexcept ;
86 static void fdupdate(
int ,
int fd ,
unsigned int old_events ,
unsigned int new_events ) ;
87 static void fdupdate(
int ,
int fd ,
unsigned int old_events ,
unsigned int new_events , std::nothrow_t ) noexcept ;
88 static void fdadd(
int ,
int fd ,
unsigned int events ) ;
89 static void fdmodify(
int ,
int fd ,
unsigned int events ) ;
90 static int fdmodify(
int ,
int fd ,
unsigned int events , std::nothrow_t ) noexcept ;
91 static void fdremove(
int ,
int fd ) noexcept ;
94 std::vector<struct epoll_event> m_wait_events ;
96 bool m_running {
false} ;
98 std::string m_quit_reason ;
102 int m_suppress_seq {0} ;
103 EventState m_es_current ;
110 return std::make_unique<EventLoopImp>() ;
115GNet::EventLoopImp::EventLoopImp() :
116 m_epoll_fd(epoll_create1(EPOLL_CLOEXEC)) ,
117 m_es_current(
EventState::Private(),nullptr,nullptr)
119 if( m_epoll_fd == -1 )
120 throw Error(
"epoll_create" ) ;
121 m_list.reserve( 1024U ) ;
124GNet::EventLoopImp::~EventLoopImp()
126 close( m_epoll_fd ) ;
142 std::string quit_reason = m_quit_reason ;
143 m_quit_reason.clear() ;
148void GNet::EventLoopImp::runOnce()
152 m_wait_events.resize( std::max(std::size_t(1U),m_list.size()) ) ;
155 int timeout_ms = ms() ;
156 m_wait_rc = epoll_wait( m_epoll_fd , m_wait_events.data() , m_wait_events.size() , timeout_ms ) ;
165 if( m_wait_rc == 0 || timeout_ms == 0 )
172 if( m_suppress_seq == std::numeric_limits<int>::max() )
175 std::for_each( m_list.begin() , m_list.end() ,
176 [](ListItem & i_){ i_.m_suppress_read = i_.m_suppress_write = -1 ; } ) ;
180 G_ASSERT( m_index == -1 ) ;
182 auto wait_event = m_wait_events.begin() ;
183 for( m_index = 0 ; m_wait_rc > 0 && m_index < m_wait_rc ; m_index++ , ++wait_event )
185 Descriptor fdd( wait_event->data.fd ) ;
186 if( wait_event->events & EPOLLIN )
188 ListItem * item = find( fdd ) ;
189 if( item && item->m_suppress_read != m_suppress_seq && item->m_handler !=
nullptr )
191 m_es_current = item->m_es ;
195 if( wait_event->events & EPOLLOUT )
197 ListItem * item = find( fdd ) ;
198 if( item && item->m_suppress_write != m_suppress_seq && item->m_handler !=
nullptr )
200 m_es_current = item->m_es ;
207int GNet::EventLoopImp::ms()
const
209 constexpr int infinite = -1 ;
215 else if( pair.first.s() == 0 && pair.first.us() == 0U )
218 return std::max( 1 , ms(pair.first.s(),pair.first.us()) ) ;
226int GNet::EventLoopImp::ms(
unsigned int s ,
unsigned int us )
noexcept
228 constexpr unsigned int s_max =
static_cast<unsigned int>( std::numeric_limits<int>::max()/1000 - 1 ) ;
229 static_assert( s_max > 600 ,
"" ) ;
232 std::numeric_limits<int>::max() :
233 static_cast<int>( (s*1000U) + ((us+999U)/1000U) ) ;
238 m_quit_reason = reason ;
247GNet::EventLoopImp::ListItem * GNet::EventLoopImp::find( Descriptor fdd )
noexcept
249 std::size_t ufd =
static_cast<unsigned int>(fdd.fd()) ;
250 return fdd.fd() >= 0 && ufd < m_list.size() ? &m_list[ufd] : nullptr ;
253GNet::EventLoopImp::ListItem & GNet::EventLoopImp::findOrCreate( Descriptor fdd )
255 ListItem * p = find( fdd ) ;
258 std::size_t ufd =
static_cast<unsigned int>(fdd.fd()) ;
259 m_list.resize( std::max(m_list.size(),ufd+1U) ) ;
268 G_ASSERT( fdd.fd() >= 0 ) ;
269 handler.setDescriptor( fdd ) ;
270 unsigned int new_events = EPOLLIN ;
271 ListItem & item = findOrCreate( fdd ) ;
272 fdupdate( m_epoll_fd , fdd.fd() , item.m_events , item.m_events | new_events ) ;
273 item.m_events |= new_events ;
274 item.update( &handler , es ) ;
279 G_ASSERT( fdd.fd() >= 0 ) ;
280 handler.setDescriptor( fdd ) ;
281 unsigned int new_events = EPOLLOUT ;
282 ListItem & item = findOrCreate( fdd ) ;
283 fdupdate( m_epoll_fd , fdd.fd() , item.m_events , item.m_events | new_events ) ;
284 item.m_events |= new_events ;
285 item.update( &handler , es ) ;
295 ListItem * item = find( fdd ) ;
296 if( item && ( item->m_events & EPOLLIN ) )
298 unsigned int new_events = item->m_events & ~EPOLLIN ;
299 fdupdate( m_epoll_fd , fdd.fd() , item->m_events , new_events , std::nothrow ) ;
300 item->m_events = new_events ;
301 item->m_suppress_read = m_suppress_seq ;
307 ListItem * item = find( fdd ) ;
308 if( item && ( item->m_events & EPOLLOUT ) )
310 unsigned int new_events = item->m_events & ~EPOLLOUT ;
311 fdupdate( m_epoll_fd , fdd.fd() , item->m_events , new_events , std::nothrow ) ;
312 item->m_events = new_events ;
313 item->m_suppress_write = m_suppress_seq ;
324 ListItem * item = find( fdd ) ;
328 fdremove( m_epoll_fd , fdd.fd() ) ;
329 item->m_events = 0U ;
331 item->m_suppress_read = m_suppress_seq ;
332 item->m_suppress_write = m_suppress_seq ;
338 if( m_es_current.eh() == eh )
339 m_es_current.disarm() ;
341 for(
auto & list_item : m_list )
342 list_item.disarm( eh ) ;
347void GNet::EventLoopImp::fdupdate(
int epoll_fd ,
int fd ,
unsigned int old_events ,
unsigned int new_events )
349 if( new_events == 0U )
350 fdremove( epoll_fd , fd ) ;
351 else if( old_events == 0U )
352 fdadd( epoll_fd , fd , new_events ) ;
354 fdmodify( epoll_fd , fd , new_events ) ;
357void GNet::EventLoopImp::fdupdate(
int epoll_fd ,
int fd ,
unsigned int ,
unsigned int new_events , std::nothrow_t )
noexcept
359 if( new_events == 0U )
360 fdremove( epoll_fd , fd ) ;
362 fdmodify( epoll_fd , fd , new_events , std::nothrow ) ;
365void GNet::EventLoopImp::fdadd(
int epoll_fd ,
int fd ,
unsigned int events )
367 epoll_event
event {} ;
369 event.events = events ;
370 int rc = epoll_ctl( epoll_fd , EPOLL_CTL_ADD , fd , &event ) ;
378void GNet::EventLoopImp::fdmodify(
int epoll_fd ,
int fd ,
unsigned int events )
380 epoll_event
event {} ;
382 event.events = events ;
383 int rc = epoll_ctl( epoll_fd , EPOLL_CTL_MOD , fd , &event ) ;
391int GNet::EventLoopImp::fdmodify(
int epoll_fd ,
int fd ,
unsigned int events , std::nothrow_t )
noexcept
393 epoll_event
event {} ;
395 event.events = events ;
396 int rc = epoll_ctl( epoll_fd , EPOLL_CTL_MOD , fd , &event ) ;
398 return rc == -1 ? e : 0 ;
401void GNet::EventLoopImp::fdremove(
int epoll_fd ,
int fd )
noexcept
403 epoll_event
event {} ;
404 epoll_ctl( epoll_fd , EPOLL_CTL_DEL , fd , &event ) ;
static void raiseReadEvent(EventHandler *, EventState &)
Calls readEvent() on the event handler and catches any exceptions and delivers them to the EventState...
static void raiseWriteEvent(EventHandler *, EventState &)
Calls writeEvent() on the event handler and catches any exceptions and delivers them to the EventStat...
virtual void dropWrite(Descriptor fd) noexcept=0
Removes the given event descriptor from the list of write sources.
virtual bool running() const =0
Returns true if called from within run().
virtual void drop(Descriptor fd) noexcept=0
Removes the given event descriptor from the event loop as the EventHandler is being destructed.
static std::unique_ptr< EventLoop > create()
A factory method which creates an instance of a derived class on the heap.
virtual void dropRead(Descriptor fd) noexcept=0
Removes the given event descriptor from the list of read sources.
virtual void disarm(ExceptionHandler *) noexcept=0
Used to prevent the given interface from being used, typically called from the ExceptionHandler destr...
virtual void quit(const std::string &reason)=0
Causes run() to return (once the call stack has unwound).
virtual std::string run()=0
Runs the main event loop.
virtual void addWrite(Descriptor fd, EventHandler &, EventState)=0
Adds the given event source descriptor and associated handler to the write list.
virtual void dropOther(Descriptor fd) noexcept=0
Removes the given event descriptor from the list of other-event sources.
virtual void addRead(Descriptor fd, EventHandler &, EventState)=0
Adds the given event source descriptor and associated handler to the read list.
virtual void addOther(Descriptor fd, EventHandler &, EventState)=0
Adds the given event source descriptor and associated handler to the exception list.
A lightweight object containing an ExceptionHandler pointer, optional ExceptionSource pointer and opt...
std::pair< G::TimeInterval, bool > interval() const
Returns the interval to the first timer expiry.
void doTimeouts()
Triggers the timeout callbacks of any expired timers.
static TimerList & instance()
Singleton access. Throws an exception if none.
static TimerList * ptr() noexcept
Singleton access. Returns nullptr if none.
static std::string strerror(int errno_)
Translates an 'errno' value into a meaningful diagnostic string.
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
A class that sets a simple variable to a particular value at the end of its scope.
An empty structure that is used to indicate a signal-safe, reentrant implementation.
constexpr const char * tx(const char *p) noexcept
A briefer alternative to G::gettext_noop().