[*] AuOSThread harden set piro and name against race conditions

amend: i didnt want to lock so deep of a callstack, but oh well.
amend: win32 regression
This commit is contained in:
Reece Wilson 2024-06-29 06:12:19 +01:00 committed by Jamie Reece Wilson
parent 27b18c0a37
commit bd034aec31
2 changed files with 146 additions and 75 deletions

View File

@ -418,12 +418,16 @@ namespace Aurora::Threading::Threads
void OSThread::SetName(const AuString &name)
{
AU_LOCK_GUARD(this->metaMutex);
if (gRuntimeConfig.threadingConfig.bNoThreadNames)
{
return;
}
this->name_ = name;
this->uNameIdentity_ = AuFnv1a32Runtime(name.data(), name.size());
this->UpdateName();
}
@ -510,13 +514,7 @@ namespace Aurora::Threading::Threads
auto ret = SpawnThread([this]()
{
while ((!this->handle_) ||
#if defined(INVALID_HANDLE_VALUE)
(this->handle_ == INVALID_HANDLE_VALUE)
#else
0
#endif
)
while (!this->HasValidThreadHandleYet())
{
AuThreading::ContextYield();
}
@ -744,6 +742,13 @@ namespace Aurora::Threading::Threads
void OSThread::UpdateName()
{
AU_LOCK_GUARD(this->metaMutex);
if (!this->HasValidThreadIdYet())
{
return;
}
if (gRuntimeConfig.threadingConfig.bNoThreadNames)
{
return;
@ -782,9 +787,9 @@ namespace Aurora::Threading::Threads
{
info.dwThreadID = pGetThreadId(this->handle_);
}
else if (this->unixThreadId_ == GetCurrentThreadId())
else if (this->optUnixThreadId_.ValueOr(0) == GetCurrentThreadId())
{
info.dwThreadID = this->unixThreadId_;
info.dwThreadID = this->optUnixThreadId_.ValueOr(0);
}
else
{
@ -852,37 +857,45 @@ namespace Aurora::Threading::Threads
AU_DEBUG_MEMCRUNCH;
HandleRegister(this);
#if defined(AURORA_IS_LINUX_DERIVED)
this->unixThreadId_ = gettid();
#elif defined(AURORA_IS_XNU_DERIVED)
this->unixThreadId_ = pthread_getthreadid_np();
#elif defined(AURORA_IS_BSD_DERIVED)
#if __FreeBSD_version < 900031
long lwpid;
thr_self(&lwpid);
this->unixThreadId_ = lwpid;
#elif __FreeBSD_version > 900030
this->unixThreadId_ = pthread_getthreadid_np();
#else
static_assert(false);
#endif
#elif defined(AURORA_HAS_PTHREADS)
this->unixThreadId_ = 0; // !!!!
#endif
#if defined(AURORA_PLATFORM_WIN32)
this->unixThreadId_ = GetCurrentThreadId();
#endif
{
AU_LOCK_GUARD(this->metaMutex);
#if defined(AURORA_IS_LINUX_DERIVED)
this->optUnixThreadId_ = gettid();
#elif defined(AURORA_IS_XNU_DERIVED)
this->optUnixThreadId_ = pthread_getthreadid_np();
#elif defined(AURORA_IS_BSD_DERIVED)
#if __FreeBSD_version < 900031
long lwpid;
thr_self(&lwpid);
this->optUnixThreadId_ = lwpid;
#elif __FreeBSD_version > 900030
this->optUnixThreadId_ = pthread_getthreadid_np();
#else
static_assert(false);
#endif
#elif defined(AURORA_HAS_PTHREADS)
this->optUnixThreadId_ = 0; // !!!!
#endif
#if defined(AURORA_PLATFORM_WIN32)
this->optUnixThreadId_ = GetCurrentThreadId();
#endif
}
if (this->tls_)
{
SetThreadKey(this->tls_);
}
UpdatePrio(this->throttle_, this->prio_);
SetAffinityMask(this->mask_);
AffinityPrioThrottleTickAmendECores();
UpdateName();
{
AU_LOCK_GUARD(this->metaMutex);
UpdatePrio(this->throttle_, this->prio_);
SetAffinityMask(this->mask_);
AffinityPrioThrottleTickAmendECores();
UpdateName();
}
}
static AuHashMap<EThreadPriority, int> kNiceMap
@ -995,10 +1008,59 @@ namespace Aurora::Threading::Threads
AuMakePair(EThreadThrottle::eEfficient, EThreadPriority::ePrioLowest), AuMakePair(DISPATCH_QUEUE_PRIORITY_BACKGROUND, -2)
}
};
#endif
bool OSThread::HasValidThreadIdYet()
{
// set by the new thread context on attachment
return bool(this->optUnixThreadId_);
}
bool OSThread::HasValidThreadHandleYet()
{
// set by thread spawner
return this->handle_ &&
#if defined(INVALID_HANDLE_VALUE)
!(this->handle_ == INVALID_HANDLE_VALUE)
#else
true
#endif
;
}
#if defined(AURORA_IS_LINUX_DERIVED)
static void DoWarnBadOperatingSystem()
{
static AuInitOnce gInitOnce;
gInitOnce.Call([]()
{
AuLogWarn("We will not use RT-Kit to keep your Linux machine alive through XDG_FREEEEeeEEEeEeEEeeeDESKTOP_HOPES_AND_DREAMS, dbus-slop, and systemd-specific daemons");
AuLogWarn("Obligatory sod off System-D, IBM Red Hat, and Lennart Poettering 🤮");
AuLogWarn("Hint: try (1) : append:\n"
"* hard rtprio unlimited\n"
"* soft rtprio unlimited\n"
"* hard priority unlimited\n"
"* soft priority unlimited\n"
"... to /etc/security/limits.conf");
AuLogWarn("Hint: try (2) : in bash, elevate using: '[doas/sudo] prlimit --rtprio=unlimited --pid=$$' before running the main executable");
AuLogWarn("Hint: try (3) : check /etc/systemd/[system/user].conf");
});
}
#endif
void OSThread::UpdatePrio(EThreadThrottle throttle, EThreadPriority prio)
{
AU_LOCK_GUARD(this->metaMutex);
if (!this->HasValidThreadIdYet())
{
this->prio_ = prio;
this->throttle_ = throttle;
return;
}
#if defined(AURORA_IS_MODERNNT_DERIVED)
if (this->handle_ == INVALID_HANDLE_VALUE)
@ -1047,10 +1109,14 @@ namespace Aurora::Threading::Threads
//#elif defined(AURORA_IS_XNU_DERIVED)
#elif defined(AURORA_HAS_PTHREADS)
auto unixThreadId = this->optUnixThreadId_.Value();
sched_param param {};
int policyNonRT {};
int schedA {};
int scheduler {};
#if defined(AURORA_IS_LINUX_DERIVED)
rlimit rl;
#endif
@ -1072,13 +1138,10 @@ namespace Aurora::Threading::Threads
};
#endif
if (!this->handle_)
{
return;
}
// if switch to rtprio scheduling
if (prio == EThreadPriority::ePrioRT)
{
/////////////////////////////////
#if defined(AURORA_IS_POSIX_DERIVED)
#if defined(SCHED_RR)
@ -1087,26 +1150,23 @@ namespace Aurora::Threading::Threads
param.sched_priority = sched_get_priority_max((schedA = SCHED_FIFO));
#endif
if (::sched_setscheduler(this->unixThreadId_, schedA, &param) == 0)
if (::sched_setscheduler(unixThreadId, schedA, &param) == 0)
{
goto gtfo;
}
{
// if switch to rtprio with linux scheduling and rlimit adjustments
#if defined(AURORA_IS_LINUX_DERIVED)
////////////////////////////////////////////////////////////////////
if (getrlimit(RLIMIT_RTPRIO, &rl) == 0)
{
bool bWarnOnce {};
rl.rlim_max = param.sched_priority;
if (rl.rlim_max != RLIM_INFINITY)
{
AuLogWarn("We will not use RT-Kit to keep your Linux machine alive through XDG_FREEEEEEDESKTOP_HOPES_AND_DREAMS");
AuLogWarn("Hint: try (1) : in bash, elevate using: '[doas/sudo] prlimit --rtprio=unlimited --pid=$$' before running the main executable");
AuLogWarn("Hint: try (2) : append '* hard rtprio unlimited' to /etc/security/limits.conf");
AuLogWarn("Hint: try (3) : check /etc/systemd/[system/user].conf");
bWarnOnce = true;
DoWarnBadOperatingSystem();
rl.rlim_cur = rl.rlim_max;
}
else
@ -1117,23 +1177,19 @@ namespace Aurora::Threading::Threads
param.sched_priority = rl.rlim_cur;
#if defined(SCHED_RR)
if (::sched_setscheduler(this->unixThreadId_, SCHED_RR, &param) == 0)
if (::sched_setscheduler(unixThreadId, SCHED_RR, &param) == 0)
{
goto gtfo;
}
#endif
if (::setrlimit(RLIMIT_RTPRIO, &rl) != 0 &&
!bWarnOnce)
if (::setrlimit(RLIMIT_RTPRIO, &rl) != 0)
{
AuLogWarn("We will not use RT-Kit to keep your Linux machine alive through XDG_FREEEEEEDESKTOP_HOPES_AND_DREAMS");
AuLogWarn("Hint: try (1) : in bash, elevate using: '[doas/sudo] prlimit --rtprio=unlimited --pid=$$' before running the main executable");
AuLogWarn("Hint: try (2) : append '* hard rtprio unlimited' to /etc/security/limits.conf");
AuLogWarn("Hint: try (3) : check /etc/systemd/[system/user].conf");
DoWarnBadOperatingSystem();
}
#if defined(SCHED_RR)
if (::sched_setscheduler(this->unixThreadId_, SCHED_RR, &param) == 0)
if (::sched_setscheduler(unixThreadId, SCHED_RR, &param) == 0)
{
goto gtfo;
}
@ -1141,49 +1197,58 @@ namespace Aurora::Threading::Threads
param.sched_priority = sched_get_priority_max(SCHED_FIFO);
if (::sched_setscheduler(this->unixThreadId_, SCHED_FIFO, &param) == 0)
if (::sched_setscheduler(unixThreadId, SCHED_FIFO, &param) == 0)
{
goto gtfo;
}
}
// else if switch to posix RTPRIO with posix SCHED_FIFO + sched_setscheduler
else
#endif
{
////////////////////////////////////////////////////////////////////////////
param.sched_priority = sched_get_priority_max(SCHED_FIFO);
if (::sched_setscheduler(this->unixThreadId_, SCHED_FIFO, &param) == 0)
if (::sched_setscheduler(unixThreadId, SCHED_FIFO, &param) == 0)
{
goto gtfo;
}
}
}
// !defined(AURORA_IS_POSIX_DERIVED) with pthreads
#else
#if defined(SCHED_RR)
param.sched_priority = sched_get_priority_min(SCHED_RR);
if (pthread_setschedparam(this->handle_, SCHED_RR, &param) == 0)
{
goto gtfo;
}
#endif
#if defined(SCHED_RR)
param.sched_priority = sched_get_priority_min(SCHED_RR);
param.sched_priority = GetPrioLevel(SCHED_FIFO, prio);
// set SCHED_RR with pthreads, if available
if (pthread_setschedparam(this->handle_, SCHED_RR, &param) == 0)
{
goto gtfo;
}
if (pthread_setschedparam(this->handle_, SCHED_FIFO, &param) == 0)
{
goto gtfo;
}
#endif
param.sched_priority = GetPrioLevel(SCHED_FIFO, prio);
// set SCHED_FIFO to the highest prio level with pthreads, if available
if (pthread_setschedparam(this->handle_, SCHED_FIFO, &param) == 0)
{
goto gtfo;
}
#endif
// fall through on error
}
// if linux and the next throttle type requires shifting down to SCHED_IDLE scheduling
#if defined(AURORA_IS_LINUX_DERIVED)
else if (throttle == EThreadThrottle::eEfficient)
{
///////////////////////////////////////////////////////////////////////////////////////
param.sched_priority = GetPrioLevel(SCHED_IDLE, prio);
if (::sched_setscheduler(this->unixThreadId_, SCHED_IDLE, &param) == 0)
if (::sched_setscheduler(unixThreadId, SCHED_IDLE, &param) == 0)
{
goto gtfo;
}
@ -1191,11 +1256,14 @@ namespace Aurora::Threading::Threads
// fail
return;
}
// else if restore to regular scheduling condition
else if (this->throttle_ == EThreadThrottle::eEfficient ||
this->prio_ == EThreadPriority::ePrioRT)
#else
else if (this->prio_ == EThreadPriority::ePrioRT)
#endif
//////////////////////////////////////////////////
{
#if defined(AURORA_IS_POSIX_DERIVED)
policyNonRT =
@ -1206,8 +1274,7 @@ namespace Aurora::Threading::Threads
#endif
param.sched_priority = GetPrioLevel(policyNonRT, prio);
if (::sched_setscheduler(this->unixThreadId_, policyNonRT, &param) == 0)
if (::sched_setscheduler(unixThreadId, policyNonRT, &param) == 0)
{
goto gtfo;
}
@ -1217,10 +1284,10 @@ namespace Aurora::Threading::Threads
}
{
scheduler = ::sched_getscheduler(this->unixThreadId_);
scheduler = ::sched_getscheduler(unixThreadId);
param.sched_priority = GetPrioLevel(scheduler, prio);
if (::sched_setscheduler(this->unixThreadId_, scheduler, &param) == 0)
if (::sched_setscheduler(unixThreadId, scheduler, &param) == 0)
{
goto gtfo;
}
@ -1230,6 +1297,7 @@ namespace Aurora::Threading::Threads
AuLogWarn("Couldn't set affinity");
#endif
// epilogue
gtfo:
#if defined(AURORA_IS_LINUX_DERIVED) || defined(AURORA_IS_BSD_DERIVED)
switch (throttle)

View File

@ -76,6 +76,8 @@ namespace Aurora::Threading::Threads
void OSDeatach();
void TeminateOSContext(bool calledFromThis);
void FreeOSContext();
bool HasValidThreadIdYet();
bool HasValidThreadHandleYet();
void HookReleaseThreadResources();
void HookOnExit();
@ -123,9 +125,10 @@ namespace Aurora::Threading::Threads
pthread_t handle_ {};
#endif
AuUInt64 unixThreadId_ = 0;
AuOptional<AuUInt64> optUnixThreadId_;
bool detached_ {};
AuUInt32 uNameIdentity_ {};
AuCriticalSection metaMutex;
};
void InitThreading();