23#include "geventloophandles.h"
45class GNet::EventLoopImp :
public EventLoop
51 virtual ~EventLoopImp() ;
55 std::string
run()
override ;
57 void quit(
const std::string & )
override ;
59 void disarm( ExceptionHandler * )
noexcept override ;
60 void addRead( Descriptor , EventHandler & , EventState )
override ;
61 void addWrite( Descriptor , EventHandler & , EventState )
override ;
62 void addOther( Descriptor , EventHandler & , EventState )
override ;
63 void dropRead( Descriptor )
noexcept override ;
64 void dropWrite( Descriptor )
noexcept override ;
65 void dropOther( Descriptor )
noexcept override ;
66 void drop( Descriptor )
noexcept override ;
69 EventLoopImp(
const EventLoopImp & ) = delete ;
70 EventLoopImp( EventLoopImp && ) = delete ;
71 EventLoopImp & operator=(
const EventLoopImp & ) = delete ;
72 EventLoopImp & operator=( EventLoopImp && ) = delete ;
78 using Rc = EventLoopHandles::Rc ;
79 using RcType = EventLoopHandles::RcType ;
80 static constexpr long READ_EVENTS = (FD_READ | FD_ACCEPT | FD_OOB) ;
81 static constexpr long WRITE_EVENTS = (FD_WRITE) ;
82 static constexpr long EXCEPTION_EVENTS = (FD_CLOSE | FD_CONNECT) ;
83 using s_type = G::TimeInterval::s_type ;
84 using us_type = G::TimeInterval::us_type ;
89 Library(
const Library & ) = delete ;
90 Library( Library && ) = delete ;
91 Library & operator=(
const Library & ) = delete ;
92 Library & operator=( Library && ) = delete ;
96 enum class ListItemType
103 explicit ListItem( Descriptor fdd ) :
104 m_type(fdd.fd()==INVALID_SOCKET?ListItemType::simple:ListItemType::socket) ,
109 ListItemType m_type {ListItemType::socket} ;
110 SOCKET m_socket {INVALID_SOCKET} ;
111 HANDLE m_handle {HNULL} ;
113 EventState m_es {EventState::Private(),
nullptr,
nullptr} ;
114 EventHandler * m_handler {
nullptr} ;
116 using List = std::vector<ListItem> ;
119 ListItem * find( Descriptor ) ;
120 ListItem & findOrCreate( Descriptor ) ;
121 void fdupdate( Descriptor ,
long ) ;
122 bool fdupdate( Descriptor ,
long , std::nothrow_t ) noexcept ;
123 void handleSimpleEvent( ListItem & ) ;
124 void handleSocketEvent( std::size_t ) ;
125 void checkForOverflow(
const ListItem & ) ;
126 bool collectGarbage() ;
128 static DWORD ms( s_type , us_type ) noexcept ;
129 static bool isValid(
const ListItem & ) ;
130 static bool isInvalid(
const ListItem & ) ;
135 std::unique_ptr<EventLoopHandles> m_handles ;
139 std::string m_quit_reason ;
140 EventState m_es_current {EventState::Private(),
nullptr,
nullptr} ;
145 return std::make_unique<EventLoopImp>() ;
150GNet::EventLoopImp::EventLoopImp() :
155 m_handles = std::make_unique<EventLoopHandles>() ;
158GNet::EventLoopImp::~EventLoopImp()
164 if( m_es_current.eh() == eh )
165 m_es_current.disarm() ;
167 for(
auto & item : m_list )
169 if( item.m_es.eh() == eh )
184 List * list_p = &m_list ;
185 m_handles->update( m_list.size() , [list_p,&i](){return (*list_p)[i++].m_handle;} ) ;
194 std::string quit_reason = m_quit_reason ;
195 m_quit_reason.clear() ;
200void GNet::EventLoopImp::runOnce()
202 G_ASSERT( std::find_if(m_list.begin(),m_list.end(),[](
const ListItem & i){return i.m_handle==HNULL;}) == m_list.end() ) ;
203 G_ASSERT( std::find_if(m_list.begin(),m_list.end(),[](
const ListItem & i){return i.m_events==0L;}) == m_list.end() ) ;
204 G_ASSERT( !m_dirty ) ;
206 EventLoopHandles & handles = *m_handles ;
208 auto rc = handles.wait( ms() ) ;
209 if( rc == RcType::overflow )
213 else if( rc == RcType::timeout )
215 TimerList::instance().doTimeouts() ;
217 else if( rc == RcType::event )
219 ListItem & list_item = m_list[rc.index()] ;
220 if( list_item.m_type == ListItemType::socket )
221 handleSocketEvent( rc.index() ) ;
223 handleSimpleEvent( list_item ) ;
225 else if( rc == RcType::message )
227 std::pair<bool,std::string> quit = GGui::Pump::runToEmpty() ;
230 G_DEBUG(
"GNet::EventLoopImp::run: quit" ) ;
231 m_quit_reason = quit.second ;
235 else if( rc == RcType::failed )
237 throw Error(
"wait failed (" + std::to_string(rc.m_error) +
")" ) ;
245 bool was_dirty = collectGarbage() ;
249 List * list_p = &m_list ;
250 handles.update( m_list.size() , [list_p,&i](){return (*list_p)[i++].m_handle;} , was_dirty ) ;
253bool GNet::EventLoopImp::collectGarbage()
255 bool was_dirty = m_dirty ;
258 m_list.erase( std::remove_if(m_list.begin(),m_list.end(),&EventLoopImp::isInvalid) , m_list.end() ) ;
264DWORD GNet::EventLoopImp::ms()
266 if( TimerList::ptr() )
268 auto pair = TimerList::instance().interval() ;
271 else if( pair.first.s() == 0 && pair.first.us() == 0U )
274 return std::max( DWORD(1) , ms(pair.first.s(),pair.first.us()) ) ;
282DWORD GNet::EventLoopImp::ms( s_type s , us_type us )
noexcept
284 constexpr DWORD dword_max = 0xffffffff ;
285 constexpr DWORD dword_max_1 = dword_max - 1 ;
286 static_assert( INFINITE == dword_max ,
"" ) ;
287 constexpr auto s_max =
static_cast<s_type
>( dword_max/1000U - 1U ) ;
291 static_cast<DWORD
>( (s*1000U) + ((us+999U)/1000U) ) ;
296 GGui::Pump::quit( reason ) ;
305GNet::EventLoopImp::ListItem * GNet::EventLoopImp::find( Descriptor fdd )
307 const HANDLE h = fdd.h() ;
308 auto p = std::find_if( m_list.begin() , m_list.end() , [h](
const ListItem & i){return i.m_handle==h;} ) ;
309 return p == m_list.end() ? nullptr : &(*p) ;
312GNet::EventLoopImp::ListItem & GNet::EventLoopImp::findOrCreate( Descriptor fdd )
314 G_ASSERT( fdd.h() != HNULL ) ;
315 const HANDLE h = fdd.h() ;
316 auto p = std::find_if( m_list.begin() , m_list.end() , [h](
const ListItem & i){return i.m_handle==h;} ) ;
317 if( p == m_list.end() )
319 m_list.emplace_back( fdd ) ;
320 p = m_list.begin() + m_list.size() - 1U ;
328 G_ASSERT( fdd.h() != HNULL ) ;
329 handler.setDescriptor( fdd ) ;
330 ListItem & item = findOrCreate( fdd ) ;
331 checkForOverflow( item ) ;
332 m_dirty |= ( item.m_events == 0L ) ;
333 item.m_events |= READ_EVENTS ;
334 item.m_handler = &handler ;
336 fdupdate( fdd , item.m_events ) ;
341 G_ASSERT( fdd.h() != HNULL ) ;
342 handler.setDescriptor( fdd ) ;
343 ListItem & item = findOrCreate( fdd ) ;
344 checkForOverflow( item ) ;
345 m_dirty |= ( item.m_events == 0L ) ;
346 item.m_events |= WRITE_EVENTS ;
347 item.m_handler = &handler ;
349 fdupdate( fdd , item.m_events ) ;
354 G_ASSERT( fdd.h() != HNULL ) ;
355 handler.setDescriptor( fdd ) ;
356 ListItem & item = findOrCreate( fdd ) ;
357 checkForOverflow( item ) ;
358 m_dirty |= ( item.m_events == 0L ) ;
359 item.m_events |= EXCEPTION_EVENTS ;
360 item.m_handler = &handler ;
362 fdupdate( fdd , item.m_events ) ;
367 ListItem * item = find( fdd ) ;
370 item->m_events &= ~READ_EVENTS ;
371 fdupdate( fdd , item->m_events , std::nothrow ) ;
372 if( item->m_events == 0L )
375 m_handles->onClose( fdd.h() ) ;
382 ListItem * item = find( fdd ) ;
385 item->m_events &= ~WRITE_EVENTS ;
386 fdupdate( fdd , item->m_events , std::nothrow ) ;
387 if( item->m_events == 0L )
390 m_handles->onClose( fdd.h() ) ;
397 ListItem * item = find( fdd ) ;
400 item->m_events &= ~EXCEPTION_EVENTS ;
401 fdupdate( fdd , item->m_events , std::nothrow ) ;
402 if( item->m_events == 0L )
405 m_handles->onClose( fdd.h() ) ;
412 ListItem * item = find( fdd ) ;
415 item->m_events = 0U ;
416 fdupdate( fdd , item->m_events , std::nothrow ) ;
417 item->m_handler = nullptr ;
419 m_handles->onClose( fdd.h() ) ;
423bool GNet::EventLoopImp::isInvalid(
const ListItem & item )
425 return item.m_events == 0L ;
428bool GNet::EventLoopImp::isValid(
const ListItem & item )
430 return item.m_events != 0L ;
433void GNet::EventLoopImp::handleSimpleEvent( ListItem & item )
435 ResetEvent( item.m_handle ) ;
436 m_es_current = item.m_es ;
437 EventEmitter::raiseReadEvent( item.m_handler , m_es_current ) ;
440void GNet::EventLoopImp::handleSocketEvent( std::size_t index )
442 ListItem * item = &m_list[index] ;
444 WSANETWORKEVENTS events_info ;
445 bool e_not_sock = false ;
446 int rc = WSAEnumNetworkEvents( item->m_socket , item->m_handle , &events_info ) ;
448 e_not_sock = WSAGetLastError() == WSAENOTSOCK ;
449 if( rc != 0 && !e_not_sock )
450 throw Error(
"enum-network-events failed" ) ;
452 throw Error(
"enum-network-events failed: not a socket" ) ;
459 long events = events_info.lNetworkEvents ;
460 if( events & READ_EVENTS )
462 m_es_current = item->m_es ;
463 EventEmitter::raiseReadEvent( item->m_handler , m_es_current ) ;
466 if( events & WRITE_EVENTS )
468 item = item ? item : &m_list[index] ;
469 m_es_current = item->m_es ;
470 EventEmitter::raiseWriteEvent( item->m_handler , m_es_current ) ;
473 if( events & EXCEPTION_EVENTS )
475 static_assert( EXCEPTION_EVENTS == (FD_CLOSE|FD_CONNECT) ,
"" ) ;
476 item = item ? item : &m_list[index] ;
477 if( events_info.lNetworkEvents & FD_CONNECT )
479 int e = events_info.iErrorCode[FD_CONNECT_BIT] ;
482 m_es_current = item->m_es ;
483 EventEmitter::raiseOtherEvent( item->m_handler , m_es_current , EventHandler::Reason::failed ) ;
488 int e = events_info.iErrorCode[FD_CLOSE_BIT] ;
489 EventHandler::Reason reason = EventHandler::Reason::other ;
490 if( e == 0 ) reason = EventHandler::Reason::closed ;
491 if( e == WSAENETDOWN ) reason = EventHandler::Reason::down ;
492 if( e == WSAECONNRESET ) reason = EventHandler::Reason::reset ;
493 if( e == WSAECONNABORTED ) reason = EventHandler::Reason::abort ;
494 m_es_current = item->m_es ;
495 EventEmitter::raiseOtherEvent( item->m_handler , m_es_current , reason ) ;
500void GNet::EventLoopImp::checkForOverflow(
const ListItem & item )
502 G_ASSERT( m_handles !=
nullptr ) ;
503 const bool is_new = item.m_events == 0L ;
505 List * list_p = &m_list ;
506 std::function<std::size_t()> count_valid_fn = [list_p](){
508 for(
auto p = list_p->cbegin() ; p != list_p->cend() ; ++p )
510 if( std::next(p) == list_p->cend() || isValid(*p) )
516 if( is_new && m_handles->overflow( m_list.size() , count_valid_fn ) )
523void GNet::EventLoopImp::fdupdate( Descriptor fdd ,
long events )
525 G_ASSERT( fdd.h() != 0 ) ;
526 if( fdd.fd() != INVALID_SOCKET )
528 int rc = WSAEventSelect( fdd.fd() , fdd.h() , events ) ;
530 throw Error(
"wsa-event-select failed" ) ;
534bool GNet::EventLoopImp::fdupdate( Descriptor fdd ,
long events , std::nothrow_t )
noexcept
537 if( fdd.fd() != INVALID_SOCKET )
538 rc = WSAEventSelect( fdd.fd() , fdd.h() , events ) ;
544GNet::EventLoopImp::Library::Library()
547 WORD version = MAKEWORD( 2 , 2 ) ;
548 int rc = WSAStartup( version , &info ) ;
551 throw EventLoopImp::Error(
"winsock startup failure" ) ;
553 if( LOBYTE(info.wVersion) != 2 || HIBYTE(info.wVersion) != 2 )
556 throw EventLoopImp::Error(
"incompatible winsock version" ) ;
560GNet::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 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 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.