[+] Initial OSThread SetThrottle attempt

This commit is contained in:
Reece Wilson 2022-03-21 07:16:12 +00:00
parent 0fb514f856
commit 9542ec8374
7 changed files with 260 additions and 54 deletions

View File

@ -1,7 +1,7 @@
/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: EThreadPrio.hpp
File: EThreadPriority.hpp
Date: 2021-6-11
Author: Reece
***/
@ -9,13 +9,13 @@
namespace Aurora::Threading::Threads
{
AUE_DEFINE(EThreadPrio,
AUE_DEFINE(EThreadPriority,
(
eInvalid,
ePrioAboveHigh,
ePrioHigh,
ePrioNormal,
ePrioSub,
ePrioLow,
ePrioLowest,
ePrioRT
));
}

View File

@ -0,0 +1,18 @@
/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: EThreadThrottle.hpp
Date: 2021-6-11
Author: Reece
***/
#pragma once
namespace Aurora::Threading::Threads
{
AUE_DEFINE(EThreadThrottle,
(
ePerformance,
eNormal,
eEfficient
));
}

View File

@ -45,12 +45,14 @@ namespace Aurora::Threading::Threads
virtual void Exit() = 0;
virtual bool Exiting() = 0;
virtual void SendExitSignal() = 0;
virtual void SetPrio(EThreadPrio prio) = 0;
virtual void SetAffinity(const HWInfo::CpuBitId &mask) = 0;
virtual void SetPriority(EThreadPriority prio) = 0;
virtual void SetThrottle(EThreadThrottle throttle) = 0;
virtual void SetAffinity(const HWInfo::CpuBitId &mask) = 0;
virtual void SetName(const AuString &name) = 0;
virtual EThreadPrio GetPrio() = 0;
virtual EThreadPriority GetPriority() = 0;
virtual EThreadThrottle GetThrottle() = 0;
virtual HWInfo::CpuBitId GetMask() = 0;
virtual AuString GetName() = 0;

View File

@ -8,7 +8,8 @@
#pragma once
#include "IThreadFeature.hpp"
#include "EThreadPrio.hpp"
#include "EThreadPriority.hpp"
#include "EThreadThrottle.hpp"
#include "IThreadVectors.hpp"
#include "IAuroraThread.hpp"
#include "ThreadInfo.hpp"

View File

@ -105,7 +105,8 @@ namespace Aurora::Grug
}
// he's a lazy bastard (ira type insult)
gGrugsBigWorld->SetPrio(AuThreads::EThreadPrio::ePrioSub);
gGrugsBigWorld->SetThrottle(AuThreads::EThreadThrottle::eEfficient);
gGrugsBigWorld->SetPriority(AuThreads::EThreadPriority::ePrioLow);
gGrugsBigWorld->Run();
}
@ -114,7 +115,8 @@ namespace Aurora::Grug
gMutex = AuThreadPrimitives::ConditionMutexUnique();
SysAssert(gMutex, "Couldn't allocate a unique condition variable mutex for grug");
gCondVar = AuThreadPrimitives::ConditionVariableUnique(AuUnsafeRaiiToShared(gMutex));
auto shared = AuUnsafeRaiiToShared(gMutex);
gCondVar = AuThreadPrimitives::ConditionVariableUnique(shared);
SysAssert(gCondVar, "Couldn't allocate a unique condition variable for grug");
gArrows = AuThreadPrimitives::SemaphoreUnique();

View File

