23#include "geventloophandles.h"
43 class EventLoopHandles ;
46class GNet::EventLoopImp :
public EventLoop
52 virtual ~EventLoopImp() ;
56 std::string
run()
override ;
58 void quit(
const std::string & )
override ;
60 void disarm( ExceptionHandler * )
noexcept override ;
61 void addRead( Descriptor , EventHandler & , ExceptionSink )
override ;
62 void addWrite( Descriptor , EventHandler & , ExceptionSink )
override ;
63 void addOther( Descriptor , EventHandler & , ExceptionSink )
override ;
64 void dropRead( Descriptor )
noexcept override ;
65 void dropWrite( Descriptor )
noexcept override ;
66 void dropOther( Descriptor )
noexcept override ;
67 void drop( Descriptor )
noexcept override ;
70 EventLoopImp(
const EventLoopImp & ) = delete ;
71 EventLoopImp( EventLoopImp && ) = delete ;
72 EventLoopImp & operator=(
const EventLoopImp & ) = delete ;
73 EventLoopImp & operator=( EventLoopImp && ) = delete ;
79 using Rc = EventLoopHandles::Rc ;
80 using RcType = EventLoopHandles::RcType ;
81 static constexpr long READ_EVENTS = (FD_READ | FD_ACCEPT | FD_OOB) ;
82 static constexpr long WRITE_EVENTS = (FD_WRITE) ;
83 static constexpr long EXCEPTION_EVENTS = (FD_CLOSE | FD_CONNECT) ;
84 using s_type = G::TimeInterval::s_type ;
85 using us_type = G::TimeInterval::us_type ;
90 Library(
const Library & ) = delete ;
91 Library( Library && ) = delete ;
92 Library & operator=(
const Library & ) = delete ;
93 Library & operator=( Library && ) = delete ;
97 enum class ListItemType
104 explicit ListItem( Descriptor fdd ) :
105 m_type(fdd.fd()==INVALID_SOCKET?ListItemType::simple:ListItemType::socket) ,
110 Descriptor fd()
const
112 return Descriptor( m_socket , m_handle ) ;
114 ListItemType m_type{ListItemType::socket} ;
115 SOCKET m_socket {INVALID_SOCKET} ;
116 HANDLE m_handle {HNULL} ;
118 EventEmitter m_read_emitter ;
119 EventEmitter m_write_emitter ;
120 EventEmitter m_other_emitter ;
122 using List = std::vector<ListItem> ;
125 ListItem * find( Descriptor ) ;
126 ListItem & findOrCreate( Descriptor ) ;
127 void fdupdate( Descriptor ,
long ) ;
128 bool fdupdate( Descriptor ,
long , std::nothrow_t ) noexcept ;
129 void handleSimpleEvent( ListItem & ) ;
130 void handleSocketEvent( std::size_t ) ;
131 void checkForOverflow(
const ListItem & ) ;
133 static DWORD ms( s_type , us_type ) noexcept ;
134 static bool isValid(
const ListItem & ) ;
135 static bool isInvalid(
const ListItem & ) ;
140 std::unique_ptr<EventLoopHandles> m_handles ;
144 std::string m_quit_reason ;
149 return std::make_unique<EventLoopImp>() ;
154GNet::EventLoopImp::EventLoopImp() :
159 m_handles = std::make_unique<EventLoopHandles>() ;
162GNet::EventLoopImp::~EventLoopImp()
168 for(
auto & item : m_list )
170 item.m_read_emitter.disarm( p ) ;
171 item.m_write_emitter.disarm( p ) ;
172 item.m_other_emitter.disarm( p ) ;
183 m_handles->init( m_list ) ;
192 std::string quit_reason = m_quit_reason ;
193 m_quit_reason.clear() ;
198void GNet::EventLoopImp::runOnce()
200 EventLoopHandles & handles = *m_handles ;
202 if( handles.overflow( m_list.size() ) )
203 throw Overflow( handles.help(m_list,
false) ) ;
205 auto rc = handles.wait( ms() ) ;
207 if( rc == RcType::timeout )
209 TimerList::instance().doTimeouts() ;
211 else if( rc == RcType::event )
214 std::size_t list_index = handles.shuffle( m_list , rc ) ;
216 ListItem & list_item = m_list[list_index] ;
217 if( list_item.m_type == ListItemType::socket )
218 handleSocketEvent( list_index ) ;
220 handleSimpleEvent( list_item ) ;
222 else if( rc == RcType::message )
224 std::pair<bool,std::string> quit = GGui::Pump::runToEmpty() ;
227 G_DEBUG(
"GNet::EventLoopImp::run: quit" ) ;
228 m_quit_reason = quit.second ;
232 else if( rc == RcType::failed )
234 DWORD e = GetLastError() ;
235 throw Error(
"wait-for-multiple-objects failed" ,
244 bool updated = m_dirty ;
247 m_list.erase( std::remove_if(m_list.begin(),m_list.end(),&EventLoopImp::isInvalid) , m_list.end() ) ;
252 handles.update( m_list , updated , rc ) ;
255DWORD GNet::EventLoopImp::ms()
257 if( TimerList::ptr() )
259 auto pair = TimerList::instance().interval() ;
262 else if( pair.first.s() == 0 && pair.first.us() == 0U )
265 return std::max( DWORD(1) , ms(pair.first.s(),pair.first.us()) ) ;
273DWORD GNet::EventLoopImp::ms( s_type s , us_type us )
noexcept
275 constexpr DWORD dword_max = 0xffffffff ;
276 constexpr DWORD dword_max_1 = dword_max - 1 ;
277 static_assert( INFINITE == dword_max ,
"" ) ;
278 constexpr auto s_max =
static_cast<s_type
>( dword_max/1000U - 1U ) ;
282 static_cast<DWORD
>( (s*1000U) + ((us+999U)/1000U) ) ;
287 GGui::Pump::quit( reason ) ;
296GNet::EventLoopImp::ListItem * GNet::EventLoopImp::find( Descriptor fd )
298 const HANDLE h = fd.h() ;
299 auto p = std::find_if( m_list.begin() , m_list.end() , [h](
const ListItem & i){return i.m_handle==h;} ) ;
300 return p == m_list.end() ? nullptr : &(*p) ;
303GNet::EventLoopImp::ListItem & GNet::EventLoopImp::findOrCreate( Descriptor fd )
305 G_ASSERT( fd.h() != HNULL ) ;
306 const HANDLE h = fd.h() ;
307 auto p = std::find_if( m_list.begin() , m_list.end() , [h](
const ListItem & i){return i.m_handle==h;} ) ;
308 if( p == m_list.end() )
310 m_list.emplace_back( fd ) ;
311 p = m_list.begin() + m_list.size() - 1U ;
319 handler.setDescriptor( fd ) ;
320 ListItem & item = findOrCreate( fd ) ;
321 checkForOverflow( item ) ;
322 m_dirty |= ( item.m_events == 0L ) ;
323 item.m_events |= READ_EVENTS ;
324 item.m_read_emitter = EventEmitter( &handler , es ) ;
325 fdupdate( fd , item.m_events ) ;
330 handler.setDescriptor( fd ) ;
331 ListItem & item = findOrCreate( fd ) ;
332 checkForOverflow( item ) ;
333 m_dirty |= ( item.m_events == 0L ) ;
334 item.m_events |= WRITE_EVENTS ;
335 item.m_write_emitter = EventEmitter( &handler , es ) ;
336 fdupdate( fd , item.m_events ) ;
341 handler.setDescriptor( fd ) ;
342 ListItem & item = findOrCreate( fd ) ;
343 checkForOverflow( item ) ;
344 m_dirty |= ( item.m_events == 0L ) ;
345 item.m_events |= EXCEPTION_EVENTS ;
346 item.m_other_emitter = EventEmitter( &handler , es ) ;
347 fdupdate( fd , item.m_events ) ;
352 ListItem * item = find( fd ) ;
355 item->m_events &= ~READ_EVENTS ;
356 fdupdate( fd , item->m_events , std::nothrow ) ;
357 m_dirty |= ( item->m_events == 0L ) ;
363 ListItem * item = find( fd ) ;
366 item->m_events &= ~WRITE_EVENTS ;
367 fdupdate( fd , item->m_events , std::nothrow ) ;
368 m_dirty |= ( item->m_events == 0L ) ;
374 ListItem * item = find( fd ) ;
377 item->m_events &= ~EXCEPTION_EVENTS ;
378 fdupdate( fd , item->m_events , std::nothrow ) ;
379 m_dirty |= ( item->m_events == 0L ) ;
385 ListItem * item = find( fd ) ;
388 item->m_events = 0U ;
389 fdupdate( fd , item->m_events , std::nothrow ) ;
390 item->m_read_emitter.reset() ;
391 item->m_write_emitter.reset() ;
392 item->m_other_emitter.reset() ;
397bool GNet::EventLoopImp::isInvalid(
const ListItem & item )
399 return item.m_events == 0L ;
402bool GNet::EventLoopImp::isValid(
const ListItem & item )
404 return item.m_events != 0L ;
407void GNet::EventLoopImp::handleSimpleEvent( ListItem & item )
409 ResetEvent( item.m_handle ) ;
410 item.m_read_emitter.raiseReadEvent( Descriptor(INVALID_SOCKET,item.m_handle) ) ;
413void GNet::EventLoopImp::handleSocketEvent( std::size_t index )
415 ListItem * item = &m_list[index] ;
417 WSANETWORKEVENTS events_info ;
418 bool e_not_sock = false ;
419 int rc = WSAEnumNetworkEvents( item->m_socket , item->m_handle , &events_info ) ;
421 e_not_sock = WSAGetLastError() == WSAENOTSOCK ;
422 if( rc != 0 && !e_not_sock )
423 throw Error(
"enum-network-events failed" ) ;
425 throw Error(
"enum-network-events failed: not a socket" ) ;
432 long events = events_info.lNetworkEvents ;
433 if( events & READ_EVENTS )
435 item->m_read_emitter.raiseReadEvent( item->fd() ) ;
438 if( events & WRITE_EVENTS )
440 item = item ? item : &m_list[index] ;
441 item->m_write_emitter.raiseWriteEvent( item->fd() ) ;
444 if( events & EXCEPTION_EVENTS )
446 static_assert( EXCEPTION_EVENTS == (FD_CLOSE|FD_CONNECT) ,
"" ) ;
447 item = item ? item : &m_list[index] ;
448 if( events_info.lNetworkEvents & FD_CONNECT )
450 int e = events_info.iErrorCode[FD_CONNECT_BIT] ;
452 item->m_other_emitter.raiseOtherEvent( item->fd() , EventHandler::Reason::failed ) ;
456 int e = events_info.iErrorCode[FD_CLOSE_BIT] ;
457 EventHandler::Reason reason = EventHandler::Reason::other ;
458 if( e == 0 ) reason = EventHandler::Reason::closed ;
459 if( e == WSAENETDOWN ) reason = EventHandler::Reason::down ;
460 if( e == WSAECONNRESET ) reason = EventHandler::Reason::reset ;
461 if( e == WSAECONNABORTED ) reason = EventHandler::Reason::abort ;
462 item->m_other_emitter.raiseOtherEvent( item->fd() , reason ) ;
467void GNet::EventLoopImp::checkForOverflow(
const ListItem & item )
469 G_ASSERT( m_handles !=
nullptr ) ;
470 const bool is_new = item.m_events == 0L ;
471 if( is_new && m_handles->overflow( m_list , &EventLoopImp::isValid ) )
474 throw Overflow( m_handles->help(m_list,
true) ) ;
478void GNet::EventLoopImp::fdupdate( Descriptor fdd ,
long events )
480 G_ASSERT( fdd.h() != 0 ) ;
481 if( fdd.fd() != INVALID_SOCKET )
483 int rc = WSAEventSelect( fdd.fd() , fdd.h() , events ) ;
485 throw Error(
"wsa-event-select failed" ) ;
489bool GNet::EventLoopImp::fdupdate( Descriptor fdd ,
long events , std::nothrow_t )
noexcept
492 if( fdd.fd() != INVALID_SOCKET )
493 rc = WSAEventSelect( fdd.fd() , fdd.h() , events ) ;
499GNet::EventLoopImp::Library::Library()
502 WORD version = MAKEWORD( 2 , 2 ) ;
503 int rc = WSAStartup( version , &info ) ;
506 throw EventLoopImp::Error(
"winsock startup failure" ) ;
508 if( LOBYTE(info.wVersion) != 2 || HIBYTE(info.wVersion) != 2 )
511 throw EventLoopImp::Error(
"incompatible winsock version" ) ;
515GNet::EventLoopImp::Library::~Library()
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 addOther(Descriptor fd, EventHandler &, ExceptionSink)=0
Adds the given event source descriptor and associated handler to the exception list.
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 void addWrite(Descriptor fd, EventHandler &, ExceptionSink)=0
Adds the given event source descriptor and associated handler to the write list.
virtual std::string run()=0
Runs the main event loop.
virtual void addRead(Descriptor fd, EventHandler &, ExceptionSink)=0
Adds the given event source descriptor and associated handler to the read list.
virtual void dropOther(Descriptor fd) noexcept=0
Removes the given event descriptor from the list of other-event sources.
A class that sets a boolean variable to false at the end of its scope.
An empty structure that is used to indicate a signal-safe, reentrant implementation.
static std::string fromUInt(unsigned int ui)
Converts unsigned int 'ui' to a string.