[*] 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",
"c99-designator",
"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": [],
"soft-depends": [
"wxwidgets",

View File

@ -374,6 +374,8 @@ namespace Aurora
AuUInt64 uAdaptiveSpinCUCnt16 : 4 { 4 };
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 bPreferLinuxPrimitivesFutexNoSpin : 1 { false };
AuUInt64 bPreferUnixPrimitivesNoSpin : 1 { false };
};
struct DummyConfig

View File

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

View File

@ -19,6 +19,12 @@
#include "ConsoleTTY.Unix.hpp"
#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"
namespace Aurora::Console::ConsoleTTY

View File

@ -9,6 +9,12 @@
#include "Debug.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
{
struct ErrorStackAccessor

View File

@ -9,6 +9,12 @@
#include <tomcrypt.h>
#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
{
#define DIGEST_CHECK(n) SysAssert(n == CRYPT_OK)

View File

@ -17,6 +17,12 @@
#include <unistd.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
{
AuUInt64 AFileHandle::DupHandle(AuUInt64 uOSHandle, bool bWriteAccess)
@ -44,122 +50,7 @@ namespace Aurora::IO
struct UnixIOHandle final : AFileHandle
{
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 InitFromPath(HandleCreate create) override;
bool IsFile() override;
bool IsTTY() override;
@ -170,6 +61,123 @@ namespace Aurora::IO
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 bIsFile {};

View File

@ -10,6 +10,12 @@
#include "FileAdvisory.Unix.hpp"
#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
{
bool ApplyDumbAdvisoryLock(int fd, EFileAdvisoryLockLevel level)

View File

@ -13,6 +13,12 @@
#include <Source/Time/Time.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
{
// On Linux, Loop Queues are glorified eventfd to epoll adapters.

View File

@ -8,6 +8,12 @@
#include "Networking.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
{
IPAddress::IPAddress()

View File

@ -8,6 +8,12 @@
#include "Networking.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
{
AuUInt8 OptimizeEndpoint(NetEndpoint &ep)

View File

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

View File

@ -48,7 +48,7 @@ namespace Aurora::Threading::Primitives
{
struct timespec tspec;
if (TryLockNoSpin())
if (this->TryLockNoSpin())
{
return true;
}
@ -57,12 +57,22 @@ namespace Aurora::Threading::Primitives
{
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);
auto state = this->uState_;
@ -143,9 +153,19 @@ namespace Aurora::Threading::Primitives
void LinuxConditionMutex::Lock()
{
if (TryLockHeavy())
if (gRuntimeConfig.threadingConfig.bPreferLinuxPrimitivesFutexNoSpin)
{
return;
if (this->TryLockNoSpin())
{
return;
}
}
else
{
if (this->TryLockHeavy())
{
return;
}
}
AuAtomicAdd(&this->uSleeping_, 1u);

View File

@ -14,6 +14,9 @@ namespace Aurora::Threading::Primitives
#pragma pack(push)
#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
{
LinuxConditionMutex();

View File

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

View File

@ -41,11 +41,11 @@ namespace Aurora::Threading::Primitives
{
if (gRuntimeConfig.threadingConfig.bPreferLinuxMutexSpinTryLock)
{
return TryLockHeavy();
return this->TryLockHeavy();
}
else
{
return TryLockNoSpin();
return this->TryLockNoSpin();
}
}
@ -72,9 +72,19 @@ namespace Aurora::Threading::Primitives
AuUInt64 uStart {};
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);

View File

@ -50,10 +50,17 @@ namespace Aurora::Threading::Primitives
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)

View File

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

View File

@ -47,10 +47,17 @@ namespace Aurora::Threading::Primitives
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)

View File

@ -36,6 +36,12 @@
};
#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"
namespace Aurora