From 8bf6bdd963a18f9c439ca465e54ac8530a06f843 Mon Sep 17 00:00:00 2001 From: Jamie Reece Wilson Date: Sat, 19 Aug 2023 18:14:28 +0100 Subject: [PATCH] [+] More threading options [+] AuThreading::SetSpinCountTimeout [+] AuThreading::SetThreadLocalAdditionalSpinCountTimeout --- Include/Aurora/Runtime.hpp | 6 +++++ Include/Aurora/Threading/SpinTime.hpp | 17 +++++++++++++ Include/Aurora/Threading/Threading.hpp | 2 ++ .../Primitives/AuConditionMutex.NT.cpp | 18 +++++++++++--- .../Primitives/AuConditionMutex.NT.hpp | 1 + Source/Threading/Primitives/AuMutex.NT.cpp | 16 +++++++++++-- Source/Threading/Primitives/AuMutex.NT.hpp | 3 ++- .../Threading/Primitives/AuSemaphore.NT.cpp | 18 +++++++++++--- .../Threading/Primitives/AuSemaphore.NT.hpp | 1 + Source/Threading/Primitives/SMTYield.cpp | 19 +++++++++++++++ Source/Threading/Primitives/SMTYield.hpp | 24 +++++++++++++++++++ 11 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 Include/Aurora/Threading/SpinTime.hpp diff --git a/Include/Aurora/Runtime.hpp b/Include/Aurora/Runtime.hpp index b6c1afce..e4020ea7 100644 --- a/Include/Aurora/Runtime.hpp +++ b/Include/Aurora/Runtime.hpp @@ -338,6 +338,12 @@ namespace Aurora bool bPreferNt51XpCondvarsOver8 { false }; bool bPreferNtCondvarModernWinSpin { false }; bool bPreferNtCondvarOlderWinSpin { true }; + bool bPreferNtSemaphoreSpinTryLock { true }; + bool bPreferNtMutexSpinTryLock { true }; + bool bPreferNtCondMutexSpinTryLock { true }; + bool bPreferLinuxSemaphoreSpinTryLock { true }; + bool bPreferLinuxMutexSpinTryLock { true }; + bool bPreferLinuxCondMutexSpinTryLock { true }; bool bPreferEmulatedWakeOnAddress { false }; }; diff --git a/Include/Aurora/Threading/SpinTime.hpp b/Include/Aurora/Threading/SpinTime.hpp new file mode 100644 index 00000000..51a515f7 --- /dev/null +++ b/Include/Aurora/Threading/SpinTime.hpp @@ -0,0 +1,17 @@ +/*** + Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. + + File: SpinTime.hpp + Date: 2023-08-19 + Author: Reece +***/ +#pragma once + +namespace Aurora::Threading +{ + // Note: the following APIs are in powers of 2! + + AUKN_SYM void SetSpinCountTimeout(AuUInt8 uTimeout); + AUKN_SYM AuUInt8 GetSpinCountTimeout(); + AUKN_SYM void SetThreadLocalAdditionalSpinCountTimeout(AuUInt8 uTimeout); +} \ No newline at end of file diff --git a/Include/Aurora/Threading/Threading.hpp b/Include/Aurora/Threading/Threading.hpp index fdfae2ec..8e72483b 100644 --- a/Include/Aurora/Threading/Threading.hpp +++ b/Include/Aurora/Threading/Threading.hpp @@ -24,4 +24,6 @@ namespace Aurora::Threading #include "LockGuard.hpp" #include "LockGuardTry.hpp" +#include "SpinTime.hpp" + #include "Threads/Threads.hpp" \ No newline at end of file diff --git a/Source/Threading/Primitives/AuConditionMutex.NT.cpp b/Source/Threading/Primitives/AuConditionMutex.NT.cpp index 76e1914a..65348fb4 100644 --- a/Source/Threading/Primitives/AuConditionMutex.NT.cpp +++ b/Source/Threading/Primitives/AuConditionMutex.NT.cpp @@ -46,11 +46,23 @@ namespace Aurora::Threading::Primitives #if defined(AURORA_FORCE_SRW_LOCKS) return ::TryAcquireSRWLockExclusive(&this->lock_); #else + if (gRuntimeConfig.threadingConfig.bPreferNtCondMutexSpinTryLock) + { + return TryLockHeavy(); + } + else + { + return TryLockNoSpin(); + } + #endif + } + + bool Win32ConditionMutex::TryLockHeavy() + { return DoTryIf([=]() { - return this->TryLockNoSpin(); + return TryLockNoSpin(); }); - #endif } bool Win32ConditionMutex::TryLockNoSpin() @@ -68,7 +80,7 @@ namespace Aurora::Threading::Primitives ::AcquireSRWLockExclusive(&this->lock_); #else - if (this->TryLock()) + if (this->TryLockHeavy()) { return; } diff --git a/Source/Threading/Primitives/AuConditionMutex.NT.hpp b/Source/Threading/Primitives/AuConditionMutex.NT.hpp index 07771192..351d898d 100644 --- a/Source/Threading/Primitives/AuConditionMutex.NT.hpp +++ b/Source/Threading/Primitives/AuConditionMutex.NT.hpp @@ -41,6 +41,7 @@ namespace Aurora::Threading::Primitives AuUInt GetOSHandle() override; auline bool TryLockNoSpin(); + auline bool TryLockHeavy(); #if !defined(AURORA_FORCE_SRW_LOCKS) NT4Mutex lock_; diff --git a/Source/Threading/Primitives/AuMutex.NT.cpp b/Source/Threading/Primitives/AuMutex.NT.cpp index 0bf063f5..3cb1e9c4 100644 --- a/Source/Threading/Primitives/AuMutex.NT.cpp +++ b/Source/Threading/Primitives/AuMutex.NT.cpp @@ -44,7 +44,7 @@ namespace Aurora::Threading::Primitives return false; } - bool MutexImpl::TryLock() + bool MutexImpl::TryLockHeavy() { return DoTryIf([=]() { @@ -52,6 +52,18 @@ namespace Aurora::Threading::Primitives }); } + bool MutexImpl::TryLock() + { + if (gRuntimeConfig.threadingConfig.bPreferNtMutexSpinTryLock) + { + return TryLockHeavy(); + } + else + { + return TryLockNoSpin(); + } + } + bool MutexImpl::TryLockNoSpin() { return AuAtomicTestAndSet(&this->state_, 0) == 0; @@ -82,7 +94,7 @@ namespace Aurora::Threading::Primitives { bool returnValue = false; - if (this->TryLock()) + if (this->TryLockHeavy()) { return true; } diff --git a/Source/Threading/Primitives/AuMutex.NT.hpp b/Source/Threading/Primitives/AuMutex.NT.hpp index e9403029..28ba74ef 100644 --- a/Source/Threading/Primitives/AuMutex.NT.hpp +++ b/Source/Threading/Primitives/AuMutex.NT.hpp @@ -22,7 +22,8 @@ namespace Aurora::Threading::Primitives bool LockNS(AuUInt64 timeout) override; void Unlock() override; - auline bool TryLockNoSpin(); + auline bool TryLockNoSpin(); + auline bool TryLockHeavy(); private: #if defined(AURORA_FORCE_SRW_LOCKS) diff --git a/Source/Threading/Primitives/AuSemaphore.NT.cpp b/Source/Threading/Primitives/AuSemaphore.NT.cpp index 3b351d34..35300398 100644 --- a/Source/Threading/Primitives/AuSemaphore.NT.cpp +++ b/Source/Threading/Primitives/AuSemaphore.NT.cpp @@ -35,20 +35,32 @@ namespace Aurora::Threading::Primitives { return true; } - + bool SemaphoreImpl::TryLockNoSpin() { auto old = this->dwState_; return (old != 0 && AuAtomicCompareExchange(&this->dwState_, old - 1, old) == old); } - bool SemaphoreImpl::TryLock() + bool SemaphoreImpl::TryLockHeavy() { return DoTryIf([=]() { return TryLockNoSpin(); }); } + + bool SemaphoreImpl::TryLock() + { + if (gRuntimeConfig.threadingConfig.bPreferNtSemaphoreSpinTryLock) + { + return TryLockHeavy(); + } + else + { + return TryLockNoSpin(); + } + } bool SemaphoreImpl::LockMS(AuUInt64 uTimeout) { @@ -57,7 +69,7 @@ namespace Aurora::Threading::Primitives bool SemaphoreImpl::LockNS(AuUInt64 uTimeout) { - if (this->TryLock()) + if (this->TryLockHeavy()) { return true; } diff --git a/Source/Threading/Primitives/AuSemaphore.NT.hpp b/Source/Threading/Primitives/AuSemaphore.NT.hpp index 3235247d..4a70c281 100644 --- a/Source/Threading/Primitives/AuSemaphore.NT.hpp +++ b/Source/Threading/Primitives/AuSemaphore.NT.hpp @@ -27,6 +27,7 @@ namespace Aurora::Threading::Primitives void Unlock() override; auline bool TryLockNoSpin(); + auline bool TryLockHeavy(); private: AuUInt32 dwState_ {}; diff --git a/Source/Threading/Primitives/SMTYield.cpp b/Source/Threading/Primitives/SMTYield.cpp index e347cb8c..d5e29f25 100644 --- a/Source/Threading/Primitives/SMTYield.cpp +++ b/Source/Threading/Primitives/SMTYield.cpp @@ -8,6 +8,25 @@ #include #include "SMTYield.hpp" +namespace Aurora::Threading +{ + AUKN_SYM void SetSpinCountTimeout(AuUInt8 uTimeout) + { + gRuntimeConfig.threadingConfig.uSpinLoopPowerA = uTimeout; + } + + AUKN_SYM AuUInt8 GetSpinCountTimeout() + { + return gRuntimeConfig.threadingConfig.uSpinLoopPowerA; + } + + AUKN_SYM void SetThreadLocalAdditionalSpinCountTimeout(AuUInt8 uTimeout) + { + gHasThreadLocalTimeout = 1; + tlSpinCountLocal = uTimeout; + } +} + namespace Aurora::Threading::Primitives { diff --git a/Source/Threading/Primitives/SMTYield.hpp b/Source/Threading/Primitives/SMTYield.hpp index b672da7f..9f8ffa69 100644 --- a/Source/Threading/Primitives/SMTYield.hpp +++ b/Source/Threading/Primitives/SMTYield.hpp @@ -7,6 +7,12 @@ ***/ #pragma once +namespace Aurora::Threading +{ + inline AuUInt32 gHasThreadLocalTimeout {}; + inline thread_local AuUInt8 tlSpinCountLocal { 6 }; +} + namespace Aurora::Threading::Primitives { static auline void SMPPause() @@ -46,6 +52,24 @@ namespace Aurora::Threading::Primitives } } + if (gHasThreadLocalTimeout) + { + auto uCount = tlSpinCountLocal; + + int loops = (1 << uCount); + while (loops > 0) + { + SMPPause(); + + loops -= 1; + + if (callback()) + { + return true; + } + } + } + return callback(); }