2021-10-02 10:28:49 +00:00
/***
Copyright ( C ) 2021 J Reece Wilson ( a / k / a " Reece " ) . All rights reserved .
File : Loop . Unix . cpp
Date : 2021 - 10 - 1
Author : Reece
* * */
# include <Source/RuntimeInternal.hpp>
# include "Loop.Unix.hpp"
2024-08-02 01:23:09 +00:00
# include "ILoopSourceEx.hpp"
# include <poll.h>
2021-10-02 10:28:49 +00:00
2024-09-07 21:45:34 +00:00
// TODO:
# if defined(AURORA_IS_LINUX_DERIVED)
namespace Aurora : : IO : : UNIX
{
bool LinuxOverlappedYield ( bool bUserspaceOnly ) ;
}
# endif
2022-06-11 23:52:46 +00:00
namespace Aurora : : IO : : Loop
2021-10-02 10:28:49 +00:00
{
2024-10-10 10:03:26 +00:00
extern AuRWRenterableLock gWaitForMultipleALLLock ;
void ResetLoopSourceFalseAlarm ( const AuSPtr < Loop : : ILoopSource > & pLoopSource ) ;
AuList < AuSPtr < ILoopSource > > WaitMultipleOrObjects ( const AuList < AuSPtr < ILoopSource > > & objects , bool bAlert , bool bBreakAPCs , bool bZeroTick , AuUInt32 dwTimeoutReq , bool bAllowOthers , bool & bTooMany )
2024-08-02 01:23:09 +00:00
{
# if defined(AURORA_HAS_NO_POSIX_POLL)
// do the WinNT falllback into loop queue.
//
// loop queues should have long term persistency in a user-space ABI (kqueue) or in the kernel (epoll)
// they may take longer to setup, may seem wasteful to create and throw away, but it's probably the best we can do under BSD kernels.
// why? kevents exist for posix aio objects. they dont use fds.
//
// BSDs with posix AIO based async file transactions, >2.2 linux kernels, macos with corefoundation interop with kqueues, and other posix kernels with alternative aio will probably need
// need to use the win32 64 <= handles path.
bTooMany = true ;
return false ;
# else
AuList < AuSPtr < ILoopSource > > triggered ;
2024-09-28 21:59:37 +00:00
// fallback
AuList < AuSPtr < ILoopSourceEx > > loopSourceExs1 ;
AuList < AuSPtr < ILoopSourceEx > > loopSourceExs21 ;
AuList < pollfd > handleArray1 ;
AuList < AuPair < AuUInt , bool > > handleIndicies1 ;
// 4k~ of stack
AuSPtr < ILoopSourceEx > loopSourceExs12 [ 128 ] ;
AuSPtr < ILoopSourceEx > loopSourceExs22 [ 128 ] ;
pollfd handleArray2 [ 128 ] ;
AuPair < AuUInt , bool > handleIndicies2 [ 128 ] ;
// phead
AuSPtr < ILoopSourceEx > * pLoopSourceExs1 ;
AuSPtr < ILoopSourceEx > * pLoopSourceExs2 ;
pollfd * pHandleArray ;
AuPair < AuUInt , bool > * pHandleIndicies ;
AuUInt32 uHandleOffset , uFDOffset , uHandleOffset2 ;
// length / size
uHandleOffset = 0 ;
uHandleOffset2 = 0 ;
uFDOffset = 0 ;
// default heads
pLoopSourceExs1 = loopSourceExs12 ;
pLoopSourceExs2 = loopSourceExs22 ;
pHandleArray = handleArray2 ;
pHandleIndicies = handleIndicies2 ;
# define HANDLE_PUSH_CHILD_Z(value, aaa, bbb, ccc, ggg, FF) \
{ \
if ( FF > AuArraySize ( aaa ) ) \
{ \
ccc . push_back ( value ) ; \
bbb = ccc . data ( ) ; \
} \
else if ( FF = = AuArraySize ( aaa ) ) \
{ \
ccc . insert ( ccc . begin ( ) , & ggg [ 0 ] , & ggg [ AuArraySize ( aaa ) ] ) ; \
ccc . push_back ( value ) ; \
bbb = ccc . data ( ) ; \
} \
else \
{ \
bbb [ FF ] = value ; \
} \
}
# define HANDLE_PUSH_MAIN2(value) \
HANDLE_PUSH_CHILD_Z ( value , loopSourceExs22 , pLoopSourceExs2 , loopSourceExs21 , pLoopSourceExs2 , uCurHandle )
# define HANDLE_PUSH_MAIN(value, handle) \
HANDLE_PUSH_CHILD_Z ( value , loopSourceExs22 , pLoopSourceExs1 , loopSourceExs1 , pLoopSourceExs1 , handle )
# define HANDLE_PUSH_CHILD(value) \
{ \
auto uCurFD = uFDOffset + + ; \
HANDLE_PUSH_CHILD_Z ( value , loopSourceExs22 , pHandleArray , handleArray1 , handleArray2 , uCurFD ) \
}
# define HANDLE_PUSH_CHILD2(value) \
{ \
HANDLE_PUSH_CHILD_Z ( value , loopSourceExs22 , pHandleIndicies , handleIndicies1 , handleIndicies2 , ( uFDOffset - 1 ) ) \
}
2023-10-21 05:10:33 +00:00
2024-08-02 01:23:09 +00:00
try
{
2024-09-28 21:59:37 +00:00
if ( AuArraySize ( loopSourceExs22 ) < objects . size ( ) )
{
loopSourceExs1 . reserve ( objects . size ( ) ) ;
loopSourceExs21 . reserve ( objects . size ( ) ) ;
handleArray1 . reserve ( objects . size ( ) ) ;
handleIndicies1 . reserve ( triggered . size ( ) ) ;
}
2024-08-02 01:23:09 +00:00
triggered . reserve ( triggered . size ( ) ) ;
for ( const auto & source : objects )
{
if ( ! source )
{
continue ;
}
if ( auto extended = AuDynamicCast < ILoopSourceEx > ( source ) )
{
2024-09-28 21:59:37 +00:00
auto uCurHandle = uHandleOffset + + ;
HANDLE_PUSH_MAIN2 ( extended )
2024-08-02 01:23:09 +00:00
if ( extended - > Singular ( ) )
{
auto handle = extended - > GetHandle ( ) ;
auto handleWrite = extended - > GetWriteHandle ( ) ;
2024-09-28 21:59:37 +00:00
auto i = uHandleOffset2 + + ;
HANDLE_PUSH_MAIN ( extended , i ) ;
2024-08-02 01:23:09 +00:00
if ( handle ! = - 1 )
{
pollfd poll ;
poll . fd = handle ;
poll . events = POLLIN ;
poll . revents = 0 ;
2024-09-28 21:59:37 +00:00
HANDLE_PUSH_CHILD ( poll ) ;
HANDLE_PUSH_CHILD2 ( AuMakePair ( i , false ) ) ;
2024-08-02 01:23:09 +00:00
}
if ( handleWrite ! = - 1 )
{
pollfd poll ;
poll . fd = handleWrite ;
poll . events = POLLOUT ;
poll . revents = 0 ;
2024-09-28 21:59:37 +00:00
HANDLE_PUSH_CHILD ( poll ) ;
HANDLE_PUSH_CHILD2 ( AuMakePair ( i , false ) ) ;
2024-08-02 01:23:09 +00:00
}
}
else
{
auto handles = extended - > GetHandles ( ) ;
auto handlesWrite = extended - > GetWriteHandles ( ) ;
2024-09-28 21:59:37 +00:00
auto i = uHandleOffset2 + + ;
HANDLE_PUSH_MAIN ( extended , i ) ;
2024-08-02 01:23:09 +00:00
for ( const auto & handle : handles )
{
pollfd poll ;
if ( handle = = - 1 )
{
continue ;
}
poll . fd = handle ;
poll . events = POLLIN ;
poll . revents = 0 ;
2024-09-28 21:59:37 +00:00
HANDLE_PUSH_CHILD ( poll ) ;
HANDLE_PUSH_CHILD2 ( AuMakePair ( i , false ) ) ;
2024-08-02 01:23:09 +00:00
}
for ( const auto & handle : handlesWrite )
{
pollfd poll ;
if ( handle = = - 1 )
{
continue ;
}
poll . fd = handle ;
poll . events = POLLOUT ;
poll . revents = 0 ;
2024-09-28 21:59:37 +00:00
HANDLE_PUSH_CHILD ( poll ) ;
HANDLE_PUSH_CHILD2 ( AuMakePair ( i , false ) ) ;
2024-08-02 01:23:09 +00:00
}
}
}
}
}
catch ( . . . )
{
return { } ;
}
2024-09-28 21:59:37 +00:00
// must be 1:1 - do not mess with
for ( AU_ITERATE_N ( i , uHandleOffset ) )
2024-08-02 01:23:09 +00:00
{
2024-09-28 21:59:37 +00:00
pLoopSourceExs2 [ i ] - > OnPresleep ( ) ;
2024-08-02 01:23:09 +00:00
}
AuUInt32 uTimeout { } ;
if ( bZeroTick )
{
uTimeout = 0 ;
}
else if ( ! dwTimeoutReq )
{
uTimeout = - 1 ;
}
else
{
uTimeout = dwTimeoutReq ;
}
int ret ;
do
{
2024-10-10 10:03:26 +00:00
// TODO: bAlert awareness (cannot enter alert sleep yet. no such impl on linux)
// (we need an io_submit posix poll shim + apc event + queue that we dont have yet)
2024-09-28 21:59:37 +00:00
ret = poll ( pHandleArray , uFDOffset , uTimeout ) ;
2024-08-02 01:23:09 +00:00
uTimeout = 0 ;
if ( ret > 0 )
{
2024-10-10 10:03:26 +00:00
AU_LOCK_GLOBAL_GUARD ( gWaitForMultipleALLLock - > AsReadable ( ) ) ;
2024-09-28 21:59:37 +00:00
for ( AU_ITERATE_N ( i , uFDOffset ) )
2024-08-02 01:23:09 +00:00
{
2024-09-28 21:59:37 +00:00
if ( ! pHandleArray [ i ] . revents )
2024-08-02 01:23:09 +00:00
{
continue ;
}
2024-09-28 21:59:37 +00:00
auto uIndex = AuGet < 0 > ( pHandleIndicies [ i ] ) ;
auto bRead = AuGet < 1 > ( pHandleIndicies [ i ] ) ;
auto pLoopSource = AuExchange ( pLoopSourceExs1 [ uIndex ] , nullptr ) ;
2024-08-02 01:23:09 +00:00
if ( ! pLoopSource )
{
2024-09-28 21:59:37 +00:00
// TODO: notify other? special types go here?
2024-08-02 01:23:09 +00:00
continue ;
}
2024-09-28 21:59:37 +00:00
if ( ! pLoopSource - > OnTrigger ( pHandleArray [ i ] . fd ) )
2024-08-02 01:23:09 +00:00
{
2024-09-28 21:59:37 +00:00
pLoopSourceExs1 [ uIndex ] = pLoopSource ;
2024-08-02 01:23:09 +00:00
continue ;
}
triggered . push_back ( pLoopSource ) ;
if ( ! bAllowOthers )
{
break ;
}
}
}
}
while ( ret = = - 1 & &
errno = = EINTR ) ;
2024-09-28 21:59:37 +00:00
// must be 1:1 - do not mess with
for ( AU_ITERATE_N ( i , uHandleOffset ) )
2024-08-02 01:23:09 +00:00
{
2024-09-28 21:59:37 +00:00
pLoopSourceExs2 [ i ] - > OnFinishSleep ( ) ;
2024-08-02 01:23:09 +00:00
}
2024-10-10 10:03:26 +00:00
if ( bAlert )
2024-09-07 21:45:34 +00:00
{
# if defined(AURORA_IS_LINUX_DERIVED)
// Do not syscall after read or select again under Linux
UNIX : : LinuxOverlappedYield ( true ) ;
# else
IOYield ( ) ;
# endif
}
2024-09-28 21:59:37 +00:00
return AuMove ( triggered ) ;
2024-08-02 01:23:09 +00:00
# endif
}
2024-10-10 10:03:26 +00:00
bool WaitMultipleAndObjects_ ( const AuList < AuSPtr < ILoopSource > > & objects , bool bAlert , bool bBreakAPCs , bool bZeroTick , AuUInt64 qwTimeoutAbs , bool & bTooMany , bool & bTimeout )
{
# if defined(AURORA_HAS_NO_POSIX_POLL)
// do the WinNT falllback into loop queue.
//
// loop queues should have long term persistency in a user-space ABI (kqueue) or in the kernel (epoll)
// they may take longer to setup, may seem wasteful to create and throw away, but it's probably the best we can do under BSD kernels.
// why? kevents exist for posix aio objects. they dont use fds.
//
// BSDs with posix AIO based async file transactions, >2.2 linux kernels, macos with corefoundation interop with kqueues, and other posix kernels with alternative aio will probably need
// need to use the win32 64 <= handles path.
bTooMany = true ;
return false ;
# else
AuList < AuSPtr < ILoopSource > > triggered ;
// fallback
AuList < AuSPtr < ILoopSourceEx > > loopSourceExs1 ;
AuList < AuSPtr < ILoopSourceEx > > loopSourceExs21 ;
AuList < pollfd > handleArray1 ;
AuList < pollfd > handleArrayPersistent1 ;
AuList < AuPair < AuUInt , bool > > handleIndicies1 ;
// 4k~ of stack
AuSPtr < ILoopSourceEx > loopSourceExs12 [ 128 ] ;
AuSPtr < ILoopSourceEx > loopSourceExs22 [ 128 ] ;
pollfd handleArray2 [ 128 ] ;
pollfd handleArrayPersistent2 [ 128 ] ;
AuPair < AuUInt , bool > handleIndicies2 [ 128 ] ;
// phead
AuSPtr < ILoopSourceEx > * pLoopSourceExs1 ;
AuSPtr < ILoopSourceEx > * pLoopSourceExs2 ;
pollfd * pHandleArray ;
pollfd * pHandleArrayPersistent ;
AuPair < AuUInt , bool > * pHandleIndicies ;
AuUInt32 uHandleOffset , uFDOffset , uHandleOffset2 ;
// length / size
uHandleOffset = 0 ;
uHandleOffset2 = 0 ;
uFDOffset = 0 ;
// default heads
pLoopSourceExs1 = loopSourceExs12 ;
pLoopSourceExs2 = loopSourceExs22 ;
pHandleArray = handleArray2 ;
pHandleIndicies = handleIndicies2 ;
pHandleArrayPersistent = handleArrayPersistent2 ;
AuUInt32 uTotal { 0 } ;
# define HANDLE_PUSH_CHILD3(value) \
{ \
HANDLE_PUSH_CHILD_Z ( value , loopSourceExs22 , pHandleArrayPersistent , handleArrayPersistent1 , handleArrayPersistent2 , ( uFDOffset - 1 ) ) \
}
try
{
if ( AuArraySize ( loopSourceExs22 ) < objects . size ( ) )
{
loopSourceExs1 . reserve ( objects . size ( ) ) ;
loopSourceExs21 . reserve ( objects . size ( ) ) ;
handleArray1 . reserve ( objects . size ( ) ) ;
handleIndicies1 . reserve ( triggered . size ( ) ) ;
}
triggered . reserve ( triggered . size ( ) ) ;
for ( const auto & source : objects )
{
if ( ! source )
{
continue ;
}
if ( auto extended = AuDynamicCast < ILoopSourceEx > ( source ) )
{
auto uCurHandle = uHandleOffset + + ;
HANDLE_PUSH_MAIN2 ( extended )
if ( extended - > Singular ( ) )
{
auto handle = extended - > GetHandle ( ) ;
auto handleWrite = extended - > GetWriteHandle ( ) ;
auto i = uHandleOffset2 + + ;
HANDLE_PUSH_MAIN ( extended , i ) ;
if ( handle ! = - 1 )
{
pollfd poll ;
poll . fd = handle ;
poll . events = POLLIN ;
poll . revents = 0 ;
HANDLE_PUSH_CHILD ( poll ) ;
HANDLE_PUSH_CHILD2 ( AuMakePair ( i , false ) ) ;
HANDLE_PUSH_CHILD3 ( poll )
}
if ( handleWrite ! = - 1 )
{
pollfd poll ;
poll . fd = handleWrite ;
poll . events = POLLOUT ;
poll . revents = 0 ;
HANDLE_PUSH_CHILD ( poll ) ;
HANDLE_PUSH_CHILD2 ( AuMakePair ( i , false ) ) ;
HANDLE_PUSH_CHILD3 ( poll )
}
}
else
{
auto handles = extended - > GetHandles ( ) ;
auto handlesWrite = extended - > GetWriteHandles ( ) ;
auto i = uHandleOffset2 + + ;
HANDLE_PUSH_MAIN ( extended , i ) ;
for ( const auto & handle : handles )
{
pollfd poll ;
if ( handle = = - 1 )
{
continue ;
}
poll . fd = handle ;
poll . events = POLLIN ;
poll . revents = 0 ;
HANDLE_PUSH_CHILD ( poll ) ;
HANDLE_PUSH_CHILD2 ( AuMakePair ( i , false ) ) ;
HANDLE_PUSH_CHILD3 ( poll )
}
for ( const auto & handle : handlesWrite )
{
pollfd poll ;
if ( handle = = - 1 )
{
continue ;
}
poll . fd = handle ;
poll . events = POLLOUT ;
poll . revents = 0 ;
HANDLE_PUSH_CHILD ( poll ) ;
HANDLE_PUSH_CHILD2 ( AuMakePair ( i , false ) ) ;
HANDLE_PUSH_CHILD3 ( poll )
}
}
}
}
}
catch ( . . . )
{
return { } ;
}
// must be 1:1 - do not mess with
for ( AU_ITERATE_N ( i , uHandleOffset ) )
{
pLoopSourceExs2 [ i ] - > OnPresleep ( ) ;
}
int ret ;
do
{
AuUInt32 uTimeout ;
if ( bZeroTick )
{
uTimeout = 0 ;
}
else if ( qwTimeoutAbs )
{
auto uNow = AuTime : : SteadyClockNS ( ) ;
if ( uNow > = qwTimeoutAbs )
{
uTimeout = 0 ;
}
else
{
uTimeout = AuNSToMS < AuUInt32 > ( qwTimeoutAbs - uNow ) ;
}
}
else
{
uTimeout = - 1 ;
}
ret = poll ( pHandleArray , uFDOffset , uTimeout ) ;
uTimeout = 0 ;
if ( ret > 0 )
{
for ( AU_ITERATE_N ( i , uFDOffset ) )
{
if ( ! pHandleArray [ i ] . revents )
{
continue ;
}
uTotal + = 1 ;
pHandleArrayPersistent [ i ] = pHandleArray [ i ] ;
pHandleArray [ i ] . fd = - 1 ;
}
}
if ( qwTimeoutAbs )
{
auto uNow = AuTime : : SteadyClockNS ( ) ;
if ( uNow > = qwTimeoutAbs )
{
bTimeout = true ;
break ;
}
}
}
while ( ( ret = = - 1 & &
errno = = EINTR ) | | ( uTotal ! = uFDOffset ) ) ;
bool bRet = uTotal = = uFDOffset ;
if ( bRet )
{
AU_LOCK_GLOBAL_GUARD ( gWaitForMultipleALLLock - > AsWritable ( ) ) ;
for ( AU_ITERATE_N ( i , uFDOffset ) )
{
auto uIndex = AuGet < 0 > ( pHandleIndicies [ i ] ) ;
auto bRead = AuGet < 1 > ( pHandleIndicies [ i ] ) ;
auto pLoopSource = AuExchange ( pLoopSourceExs1 [ uIndex ] , nullptr ) ;
if ( ! pLoopSource )
{
continue ;
}
if ( ! pLoopSource - > OnTrigger ( pHandleArrayPersistent [ i ] . fd ) )
{
for ( AU_ITERATE_N ( z , i ) )
{
ResetLoopSourceFalseAlarm ( pLoopSourceExs1 [ z ] ) ;
}
bRet = false ;
break ;
}
}
}
// must be 1:1 - do not mess with
for ( AU_ITERATE_N ( i , uHandleOffset ) )
{
pLoopSourceExs2 [ i ] - > OnFinishSleep ( ) ;
}
return bRet ;
# endif
}
bool WaitMultipleAndObjects ( const AuList < AuSPtr < ILoopSource > > & objects , bool bAlert , bool bBreakAPCs , bool bZeroTick , AuUInt64 qwTimeoutAbs , bool & bTooMany , bool & bTimeout )
{
bool bRet { } ;
do
{
bool bAPCDispatch { } ;
bRet = WaitMultipleAndObjects_ ( objects , bAlert , bBreakAPCs , bZeroTick , qwTimeoutAbs , bTooMany , bTimeout ) ;
if ( bAlert )
{
# if defined(AURORA_IS_LINUX_DERIVED)
// Do not syscall after read or select again under Linux
bAPCDispatch = UNIX : : LinuxOverlappedYield ( true ) ;
# else
bAPCDispatch = IOYield ( ) ;
# endif
}
if ( bBreakAPCs & & bAPCDispatch )
{
break ;
}
}
while ( ! bRet & & ! bTimeout ) ;
return bRet ;
}
2021-10-02 10:28:49 +00:00
}