@ -261,7 +261,12 @@ namespace Aurora::Threading::Threads
this->name_ = name;
}
EThreadPrio OSThread::GetPrio()
EThreadThrottle OSThread::GetThrottle()
{
return this->throttle_;
}
EThreadPriority OSThread::GetPriority()
{
return this->prio_;
}
@ -284,19 +289,38 @@ namespace Aurora::Threading::Threads
mask == zero.Not())
{
this->mask_ = HWInfo::GetCPUInfo().maskAllCores;
this->userManagingAffinity_ = false;
UpdatePrio(this->throttle_, this->prio_);
}
else
{
this->mask_ = mask;
this->userManagingAffinity_ = true;
}
UpdateAffinity(this->mask_);
}
void OSThread::SetPrio(EThreadPrio prio)
void OSThread::SetPriority(EThreadPriority prio)
{
if (!EThreadPrioIsValid(prio)) return;
UpdatePrio(prio);
if (!EThreadPriorityIsValid(prio))
{
return;
}
UpdatePrio(this->throttle_, prio);
}
void OSThread::SetThrottle(EThreadThrottle throttle)
{
if (!EThreadThrottleIsValid(throttle))
{
return;
}
this->throttle_ = throttle;
UpdatePrio(this->throttle_, this->prio_);
}
AuSPtr<TLSView> OSThread::GetTlsView()
@ -463,56 +487,129 @@ namespace Aurora::Threading::Threads
#elif defined(AURORA_HAS_PTHREADS)
this->unixThreadId_ = 0; // !!!!
#endif
UpdatePrio(this->prio_);
UpdatePrio(this->throttle_, this->prio_);
SetAffinity(this->mask_);
UpdateName();
}
static AuHashMap<EThreadPrio, int> kNiceMap
static AuHashMap<EThreadPriority, int> kNiceMap
{
{
EThreadPrio::ePrioRT, -19
EThreadPriority::ePrioRT, -19
},
{
EThreadPrio::ePrioAboveHigh, -19
EThreadPriority::ePrioAboveHigh, -19
},
{
EThreadPrio::ePrioHigh, -11
EThreadPriority::ePrioHigh, -11
},
{
EThreadPrio::ePrioNormal, -2
EThreadPriority::ePrioNormal, -2
},
{
EThreadPrio::ePrioSub, 5
EThreadPriority::ePrioLow, 5
},
{
EThreadPriority::ePrioLowest, 15
}
};
#if defined(AURORA_IS_MODERNNT_DERIVED)
static const AuHashMap<EThreadPrio, int> kWin32Map
static const AuHashMap<EThreadPriority, int> kWin32Map
{
{
EThreadPrio::ePrioRT, THREAD_PRIORITY_TIME_CRITICAL
EThreadPriority::ePrioRT, THREAD_PRIORITY_TIME_CRITICAL
},
{
EThreadPrio::ePrioAboveHigh, THREAD_PRIORITY_HIGHEST
EThreadPriority::ePrioAboveHigh, THREAD_PRIORITY_HIGHEST
},
{
EThreadPrio::ePrioHigh, THREAD_PRIORITY_ABOVE_NORMAL
EThreadPriority::ePrioHigh, THREAD_PRIORITY_ABOVE_NORMAL
},
{
EThreadPrio::ePrioNormal, THREAD_PRIORITY_NORMAL
EThreadPriority::ePrioNormal, THREAD_PRIORITY_NORMAL
},
{
EThreadPrio::ePrioSub, THREAD_PRIORITY_LOWEST
EThreadPriority::ePrioLow, THREAD_PRIORITY_BELOW_NORMAL
},
{
EThreadPriority::ePrioLowest, THREAD_PRIORITY_LOWEST
}
};
#endif
void OSThread::UpdatePrio(EThreadPrio prio)
#if defined(AURORA_IS_XNU_DERIVED)
static const AuHashMap<AuPair<EThreadThrottle, EThreadPriority>, AuPair<AuUInt32, int>> kXnuQoSLevel
{
// Rt
{
AuMakePair(EThreadThrottle::eNormal, EThreadPriority::ePrioRT), AuMakePair(DISPATCH_QUEUE_PRIORITY_HIGH, 10)
},
{
AuMakePair(EThreadThrottle::eNormal, EThreadPriority::ePrioAboveHigh), AuMakePair(DISPATCH_QUEUE_PRIORITY_DEFAULT, 5)
},
{
AuMakePair(EThreadThrottle::eNormal, EThreadPriority::ePrioHigh), AuMakePair(DISPATCH_QUEUE_PRIORITY_DEFAULT, 1)
},
{
AuMakePair(EThreadThrottle::eNormal, EThreadPriority::ePrioNormal), AuMakePair(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
},
{
AuMakePair(EThreadThrottle::eNormal, EThreadPriority::ePrioLow), AuMakePair(DISPATCH_QUEUE_PRIORITY_LOW, 0)
},
{
AuMakePair(EThreadThrottle::eNormal, EThreadPriority::ePrioLowest), AuMakePair(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
},
// Perf
{
AuMakePair(EThreadThrottle::ePerformance, EThreadPriority::ePrioRT), AuMakePair(DISPATCH_QUEUE_PRIORITY_HIGH, 10)
},
{
AuMakePair(EThreadThrottle::ePerformance, EThreadPriority::ePrioAboveHigh), AuMakePair(DISPATCH_QUEUE_PRIORITY_HIGH, 8)
},
{
AuMakePair(EThreadThrottle::ePerformance, EThreadPriority::ePrioHigh), AuMakePair(DISPATCH_QUEUE_PRIORITY_HIGH, 3)
},
{
AuMakePair(EThreadThrottle::ePerformance, EThreadPriority::ePrioNormal), AuMakePair(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
},
{
AuMakePair(EThreadThrottle::ePerformance, EThreadPriority::ePrioLow), AuMakePair(DISPATCH_QUEUE_PRIORITY_DEFAULT, 1)
},
{
AuMakePair(EThreadThrottle::ePerformance, EThreadPriority::ePrioLowest), AuMakePair(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
},
// Efficient
{
AuMakePair(EThreadThrottle::eEfficient, EThreadPriority::ePrioRT), AuMakePair(DISPATCH_QUEUE_PRIORITY_LOW, 10)
},
{
AuMakePair(EThreadThrottle::eEfficient, EThreadPriority::ePrioAboveHigh), AuMakePair(DISPATCH_QUEUE_PRIORITY_LOW, 1)
},
{
AuMakePair(EThreadThrottle::eEfficient, EThreadPriority::ePrioHigh), AuMakePair(DISPATCH_QUEUE_PRIORITY_LOW, 0)
},
{
AuMakePair(EThreadThrottle::eEfficient, EThreadPriority::ePrioNormal), AuMakePair(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
},
{
AuMakePair(EThreadThrottle::eEfficient, EThreadPriority::ePrioLow), AuMakePair(DISPATCH_QUEUE_PRIORITY_BACKGROUND, -1)
},
{
AuMakePair(EThreadThrottle::eEfficient, EThreadPriority::ePrioLowest), AuMakePair(DISPATCH_QUEUE_PRIORITY_BACKGROUND, -2)
}
};
#endif
void OSThread::UpdatePrio(EThreadThrottle throttle, EThreadPriority prio)
{
#if defined(AURORA_IS_MODERNNT_DERIVED)
if (handle_ == INVALID_HANDLE_VALUE)
if (this->handle_ == INVALID_HANDLE_VALUE)
{
return;
}
@ -523,16 +620,46 @@ namespace Aurora::Threading::Threads
return;
}
SetThreadPriority(this->handle_, *val);
if (!SetThreadPriority(this->handle_, *val))
{
SysPushErrorHAL("Couldn't update thread priority");
}
THREAD_POWER_THROTTLING_STATE throttlingState {};
throttlingState.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
switch (throttle)
{
case EThreadThrottle::eNormal:
throttlingState.ControlMask = 0;
throttlingState.StateMask = 0;
break;
case EThreadThrottle::ePerformance:
throttlingState.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
throttlingState.StateMask = 0;
break;
case EThreadThrottle::eEfficient:
throttlingState.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
throttlingState.StateMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
break;
}
SetThreadInformation(this->handle_,
ThreadPowerThrottling,
&throttlingState,
sizeof(throttlingState));
//#elif defined(AURORA_IS_XNU_DERIVED)
#elif defined(AURORA_HAS_PTHREADS)
if (!handle_)
if (!this->handle_)
{
return;
}
if (prio == EThreadPrio::ePrioRT)
if (prio == EThreadPriority::ePrioRT)
{
sched_param param {};
param.sched_priority = sched_get_priority_min(SCHED_RR);
@ -544,7 +671,7 @@ namespace Aurora::Threading::Threads
// fall through on error
}
else if (this->prio_ == EThreadPrio::ePrioRT)
else if (this->prio_ == EThreadPriority::ePrioRT)
{
int policyNonRT =
#if defined(AURORA_IS_XNU_DERIVED)
@ -566,16 +693,33 @@ namespace Aurora::Threading::Threads
if (auto tid = unixThreadId_)
{
// TODO: per thread beaviour is a linux bug
setpriority(PRIO_PROCESS, tid, *val);
}
#endif
#if defined(AURORA_IS_LINUX_DERIVED) || defined(AURORA_IS_BSD_DERIVED)
switch (throttle)
{
case EThreadThrottle::eNormal:
break;
case EThreadThrottle::ePerformance:
this->throttleMask_ = AuHwInfo::GetCPUInfo().maskPCores;
break;
case EThreadThrottle::eEfficient:
this->throttleMask_ = AuHwInfo::GetCPUInfo().maskECores;
break;
}
#endif
this->prio_ = prio;
}
void OSThread::UpdateAffinity(const HWInfo::CpuBitId &mask)
{
#if defined(AURORA_IS_MODERNNT_DERIVED)
if (this->handle_ == INVALID_HANDLE_VALUE)
{
return;
@ -595,7 +739,6 @@ namespace Aurora::Threading::Threads
{
if (SetThreadSelectedCpuSets_f(this->handle_, sets.data(), sets.size()))
{
// happy days :D
return;
}
@ -612,11 +755,46 @@ namespace Aurora::Threading::Threads
}
#endif
SysPushErrorUnavailableError("Couldn't set thread affinity");
return;
#elif defined(AURORA_HAS_PTHREADS)
auto mask2 = mask.And(this->throttleMask_);
if (mask2.CpuBitCount() == 0)
{
switch (this->throttle_)
{
case EThreadThrottle::eNormal:
break;
case EThreadThrottle::ePerformance:
mask2 = AuHwInfo::GetCPUInfo().maskPCores;
break;
case EThreadThrottle::eEfficient:
mask2 = AuHwInfo::GetCPUInfo().maskECores;
break;
}
}
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
AuUInt8 index {};
while (mask2.CpuBitScanForward(index, index))
{
CPU_SET(index, &cpuset);
index++;
}
if (pthread_setaffinity_np(this->handle_, sizeof(cpuset), &cpuset) != 0)
{
SysPushErrorHAL("Couldn't set affinity mask");
}
return;
#endif
SysPushErrorUnavailableError("Couldn't set thread affinity");
}
void OSThread::OSDeatach()
@ -630,7 +808,7 @@ namespace Aurora::Threading::Threads
{
if (!locked)
{
if (!exitOnlyOnce_->TryLock())
if (!this->exitOnlyOnce_->TryLock())
{
return false;
}
@ -642,9 +820,9 @@ namespace Aurora::Threading::Threads
try
{
// dispatch kill callback
if (info_.callbacks)
if (this->info_.callbacks)
{
info_.callbacks->OnExit(this);
this->info_.callbacks->OnExit(this);
}
}
catch (...)
@ -659,16 +837,16 @@ namespace Aurora::Threading::Threads
HookReleaseThreadResources();
if (terminated_)
if (this->terminated_)
{
exitOnlyOnce_->Unlock();
terminated_->Set();
this->exitOnlyOnce_->Unlock();
this->terminated_->Set();
}
if (terminatedSignalLs_)
if (this->terminatedSignalLs_)
{
terminatedSignalLs_->Set();
this->terminatedSignalLs_->Set();
}
return true;
@ -694,7 +872,7 @@ namespace Aurora::Threading::Threads
#elif defined(AURORA_HAS_PTHREADS)
pthread_kill(handle_, SIGTERM);
pthread_kill(this->handle_, SIGTERM);
#else
@ -731,4 +909,4 @@ namespace Aurora::Threading::Threads
AuxUlibInitialize();
#endif
}
}
}

View File

@ -21,12 +21,14 @@ namespace Aurora::Threading::Threads
void Exit() override;
bool Exiting() override;
void SendExitSignal() override;
void SetPrio(EThreadPrio prio) override;
void SetPriority(EThreadPriority prio) override;
void SetThrottle(EThreadThrottle prio) override;
void SetAffinity(const HWInfo::CpuBitId &mask) override;
void SetName(const AuString &name) override;
EThreadPrio GetPrio() override;
EThreadPriority GetPriority() override;
EThreadThrottle GetThrottle() override;
HWInfo::CpuBitId GetMask() override;
AuString GetName() override;
@ -47,7 +49,7 @@ namespace Aurora::Threading::Threads
bool Exit(bool willReturnToOS);
bool ExecuteNewOSContext(AuFunction<void()> task);
void UpdatePrio(EThreadPrio prio);
void UpdatePrio(EThreadThrottle throttle, EThreadPriority prio);
void UpdateAffinity(const HWInfo::CpuBitId &mask);
void UpdateName();
void OSAttach();
@ -66,8 +68,11 @@ namespace Aurora::Threading::Threads
AuString name_;
ThreadInfo info_;
HWInfo::CpuBitId mask_ = HWInfo::CpuBitId().Not();
EThreadPrio prio_ = EThreadPrio::ePrioNormal;
HWInfo::CpuBitId throttleMask_ = HWInfo::CpuBitId().Not();
EThreadPriority prio_ = EThreadPriority::ePrioNormal;
EThreadThrottle throttle_ = EThreadThrottle::eNormal;
bool userManagingAffinity_ {};
bool exiting_{};
bool contextUsed_{}; // can this thread instance execute code again?
Primitives::EventShared_t terminated_;