diff --git a/app/src/main/cpp/skyline/common/spin_lock.cpp b/app/src/main/cpp/skyline/common/spin_lock.cpp index 8c0e343e..d0f22bda 100644 --- a/app/src/main/cpp/skyline/common/spin_lock.cpp +++ b/app/src/main/cpp/skyline/common/spin_lock.cpp @@ -4,15 +4,19 @@ #include #include #include "spin_lock.h" +#include "utils.h" namespace skyline { - static constexpr size_t LockAttemptsPerYield{256}; + static constexpr size_t LockAttemptsPerYield{32}; static constexpr size_t LockAttemptsPerSleep{1024}; - static constexpr size_t SleepDurationUs{100}; + static constexpr size_t SleepDurationUs{50}; template void FalloffLock(Func &&func) { - for (size_t i{}; !func(); i++) { + for (size_t i{1}; !func(i); i++) { + asm volatile("DMB ISHST;" + "YIELD;"); + if (i % LockAttemptsPerYield == 0) std::this_thread::yield(); if (i % LockAttemptsPerSleep == 0) @@ -21,20 +25,34 @@ namespace skyline { } void __attribute__ ((noinline)) SpinLock::LockSlow() { - FalloffLock([this] { + FalloffLock([this] (size_t i) { return try_lock(); }); } void __attribute__ ((noinline)) SharedSpinLock::LockSlow() { - FalloffLock([this] { + FalloffLock([this] (size_t i) { return try_lock(); }); } void __attribute__ ((noinline)) SharedSpinLock::LockSlowShared() { - FalloffLock([this] { + FalloffLock([this] (size_t i) { return try_lock_shared(); }); } + + static constexpr size_t AdaptiveWaitIters{1024}; //!< Number of wait iterations before waiting should fallback to a regular condition variable + + void __attribute__ ((noinline)) AdaptiveSingleWaiterConditionVariable::SpinWait() { + FalloffLock([this] (size_t i) { + return !unsignalled.test_and_set() || i >= AdaptiveWaitIters; + }); + } + + void __attribute__ ((noinline)) AdaptiveSingleWaiterConditionVariable::SpinWait(i64 maxEndTimeNs) { + FalloffLock([maxEndTimeNs, this] (size_t i) { + return util::GetTimeNs() > maxEndTimeNs || !unsignalled.test_and_set() || i >= AdaptiveWaitIters; + }); + } } diff --git a/app/src/main/cpp/skyline/common/spin_lock.h b/app/src/main/cpp/skyline/common/spin_lock.h index 70ef9cac..a0f9bb74 100644 --- a/app/src/main/cpp/skyline/common/spin_lock.h +++ b/app/src/main/cpp/skyline/common/spin_lock.h @@ -4,8 +4,11 @@ #pragma once #include +#include #include +#include #include "base.h" +#include "utils.h" namespace skyline { /** @@ -130,4 +133,131 @@ namespace skyline { } } }; + + /** + * @brief A condition variable that spins for a bit before falling back to a regular condition variable, for cases where only a single thread can wait at the same time + */ + class AdaptiveSingleWaiterConditionVariable { + private: + /** + * @brief Spins either until the condition variable is signalled or the spin wait times out (to fall back to a regular condition variable) + */ + void SpinWait(); + + /** + * @brief Spins either until the condition variable is signalled or the spin wait times out (to fall back to a regular condition variable, or until the given time is reached) + * @param maxEndTimeNs The maximum time to spin for + */ + void SpinWait(i64 maxEndTimeNs); + + std::condition_variable fallback; // endTimeNs) + return false; + + + // If the spin wait timed out (due to wanting to fallback), then fallback to a regular condition variable + + // Calculate chrono-based end time only in the fallback path to avoid polluting the fast past + auto endTime{std::chrono::system_clock::now() + std::chrono::nanoseconds(endTimeNs - util::GetTimeNs())}; + std::cv_status status{std::cv_status::no_timeout}; + while (status == std::cv_status::no_timeout && !pred()) { + std::unique_lock fallbackLock{fallbackMutex}; + + // Store that we are currently waiting on the fallback condition variable, `notify()` can check this when `fallbackLock` ends up being unlocked by `fallback.wait()` to avoid redundantly performing futex wakes (on older bionic versions) + fallbackWaiter = true; + + lock.unlock(); + status = fallback.wait_until(fallbackLock, endTime); + + fallbackWaiter = false; + fallbackLock.unlock(); + + lock.lock(); + } + + // The predicate has been satisfied, we're done here + return pred(); + } + }; }