2015-01-17 17:33:59 -05:00
// NetworkSingleton.cpp
// Implements the cNetworkSingleton class representing the storage for global data pertaining to network API
// such as a list of all connections, all listening sockets and the LibEvent dispatch thread.
# include "Globals.h"
# include "NetworkSingleton.h"
2018-08-28 20:51:25 -04:00
# include "Network.h"
2015-01-17 17:33:59 -05:00
# include <event2/thread.h>
# include <event2/bufferevent.h>
# include <event2/listener.h>
2016-11-07 17:15:07 -05:00
2015-01-17 17:33:59 -05:00
2015-06-17 10:38:00 -04:00
cNetworkSingleton : : cNetworkSingleton ( ) :
m_HasTerminated ( true )
{
}
2017-06-22 15:10:41 -04:00
cNetworkSingleton : : ~ cNetworkSingleton ( ) CAN_THROW
2015-06-17 10:38:00 -04:00
{
// Check that Terminate has been called already:
ASSERT ( m_HasTerminated ) ;
}
cNetworkSingleton & cNetworkSingleton : : Get ( void )
{
static cNetworkSingleton Instance ;
return Instance ;
}
void cNetworkSingleton : : Initialise ( void )
2015-01-17 17:33:59 -05:00
{
2017-06-15 05:03:49 -04:00
// Start the lookup thread
m_LookupThread . Start ( ) ;
2015-01-17 17:33:59 -05:00
// Windows: initialize networking:
# ifdef _WIN32
WSADATA wsaData ;
memset ( & wsaData , 0 , sizeof ( wsaData ) ) ;
int res = WSAStartup ( MAKEWORD ( 2 , 2 ) , & wsaData ) ;
if ( res ! = 0 )
{
int err = WSAGetLastError ( ) ;
LOGWARNING ( " WSAStartup failed: %d, WSAGLE = %d (%s) " , res , err , evutil_socket_error_to_string ( err ) ) ;
exit ( 1 ) ;
}
# endif // _WIN32
// Initialize LibEvent logging:
event_set_log_callback ( LogCallback ) ;
// Initialize threading:
# if defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED)
evthread_use_windows_threads ( ) ;
# elif defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED)
evthread_use_pthreads ( ) ;
# else
# error No threading implemented for EVTHREAD
# endif
// Create the main event_base:
m_EventBase = event_base_new ( ) ;
if ( m_EventBase = = nullptr )
{
LOGERROR ( " Failed to initialize LibEvent. The server will now terminate. " ) ;
abort ( ) ;
}
// Create the event loop thread:
2015-06-17 10:38:00 -04:00
m_HasTerminated = false ;
2016-06-17 10:25:31 -04:00
m_EventLoopThread = std : : thread ( RunEventLoop , this ) ;
2016-06-26 09:51:12 -04:00
m_StartupEvent . Wait ( ) ; // Wait for the LibEvent loop to actually start running (otherwise calling Terminate too soon would hang, see #3228)
2015-01-26 08:46:20 -05:00
}
void cNetworkSingleton : : Terminate ( void )
{
ASSERT ( ! m_HasTerminated ) ;
2017-06-15 05:03:49 -04:00
// Wait for the lookup thread to stop
m_LookupThread . Stop ( ) ;
2015-01-18 06:35:02 -05:00
// Wait for the LibEvent event loop to terminate:
event_base_loopbreak ( m_EventBase ) ;
2015-02-18 16:41:22 -05:00
m_EventLoopThread . join ( ) ;
2015-01-17 17:33:59 -05:00
2018-02-20 12:08:46 -05:00
// Close all open connections:
2015-01-17 17:33:59 -05:00
{
cCSLock Lock ( m_CS ) ;
2018-02-20 12:08:46 -05:00
// Must take copies because Close will modify lists
auto Conns = m_Connections ;
for ( auto & Conn : Conns )
{
Conn - > Close ( ) ;
}
auto Servers = m_Servers ;
for ( auto & Server : Servers )
{
Server - > Close ( ) ;
}
// Closed handles should have removed themself
ASSERT ( m_Connections . empty ( ) ) ;
ASSERT ( m_Servers . empty ( ) ) ;
2015-01-17 17:33:59 -05:00
}
2015-01-18 06:35:02 -05:00
// Free the underlying LibEvent objects:
event_base_free ( m_EventBase ) ;
2015-01-23 17:01:18 -05:00
libevent_global_shutdown ( ) ;
2015-07-23 10:15:21 -04:00
// Set the HasTerminated flag:
// (Only set the flag after everything has been removed, to avoid the random failures in the Google-test, caused by links terminating after this flag was set)
m_HasTerminated = true ;
2015-01-17 17:33:59 -05:00
}
2015-01-18 06:35:02 -05:00
2015-01-17 17:33:59 -05:00
void cNetworkSingleton : : LogCallback ( int a_Severity , const char * a_Msg )
{
switch ( a_Severity )
{
case _EVENT_LOG_DEBUG : LOGD ( " LibEvent: %s " , a_Msg ) ; break ;
case _EVENT_LOG_MSG : LOG ( " LibEvent: %s " , a_Msg ) ; break ;
case _EVENT_LOG_WARN : LOGWARNING ( " LibEvent: %s " , a_Msg ) ; break ;
case _EVENT_LOG_ERR : LOGERROR ( " LibEvent: %s " , a_Msg ) ; break ;
default :
{
LOGWARNING ( " LibEvent: Unknown log severity (%d): %s " , a_Severity , a_Msg ) ;
break ;
}
}
}
void cNetworkSingleton : : RunEventLoop ( cNetworkSingleton * a_Self )
{
2016-06-17 10:25:31 -04:00
auto timer = evtimer_new ( a_Self - > m_EventBase , SignalizeStartup , a_Self ) ;
timeval timeout { } ; // Zero timeout - execute immediately
evtimer_add ( timer , & timeout ) ;
2015-01-17 17:33:59 -05:00
event_base_loop ( a_Self - > m_EventBase , EVLOOP_NO_EXIT_ON_EMPTY ) ;
2017-06-16 17:30:46 -04:00
event_free ( timer ) ;
2015-01-18 06:35:02 -05:00
}
2016-06-17 10:25:31 -04:00
void cNetworkSingleton : : SignalizeStartup ( evutil_socket_t a_Socket , short a_Events , void * a_Self )
{
2018-05-02 03:50:36 -04:00
auto self = static_cast < cNetworkSingleton * > ( a_Self ) ;
2016-06-17 10:25:31 -04:00
ASSERT ( self ! = nullptr ) ;
2016-06-26 09:51:12 -04:00
self - > m_StartupEvent . Set ( ) ;
2016-06-17 10:25:31 -04:00
}
2018-02-20 12:08:46 -05:00
void cNetworkSingleton : : AddLink ( cTCPLinkPtr a_Link )
2015-01-17 17:33:59 -05:00
{
2015-01-26 08:46:20 -05:00
ASSERT ( ! m_HasTerminated ) ;
2015-01-17 17:33:59 -05:00
cCSLock Lock ( m_CS ) ;
m_Connections . push_back ( a_Link ) ;
}
2018-02-20 12:08:46 -05:00
void cNetworkSingleton : : RemoveLink ( const cTCPLink * a_Link )
2015-01-17 17:33:59 -05:00
{
2015-01-26 08:46:20 -05:00
ASSERT ( ! m_HasTerminated ) ;
2015-01-17 17:33:59 -05:00
cCSLock Lock ( m_CS ) ;
for ( auto itr = m_Connections . begin ( ) , end = m_Connections . end ( ) ; itr ! = end ; + + itr )
{
if ( itr - > get ( ) = = a_Link )
{
m_Connections . erase ( itr ) ;
return ;
}
} // for itr - m_Connections[]
}
2018-02-20 12:08:46 -05:00
void cNetworkSingleton : : AddServer ( cServerHandlePtr a_Server )
2015-01-17 17:33:59 -05:00
{
2015-01-26 08:46:20 -05:00
ASSERT ( ! m_HasTerminated ) ;
2015-01-17 17:33:59 -05:00
cCSLock Lock ( m_CS ) ;
m_Servers . push_back ( a_Server ) ;
}
2018-02-20 12:08:46 -05:00
void cNetworkSingleton : : RemoveServer ( const cServerHandle * a_Server )
2015-01-17 17:33:59 -05:00
{
2015-01-26 08:46:20 -05:00
ASSERT ( ! m_HasTerminated ) ;
2015-01-17 17:33:59 -05:00
cCSLock Lock ( m_CS ) ;
for ( auto itr = m_Servers . begin ( ) , end = m_Servers . end ( ) ; itr ! = end ; + + itr )
{
if ( itr - > get ( ) = = a_Server )
{
m_Servers . erase ( itr ) ;
return ;
}
} // for itr - m_Servers[]
}