2022-08-28 19:02:06 +00:00
/***
Copyright ( C ) 2022 J Reece Wilson ( a / k / a " Reece " ) . All rights reserved .
File : AuNetResolver . Unix . cpp
Date : 2022 - 8 - 26
Author : Reece
* * */
# include "Networking.hpp"
# include "AuNetResolver.Unix.hpp"
# include "AuNetEndpoint.hpp"
# include <Source/IO/Loop/LSSignalCatcher.Linux.hpp>
# include <Source/IO/Loop/LSEvent.hpp>
# if defined(AURORA_IS_LINUX_DERIVED)
# define RESOLVER_IS_VERY_FREETARDED
# endif
namespace Aurora : : IO : : Net
{
static AuList < AuWPtr < NetResolver > > tlsResolvers ;
struct ThreadLocalCaughtCompletion : Loop : : LSSignalCatcher
{
ThreadLocalCaughtCompletion ( NetResolver * pParent ) ;
virtual bool OnTrigger ( AuUInt handle ) override ;
virtual const AuList < AuUInt > & GetHandles ( ) override ;
virtual bool Singular ( ) override ;
private :
AuList < AuUInt > handles_ ;
NetResolver * pParent_ ;
} ;
2023-08-29 00:58:48 +00:00
// this is beyond dumb and inefficient. i think glibc just creates a singular worker thread and resolves each request in a blocking manner with no mutliplexing
// TODO: we really need to make a general purpose AuAsync IO pool and generic resolver replacer/hooking interface for net services.
static auto GetGAIAsyncIOSignal ( )
{
return SIGUSR1 + gRuntimeConfig . linuxConfig . uSignalGAIOWorkerThreadDone ;
}
2022-08-28 19:02:06 +00:00
ThreadLocalCaughtCompletion : : ThreadLocalCaughtCompletion ( NetResolver * pParent ) :
2023-08-29 00:58:48 +00:00
LSSignalCatcher ( { GetGAIAsyncIOSignal ( ) } ) ,
2022-08-28 19:02:06 +00:00
pParent_ ( pParent )
{
handles_ = { this - > handle , AuStaticCast < AuLoop : : LSEvent > ( this - > pParent_ - > pEvent ) - > GetHandle ( ) } ;
}
const AuList < AuUInt > & ThreadLocalCaughtCompletion : : GetHandles ( )
{
return this - > handles_ ;
}
bool ThreadLocalCaughtCompletion : : Singular ( )
{
return false ;
}
bool ThreadLocalCaughtCompletion : : OnTrigger ( AuUInt handle )
{
if ( this - > pParent_ - > CheckAsync ( ) )
{
if ( this - > pParent_ - > pEvent )
{
this - > pParent_ - > pEvent - > Reset ( ) ;
}
return true ;
}
// TODO (Reece) urgent: HACK FIX ME
// Its a slow path so this hack shouldn't matter.
// We specifically optimize it by keeping the event latched until we
// specifically receive a poll update, in which case, reset the event,
// give the shitty glibc freetarded code a chance to catch up, then check.
// This way, we should never miss an update
# if 1
if ( handle = = handles_ [ 1 ] )
{
if ( this - > pParent_ - > pEvent )
{
this - > pParent_ - > pEvent - > Reset ( ) ;
}
for ( int i = 0 ; i < 10 ; i + + )
{
AuThreading : : ContextYield ( ) ;
}
return this - > pParent_ - > CheckAsync ( ) ;
}
# endif
return false ;
}
NetResolver : : NetResolver ( const AuSPtr < INetWorker > & pWorker ) :
pWorker_ ( pWorker )
{
this - > pEvent = AuLoop : : NewLSEvent ( false , false ) ;
}
NetResolver : : NetResolver ( const AuSPtr < INetWorker > & pWorker ,
const AuSPtr < AuAsync : : PromiseCallback < AuList < IPAddress > , NetError > > & pCompletion ) :
pCompletion_ ( pCompletion ) ,
pWorker_ ( pWorker )
{
this - > pEvent = AuLoop : : NewLSEvent ( false , false ) ;
}
NetResolver : : ~ NetResolver ( )
{
}
bool NetResolver : : Start ( )
{
int iInfoEx { 0 } ;
2023-01-26 21:43:19 +00:00
if ( this - > bA & & this - > bAAAA )
{
infoEx . ai_family = AF_UNSPEC ;
}
else if ( this - > bA & & ! this - > bAAAA )
2022-08-28 19:02:06 +00:00
{
infoEx . ai_family = AF_INET ;
}
else if ( this - > bAAAA )
{
infoEx . ai_family = AF_INET6 ;
}
else
{
return false ;
}
sigevent event { 0 } ;
event . sigev_notify = SIGEV_SIGNAL ;
2023-08-29 00:58:48 +00:00
event . sigev_signo = GetGAIAsyncIOSignal ( ) ;
2022-08-28 19:02:06 +00:00
//event._sigev_un._tid = gettid();
auto pQueue = AuMakeShared < ThreadLocalCaughtCompletion > ( this ) ;
if ( ! pQueue )
{
SysPushErrorNet ( " " ) ;
return false ;
}
if ( ! pQueue - > HasValidHandle ( ) )
{
SysPushErrorNet ( " " ) ;
return false ;
}
this - > UpdateTrigger ( pQueue ) ;
auto pBase = & this - > gnuAsyncOperation ;
pBase - > ar_request = ( addrinfo * ) & infoEx ;
pBase - > ar_name = hostname . c_str ( ) ;
AuTryInsert ( tlsResolvers , AuSharedFromThis ( ) ) ;
int iStatus = : : getaddrinfo_a ( GAI_NOWAIT ,
& pBase ,
1 ,
& event ) ;
switch ( iStatus )
{
case EAI_AGAIN :
{
SysPushErrorNet ( " Low Resources | Failed to resolve " ) ;
return false ;
}
case EAI_MEMORY :
{
SysPushErrorNet ( " Out of Memory | Failed to resolve " ) ;
return false ;
}
case EAI_SYSTEM :
{
SysPushErrorNet ( " Invalid | Failed to resolve " ) ;
return false ;
}
}
if ( iStatus ! = 0 )
{
SysPushErrorNet ( " {} " , iStatus ) ;
return false ;
}
return this - > BeginOperation ( AuSharedFromThis ( ) ,
this - > pWorker_ ) ;
}
void NetResolver : : SetCompletion ( const AuSPtr < AuAsync : : PromiseCallback < AuList < IPAddress > , NetError > > & callback )
{
this - > pCompletion_ = callback ;
}
void NetResolver : : OnOverlappedComplete ( )
{
for ( auto itr = tlsResolvers . begin ( ) ; itr ! = tlsResolvers . end ( ) ; )
{
auto pResolver = ( * itr ) . lock ( ) ;
if ( ! pResolver )
{
itr = tlsResolvers . erase ( itr ) ;
continue ;
}
if ( pResolver . get ( ) = = this )
{
itr = tlsResolvers . erase ( itr ) ;
continue ;
}
itr + + ;
}
if ( AuExchange ( this - > bHasCompleted_ , true ) )
{
return ;
}
if ( this - > Unpack ( ) )
{
if ( this - > pCompletion_ )
{
try
{
this - > pCompletion_ - > OnSuccess ( ( void * ) & this - > processedIps_ ) ;
}
catch ( . . . )
{
SysPushErrorCatch ( ) ;
}
}
}
else
{
if ( this - > pCompletion_ )
{
try
{
this - > error_ = ENetworkError : : eAsyncError ;
this - > pCompletion_ - > OnFailure ( ( void * ) & this - > error_ ) ;
}
catch ( . . . )
{
SysPushErrorCatch ( ) ;
}
}
}
}
void NetResolver : : OnOverlappedFailure ( const NetError & error )
{
this - > error_ = error ;
if ( this - > pCompletion_ )
{
try
{
this - > pCompletion_ - > OnFailure ( ( void * ) & this - > error_ ) ;
}
catch ( . . . )
{
SysPushErrorCatch ( ) ;
}
}
this - > bHasCompleted_ = true ;
}
bool NetResolver : : HasFinished ( )
{
return this - > bHasCompleted_ ;
}
bool NetResolver : : CheckAsync ( )
{
int iStatus = : : gai_error ( & this - > gnuAsyncOperation ) ;
return ( ( iStatus = = 0 ) | |
( iStatus = = EAI_CANCELED ) ) ;
}
void NetResolver : : Cancel ( )
{
while ( ! this - > HasComplete ( ) )
{
int iStatus = : : gai_cancel ( & this - > gnuAsyncOperation ) ;
switch ( iStatus )
{
case EAI_CANCELED :
case EAI_ALLDONE :
break ;
case EAI_NOTCANCELED :
{
IOYield ( ) ;
AuThreading : : ContextYield ( ) ;
continue ;
}
}
}
}
const NetError & NetResolver : : GetError ( )
{
return this - > error_ ;
}
bool NetResolver : : Unpack ( )
{
if ( ! CheckAsync ( ) )
{
return false ;
}
for ( auto pItr = this - > gnuAsyncOperation . ar_result ; pItr ; pItr = pItr - > ai_next )
{
NetEndpoint endpoint ;
AuMemcpy ( endpoint . hint , pItr - > ai_addr , pItr - > ai_addrlen ) ;
DeoptimizeEndpoint ( endpoint ) ;
if ( AuExists ( this - > processedIps_ , endpoint . ip ) )
{
continue ;
}
if ( ! AuTryInsert ( this - > processedIps_ , endpoint . ip ) )
{
return false ;
}
}
return true ;
}
static void Annoying ( int )
{
for ( const auto & gawad : tlsResolvers )
{
if ( auto pResolver = gawad . lock ( ) )
{
pResolver - > pEvent - > Set ( ) ;
}
}
}
void InitFreetardedResolver ( )
{
struct sigaction action =
{
. sa_handler = Annoying ,
. sa_flags = SA_ONSTACK
} ;
: : sigemptyset ( & action . sa_mask ) ;
2023-08-29 00:58:48 +00:00
: : sigaction ( GetGAIAsyncIOSignal ( ) , & action , nullptr ) ;
2022-08-28 19:02:06 +00:00
}
}