[*] Linux and UNIX QOL

This commit is contained in:
Reece Wilson 2023-08-27 12:41:51 +01:00
parent 9bd05a0752
commit 5cf7533eab
20 changed files with 276 additions and 150 deletions

View File

@ -13,8 +13,10 @@
"deprecated-declarations", "deprecated-declarations",
"c99-designator", "c99-designator",
"reorder-init-list", "reorder-init-list",
"ignored-attributes" "ignored-attributes",
"missing-declarations"
], ],
"notesClang": "missing-declarations was added to slience a macro hack. dw about it. it being '(struct {};)'. all the other options are my desire for c++ to be less stupid of a language, we all know whats unofficially supported and commonly suppressed in large projects.",
"defines": [], "defines": [],
"soft-depends": [ "soft-depends": [
"wxwidgets", "wxwidgets",

View File

@ -374,6 +374,8 @@ namespace Aurora
AuUInt64 uAdaptiveSpinCUCnt16 : 4 { 4 }; AuUInt64 uAdaptiveSpinCUCnt16 : 4 { 4 };
AuUInt64 bPreferFutexRWLock : 1 { true }; AuUInt64 bPreferFutexRWLock : 1 { true };
AuUInt64 bWinXpThrough7BlazeOptimizerPower : 7 { 12 }; // dont worry about it. we dont care about old portables. lets try to make older win32 targets tweak the scheduling in our favor a bit. AuUInt64 bWinXpThrough7BlazeOptimizerPower : 7 { 12 }; // dont worry about it. we dont care about old portables. lets try to make older win32 targets tweak the scheduling in our favor a bit.
AuUInt64 bPreferLinuxPrimitivesFutexNoSpin : 1 { false };
AuUInt64 bPreferUnixPrimitivesNoSpin : 1 { false };
}; };
struct DummyConfig struct DummyConfig

View File

@ -122,7 +122,7 @@ namespace Aurora::Threading::Waitables
AuUInt32 uWaitCount {}; AuUInt32 uWaitCount {};
AuUInt32 uWaiters {}; AuUInt32 uWaiters {};
while (uWaiters = AuAtomicLoad(&this->uAtomicSleeping)) while ((uWaiters = AuAtomicLoad(&this->uAtomicSleeping)))
{ {
AuAtomicAdd(&this->uAtomicState, uWaiters); AuAtomicAdd(&this->uAtomicState, uWaiters);
WakeNOnAddress((const void *)&this->uAtomicState, uWaiters); WakeNOnAddress((const void *)&this->uAtomicState, uWaiters);

View File

@ -19,6 +19,12 @@
#include "ConsoleTTY.Unix.hpp" #include "ConsoleTTY.Unix.hpp"
#endif #endif
#if defined(AURORA_COMPILER_CLANG)
// warning: enumeration values 'kEnumCount' and 'kEnumInvalid' not handled in switch [-Wswitch
#pragma clang diagnostic ignored "-Wswitch"
// Yea, I don't give a shit.
#endif
#include "../ColorConvert.hpp" #include "../ColorConvert.hpp"
namespace Aurora::Console::ConsoleTTY namespace Aurora::Console::ConsoleTTY

View File

@ -9,6 +9,12 @@
#include "Debug.hpp" #include "Debug.hpp"
#include "ErrorStack.hpp" #include "ErrorStack.hpp"
#if defined(AURORA_COMPILER_CLANG)
// warning: ISO C++20 considers use of overloaded operator '==' (with operand types 'AuSPtr<Aurora::Debug::ThreadMessage>' (aka 'ExSharedPtr<Aurora::Debug::ThreadMessage, std::shared_ptr<ThreadMessage>>') and 'AuSPtr<Aurora::Debug::ThreadMessage>' (aka 'ExSharedPtr<Aurora::Debug::ThreadMessage, std::shared_ptr<ThreadMessage>>')) to be ambiguous despite there being a unique best viable function [-Wambiguous-reversed-operator]
#pragma clang diagnostic ignored "-Wambiguous-reversed-operator"
// Yea, I couldn't give less of a nanoshit what some C++20 spec says. Even llvm/clang doesn't care to language police it into a fatal unimplemented compiler condition. So, idc.
#endif
namespace Aurora::Debug namespace Aurora::Debug
{ {
struct ErrorStackAccessor struct ErrorStackAccessor

View File

@ -9,6 +9,12 @@
#include <tomcrypt.h> #include <tomcrypt.h>
#include "AuHashStream.hpp" #include "AuHashStream.hpp"
#if defined(AURORA_COMPILER_CLANG)
// warning: enumeration values 'kEnumCount' and 'kEnumInvalid' not handled in switch [-Wswitch
#pragma clang diagnostic ignored "-Wswitch"
// Yea, I don't give a shit.
#endif
namespace Aurora::Hashing namespace Aurora::Hashing
{ {
#define DIGEST_CHECK(n) SysAssert(n == CRYPT_OK) #define DIGEST_CHECK(n) SysAssert(n == CRYPT_OK)

View File

@ -17,6 +17,12 @@
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#if defined(AURORA_COMPILER_CLANG)
// warning: enumeration values 'kEnumCount' and 'kEnumInvalid' not handled in switch [-Wswitch
#pragma clang diagnostic ignored "-Wswitch"
// Yea, I don't give a shit.
#endif
namespace Aurora::IO namespace Aurora::IO
{ {
AuUInt64 AFileHandle::DupHandle(AuUInt64 uOSHandle, bool bWriteAccess) AuUInt64 AFileHandle::DupHandle(AuUInt64 uOSHandle, bool bWriteAccess)
@ -44,122 +50,7 @@ namespace Aurora::IO
struct UnixIOHandle final : AFileHandle struct UnixIOHandle final : AFileHandle
{ {
bool InitFromPath(HandleCreate create) override bool InitFromPath(HandleCreate create) override;
{
int iFileDescriptor { -1 };
if (create.path.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
if (!FS::EFileOpenModeIsValid(create.eMode))
{
SysPushErrorParam("Invalid open mode");
return false;
}
if (!FS::EFileAdvisoryLockLevelIsValid(create.eAdvisoryLevel))
{
SysPushErrorParam("Invalid lock mode");
return false;
}
auto pathex = FS::NormalizePathRet(create.path);
if (pathex.empty())
{
SysPushErrorMemory();
return false;
}
if (create.bAsyncHandle ||
create.bDirectIOMode)
{
this->bDirectIO = true;
}
switch (create.eMode)
{
case FS::EFileOpenMode::eRead:
{
break;
}
case FS::EFileOpenMode::eReadWrite:
case FS::EFileOpenMode::eWrite:
{
if (create.bAlwaysCreateDirTree)
{
FS::CreateDirectories(pathex, true);
}
if (create.bFailIfNonEmptyFile)
{
if (AuFS::FileExists(pathex.c_str()))
{
SysPushErrorResourceExists("File {} already exists", create.path);
return false;
}
}
break;
}
};
iFileDescriptor = ::open(pathex.c_str(),
(create.eMode == FS::EFileOpenMode::eRead ? O_RDONLY : (O_RDWR | O_CREAT)) | O_CLOEXEC | (this->bDirectIO ? O_DIRECT : 0),
0664);
if (iFileDescriptor < 0)
{
SysPushErrorIO("Couldn't open file: {} ({}) {}", path, pathex, errno);
return false;
}
if (!FS::ApplyDumbAdvisoryLock(iFileDescriptor, create.eAdvisoryLevel))
{
SysPushErrorIO("Couldn't open file: {}. File node (not section) is locked.", path);
::close(iFileDescriptor);
return false;
}
if (create.bFailIfNonEmptyFile)
{
if (FS::PosixGetLength(iFileDescriptor))
{
SysPushErrorResourceExists("File {} already exists", create.path);
::close(iFileDescriptor);
return false;
}
}
switch (create.eMode)
{
case FS::EFileOpenMode::eRead:
{
this->uOSReadHandle = AuUInt64(iFileDescriptor);
break;
}
case FS::EFileOpenMode::eReadWrite:
{
this->uOSWriteHandle = AuUInt64(iFileDescriptor);
this->uOSReadHandle = AuUInt64(iFileDescriptor);
break;
}
case FS::EFileOpenMode::eWrite:
{
this->uOSWriteHandle = AuUInt64(iFileDescriptor);
break;
}
};
this->bIsAsync = create.bAsyncHandle;
this->path = create.path;
return this->IsValid();
}
bool IsFile() override; bool IsFile() override;
bool IsTTY() override; bool IsTTY() override;
@ -170,6 +61,123 @@ namespace Aurora::IO
AuOptionalEx<bool> optIsTTY {}; AuOptionalEx<bool> optIsTTY {};
}; };
bool UnixIOHandle::InitFromPath(HandleCreate create)
{
int iFileDescriptor { -1 };
if (create.path.empty())
{
SysPushErrorArg("Cannot open an IO handle to the provided empty path");
return false;
}
if (!FS::EFileOpenModeIsValid(create.eMode))
{
SysPushErrorParam("Invalid open mode");
return false;
}
if (!FS::EFileAdvisoryLockLevelIsValid(create.eAdvisoryLevel))
{
SysPushErrorParam("Invalid lock mode");
return false;
}
auto pathex = FS::NormalizePathRet(create.path);
if (pathex.empty())
{
SysPushErrorMemory();
return false;
}
if (create.bAsyncHandle ||
create.bDirectIOMode)
{
this->bDirectIO = true;
}
switch (create.eMode)
{
case FS::EFileOpenMode::eRead:
{
break;
}
case FS::EFileOpenMode::eReadWrite:
case FS::EFileOpenMode::eWrite:
{
if (create.bAlwaysCreateDirTree)
{
FS::CreateDirectories(pathex, true);
}
if (create.bFailIfNonEmptyFile)
{
if (AuFS::FileExists(pathex.c_str()))
{
SysPushErrorResourceExists("File {} already exists", create.path);
return false;
}
}
break;
}
};
iFileDescriptor = ::open(pathex.c_str(),
(create.eMode == FS::EFileOpenMode::eRead ? O_RDONLY : (O_RDWR | O_CREAT)) | O_CLOEXEC | (this->bDirectIO ? O_DIRECT : 0),
0664);
if (iFileDescriptor < 0)
{
SysPushErrorIO("Couldn't open file: {} ({}) {}", path, pathex, errno);
return false;
}
if (!FS::ApplyDumbAdvisoryLock(iFileDescriptor, create.eAdvisoryLevel))
{
SysPushErrorIO("Couldn't open file: {}. File node (not section) is locked.", path);
::close(iFileDescriptor);
return false;
}
if (create.bFailIfNonEmptyFile)
{
if (FS::PosixGetLength(iFileDescriptor))
{
SysPushErrorResourceExists("File {} already exists", create.path);
::close(iFileDescriptor);
return false;
}
}
switch (create.eMode)
{
case FS::EFileOpenMode::eRead:
{
this->uOSReadHandle = AuUInt64(iFileDescriptor);
break;
}
case FS::EFileOpenMode::eReadWrite:
{
this->uOSWriteHandle = AuUInt64(iFileDescriptor);
this->uOSReadHandle = AuUInt64(iFileDescriptor);
break;
}
case FS::EFileOpenMode::eWrite:
{
this->uOSWriteHandle = AuUInt64(iFileDescriptor);
break;
}
};
this->bIsAsync = create.bAsyncHandle;
this->path = create.path;
return this->IsValid();
}
bool UnixIOHandle::IsFile() bool UnixIOHandle::IsFile()
{ {
bool bIsFile {}; bool bIsFile {};

View File

@ -10,6 +10,12 @@
#include "FileAdvisory.Unix.hpp" #include "FileAdvisory.Unix.hpp"
#include <sys/file.h> #include <sys/file.h>
#if defined(AURORA_COMPILER_CLANG)
// warning: enumeration values 'kEnumCount' and 'kEnumInvalid' not handled in switch [-Wswitch
#pragma clang diagnostic ignored "-Wswitch"
// Yea, I don't give a shit.
#endif
namespace Aurora::IO::FS namespace Aurora::IO::FS
{ {
bool ApplyDumbAdvisoryLock(int fd, EFileAdvisoryLockLevel level) bool ApplyDumbAdvisoryLock(int fd, EFileAdvisoryLockLevel level)

View File

@ -13,6 +13,12 @@
#include <Source/Time/Time.hpp> #include <Source/Time/Time.hpp>
#include <Source/IO/UNIX/IOSubmit.Linux.hpp> #include <Source/IO/UNIX/IOSubmit.Linux.hpp>
#if defined(AURORA_COMPILER_CLANG)
// warning: ISO C++20 considers use of overloaded operator '!=' (with operand types 'AuSPtr<Aurora::IO::Loop::ILoopSource>' (aka 'ExSharedPtr<Aurora::IO::Loop::ILoopSource, std::shared_ptr<ILoopSource>>') and 'typename tuple_element<0UL, tuple<ExSharedPtr<ILoopSource, shared_ptr<ILoopSource>>, ExSharedPtr<ILoopSourceSubscriber, shared_ptr<ILoopSourceSubscriber>>, ExSharedPtr<ILoopSourceSubscriberEx, shared_ptr<ILoopSourceSubscriberEx>>>>::type' (aka '__type_pack_element<0UL, Aurora::Memory::ExSharedPtr<Aurora::IO::Loop::ILoopSource, std::shared_ptr<Aurora::IO::Loop::ILoopSource>>, Aurora::Memory::ExSharedPtr<Aurora::IO::Loop::ILoopSourceSubscriber, std::shared_ptr<Aurora::IO::Loop::ILoopSourceSubscriber>>, Aurora::Memory::ExSharedPtr<Aurora::IO::Loop::ILoopSourceSubscriberEx, std::shared_ptr<Aurora::IO::Loop::ILoopSourceSubscriberEx>>>')) to be ambiguous despite there being a unique best viable function with non-reversed arguments [-Wambiguous-reversed-operator]
#pragma clang diagnostic ignored "-Wambiguous-reversed-operator"
// Yea, I couldn't give less of a nanoshit what some C++20 spec says. Even llvm/clang doesn't care to language police it into a fatal unimplemented compiler condition. So, idc.
#endif
namespace Aurora::IO::Loop namespace Aurora::IO::Loop
{ {
// On Linux, Loop Queues are glorified eventfd to epoll adapters. // On Linux, Loop Queues are glorified eventfd to epoll adapters.

View File

@ -8,6 +8,12 @@
#include "Networking.hpp" #include "Networking.hpp"
#include "AuIPAddress.hpp" #include "AuIPAddress.hpp"
#if defined(AURORA_COMPILER_CLANG)
// warning: enumeration values 'kEnumCount' and 'kEnumInvalid' not handled in switch [-Wswitch
#pragma clang diagnostic ignored "-Wswitch"
// Yea, I don't give a shit.
#endif
namespace Aurora::IO::Net namespace Aurora::IO::Net
{ {
IPAddress::IPAddress() IPAddress::IPAddress()

View File

@ -8,6 +8,12 @@
#include "Networking.hpp" #include "Networking.hpp"
#include "AuNetEndpoint.hpp" #include "AuNetEndpoint.hpp"
#if defined(AURORA_COMPILER_CLANG)
// warning: enumeration values 'kEnumCount' and 'kEnumInvalid' not handled in switch [-Wswitch
#pragma clang diagnostic ignored "-Wswitch"
// Yea, I don't give a shit.
#endif
namespace Aurora::IO::Net namespace Aurora::IO::Net
{ {
AuUInt8 OptimizeEndpoint(NetEndpoint &ep) AuUInt8 OptimizeEndpoint(NetEndpoint &ep)

View File

@ -34,7 +34,7 @@ namespace Aurora::IO::TLS
{ {
auto index = ret++; auto index = ret++;
} }
while (pCert = pCert->next); while ((pCert = pCert->next));
return ret; return ret;
} }
@ -67,7 +67,7 @@ namespace Aurora::IO::TLS
return AuMakeSharedThrow<View>(AuMemoryViewRead { pCert->raw.p, pCert->raw.len}, AuSharedFromThis()); return AuMakeSharedThrow<View>(AuMemoryViewRead { pCert->raw.p, pCert->raw.len}, AuSharedFromThis());
} }
} }
while (pCert = pCert->next); while ((pCert = pCert->next));
return {}; return {};
} }
@ -92,7 +92,7 @@ namespace Aurora::IO::TLS
return cert; return cert;
} }
} }
while (pCert = pCert->next); while ((pCert = pCert->next));
return {}; return {};
} }

View File

@ -48,7 +48,7 @@ namespace Aurora::Threading::Primitives
{ {
struct timespec tspec; struct timespec tspec;
if (TryLockNoSpin()) if (this->TryLockNoSpin())
{ {
return true; return true;
} }
@ -58,9 +58,19 @@ namespace Aurora::Threading::Primitives
Time::monoabsns2ts(&tspec, uTimeout); Time::monoabsns2ts(&tspec, uTimeout);
} }
if (TryLockHeavy()) if (gRuntimeConfig.threadingConfig.bPreferLinuxPrimitivesFutexNoSpin)
{ {
return true; if (this->TryLockNoSpin())
{
return true;
}
}
else
{
if (this->TryLockHeavy())
{
return true;
}
} }
AuAtomicAdd(&this->uSleeping_, 1u); AuAtomicAdd(&this->uSleeping_, 1u);
@ -143,9 +153,19 @@ namespace Aurora::Threading::Primitives
void LinuxConditionMutex::Lock() void LinuxConditionMutex::Lock()
{ {
if (TryLockHeavy()) if (gRuntimeConfig.threadingConfig.bPreferLinuxPrimitivesFutexNoSpin)
{ {
return; if (this->TryLockNoSpin())
{
return;
}
}
else
{
if (this->TryLockHeavy())
{
return;
}
} }
AuAtomicAdd(&this->uSleeping_, 1u); AuAtomicAdd(&this->uSleeping_, 1u);

View File

@ -14,6 +14,9 @@ namespace Aurora::Threading::Primitives
#pragma pack(push) #pragma pack(push)
#pragma pack(4) #pragma pack(4)
// We're being recycled as a tiny mutex for AuCriticalSection
// The thread cookie of sizeof(void *) under linux/x86_64 is too large
// We can be in parity with other platforms by switching to whichever mutex type is most convenient
struct LinuxConditionMutex final struct LinuxConditionMutex final
{ {
LinuxConditionMutex(); LinuxConditionMutex();

View File

@ -163,7 +163,7 @@ namespace Aurora::Threading::Primitives
AuUInt32 uWaitCount {}; AuUInt32 uWaitCount {};
AuUInt32 uWaiters {}; AuUInt32 uWaiters {};
while (uWaiters = AuAtomicLoad(&this->uSleeping_)) while ((uWaiters = AuAtomicLoad(&this->uSleeping_)))
{ {
AuAtomicAdd(&this->uState_, uWaiters); AuAtomicAdd(&this->uState_, uWaiters);
futex_wake(&this->uState_, uWaiters); futex_wake(&this->uState_, uWaiters);

View File

@ -41,11 +41,11 @@ namespace Aurora::Threading::Primitives
{ {
if (gRuntimeConfig.threadingConfig.bPreferLinuxMutexSpinTryLock) if (gRuntimeConfig.threadingConfig.bPreferLinuxMutexSpinTryLock)
{ {
return TryLockHeavy(); return this->TryLockHeavy();
} }
else else
{ {
return TryLockNoSpin(); return this->TryLockNoSpin();
} }
} }
@ -72,9 +72,19 @@ namespace Aurora::Threading::Primitives
AuUInt64 uStart {}; AuUInt64 uStart {};
AuUInt64 uEnd {}; AuUInt64 uEnd {};
if (this->TryLockHeavy()) if (gRuntimeConfig.threadingConfig.bPreferLinuxPrimitivesFutexNoSpin)
{ {
return true; if (this->TryLockNoSpin())
{
return true;
}
}
else
{
if (this->TryLockHeavy())
{
return true;
}
} }
AuAtomicAdd(&this->dwSleeping_, 1u); AuAtomicAdd(&this->dwSleeping_, 1u);

View File

@ -50,10 +50,17 @@ namespace Aurora::Threading::Primitives
bool MutexImpl::TryLock() bool MutexImpl::TryLock()
{ {
return DoTryIf([=]() if (gRuntimeConfig.threadingConfig.bPreferUnixPrimitivesNoSpin)
{ {
return TryLockNoSpin(); return this->TryLockNoSpin();
}); }
else
{
return return DoTryIf([=]()
{
return this->TryLockNoSpin();
});
}
} }
bool MutexImpl::LockMS(AuUInt64 uTimeout) bool MutexImpl::LockMS(AuUInt64 uTimeout)

View File

@ -42,11 +42,11 @@ namespace Aurora::Threading::Primitives
{ {
if (gRuntimeConfig.threadingConfig.bPreferLinuxSemaphoreSpinTryLock) if (gRuntimeConfig.threadingConfig.bPreferLinuxSemaphoreSpinTryLock)
{ {
return TryLockHeavy(); return this->TryLockHeavy();
} }
else else
{ {
return TryLockNoSpin(); return this->TryLockNoSpin();
} }
} }
@ -84,9 +84,19 @@ namespace Aurora::Threading::Primitives
Time::monoabsns2ts(&tspec, uEnd); Time::monoabsns2ts(&tspec, uEnd);
} }
if (this->TryLockHeavy()) if (gRuntimeConfig.threadingConfig.bPreferLinuxPrimitivesFutexNoSpin)
{ {
return true; if (this->TryLockNoSpin())
{
return true;
}
}
else
{
if (this->TryLockHeavy())
{
return true;
}
} }
AuAtomicAdd<AuUInt32>(&this->dwSleeping_, 1u); AuAtomicAdd<AuUInt32>(&this->dwSleeping_, 1u);
@ -100,7 +110,7 @@ namespace Aurora::Threading::Primitives
{ {
if (Time::SteadyClockNS() >= uEnd) if (Time::SteadyClockNS() >= uEnd)
{ {
AuAtomicSub<AuUInt32>(&this->dwSleeping_, 1u); AuAtomicSub<AuUInt32>(&this->dwSleeping_, 1u);
return false; return false;
} }
@ -152,12 +162,20 @@ namespace Aurora::Threading::Primitives
{ {
AuUInt64 uEnd {}; AuUInt64 uEnd {};
if (this->TryLockNoSpin()) if (gRuntimeConfig.threadingConfig.bPreferLinuxPrimitivesFutexNoSpin)
{ {
return true; if (this->TryLockNoSpin())
{
return true;
}
}
else
{
if (this->TryLockHeavy())
{
return true;
}
} }
errno = 0;
struct timespec tspec; struct timespec tspec;
if (uTimeout != 0) if (uTimeout != 0)
@ -235,6 +253,7 @@ namespace Aurora::Threading::Primitives
void SemaphoreImpl::Unlock(AuUInt16 count) void SemaphoreImpl::Unlock(AuUInt16 count)
{ {
AuAtomicAdd<AuUInt32>(&this->dwState_, count); AuAtomicAdd<AuUInt32>(&this->dwState_, count);
if (AuAtomicLoad(&this->dwSleeping_)) if (AuAtomicLoad(&this->dwSleeping_))
{ {
futex_wake(&this->dwState_, count); futex_wake(&this->dwState_, count);

View File

@ -47,10 +47,17 @@ namespace Aurora::Threading::Primitives
bool SemaphoreImpl::TryLock() bool SemaphoreImpl::TryLock()
{ {
return DoTryIf([=]() if (gRuntimeConfig.threadingConfig.bPreferUnixPrimitivesNoSpin)
{ {
return TryLockNoSpin(); return this->TryLockNoSpin();
}); }
else
{
return return DoTryIf([=]()
{
return this->TryLockNoSpin();
});
}
} }
bool SemaphoreImpl::LockMS(AuUInt64 uTimeout) bool SemaphoreImpl::LockMS(AuUInt64 uTimeout)

View File

@ -36,6 +36,12 @@
}; };
#endif #endif
#if defined(AURORA_COMPILER_CLANG)
// warning: enumeration values 'kEnumCount' and 'kEnumInvalid' not handled in switch [-Wswitch
#pragma clang diagnostic ignored "-Wswitch"
// Yea, I don't give a shit.
#endif
#include "AuSpawnThread.hpp" #include "AuSpawnThread.hpp"
namespace Aurora namespace Aurora