mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
kernel: block short sleep requests instead of busy waiting
This commit is contained in:
+2
-40
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <chrono> // IWYU pragma: keep
|
||||
@@ -26,8 +25,7 @@
|
||||
#ifdef KYTY_WIN_CS
|
||||
#include <windows.h> // IWYU pragma: keep
|
||||
// IWYU pragma: no_include <winbase.h>
|
||||
constexpr DWORD KYTY_CS_SPIN_COUNT = 4000;
|
||||
constexpr uint64_t KYTY_SLEEP_SPIN_LIMIT_100NS = 500; // 50 us
|
||||
constexpr DWORD KYTY_CS_SPIN_COUNT = 4000;
|
||||
|
||||
#ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
|
||||
#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x00000002
|
||||
@@ -38,30 +36,6 @@ static void SleepHighResolution100ns(uint64_t units_100ns) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep spinning only where a kernel transition is
|
||||
// likely to cost more than the requested delay; ordinary millisecond sleeps use the
|
||||
// per-thread high-resolution waitable timer below.
|
||||
if (units_100ns <= KYTY_SLEEP_SPIN_LIMIT_100NS) {
|
||||
LARGE_INTEGER frequency {};
|
||||
LARGE_INTEGER start {};
|
||||
if (QueryPerformanceFrequency(&frequency) != 0 && QueryPerformanceCounter(&start) != 0 &&
|
||||
frequency.QuadPart > 0) {
|
||||
const auto wait_ticks =
|
||||
static_cast<LONGLONG>((static_cast<long double>(units_100ns) *
|
||||
static_cast<long double>(frequency.QuadPart)) /
|
||||
10000000.0L);
|
||||
const auto deadline = start.QuadPart + std::max<LONGLONG>(wait_ticks, 1);
|
||||
LARGE_INTEGER now {};
|
||||
do {
|
||||
if (QueryPerformanceCounter(&now) == 0) {
|
||||
break;
|
||||
}
|
||||
YieldProcessor();
|
||||
} while (now.QuadPart < deadline);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
thread_local HANDLE timer = CreateWaitableTimerExW(
|
||||
nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
|
||||
if (timer == nullptr) {
|
||||
@@ -131,14 +105,13 @@ static SleepConditionVariableCS_func_t ResolveSleepConditionVariableCS() {
|
||||
#endif
|
||||
|
||||
#ifdef KYTY_POSIX_HIGH_RES_SLEEP
|
||||
// Spin for very short waits; use an absolute deadline for longer waits.
|
||||
// An absolute deadline preserves the requested sleep across signal interruptions.
|
||||
static void SleepHighResolutionNanos(uint64_t nanos) {
|
||||
if (nanos == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr uint64_t NANOS_PER_SEC = 1000000000;
|
||||
constexpr uint64_t SPIN_LIMIT_NS = 50000; // below this a context switch dominates
|
||||
|
||||
timespec deadline {};
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &deadline) != 0) {
|
||||
@@ -150,17 +123,6 @@ static void SleepHighResolutionNanos(uint64_t nanos) {
|
||||
deadline.tv_sec += static_cast<time_t>(target_nsec / NANOS_PER_SEC);
|
||||
deadline.tv_nsec = static_cast<long>(target_nsec % NANOS_PER_SEC);
|
||||
|
||||
if (nanos <= SPIN_LIMIT_NS) {
|
||||
timespec now {};
|
||||
do {
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
|
||||
return;
|
||||
}
|
||||
} while (now.tv_sec < deadline.tv_sec ||
|
||||
(now.tv_sec == deadline.tv_sec && now.tv_nsec < deadline.tv_nsec));
|
||||
return;
|
||||
}
|
||||
|
||||
while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &deadline, nullptr) == EINTR) {
|
||||
}
|
||||
}
|
||||
|
||||
+1
-30
@@ -512,15 +512,6 @@ static void SchedulerBackoffOnce() {
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool SleepMicroSchedulerBackoff(uint64_t microseconds) {
|
||||
if (microseconds > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SchedulerBackoffOnce();
|
||||
return true;
|
||||
}
|
||||
|
||||
static void SleepMicroWithSignalPoll(uint64_t microseconds) {
|
||||
if (microseconds == 0) {
|
||||
KernelDispatchPendingSignalForCurrentThread();
|
||||
@@ -529,9 +520,7 @@ static void SleepMicroWithSignalPoll(uint64_t microseconds) {
|
||||
|
||||
while (microseconds > 0) {
|
||||
const auto step = std::min<uint64_t>(microseconds, SIGNAL_APC_POLL_MICROS);
|
||||
if (!SleepMicroSchedulerBackoff(step)) {
|
||||
Common::Thread::SleepMicro(step);
|
||||
}
|
||||
Common::Thread::SleepMicro(step);
|
||||
microseconds -= step;
|
||||
KernelDispatchPendingSignalForCurrentThread();
|
||||
}
|
||||
@@ -3842,28 +3831,16 @@ void KYTY_SYSV_ABI KernelSetThreadDtors(thread_dtors_func_t dtors) {
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelUsleep(KernelUseconds microseconds) {
|
||||
Common::Timer t;
|
||||
t.Start();
|
||||
SleepMicroWithSignalPoll(microseconds);
|
||||
// double ts = t.GetTimeS();
|
||||
// LOGF("\tactual: %g microseconds\n", ts * 1000000.0);
|
||||
return OK;
|
||||
}
|
||||
|
||||
unsigned int KYTY_SYSV_ABI KernelSleep(unsigned int seconds) {
|
||||
PRINT_NAME();
|
||||
LOGF("\tsleep: %u\n", seconds);
|
||||
Common::Timer t;
|
||||
t.Start();
|
||||
SleepMicroWithSignalPoll(static_cast<uint64_t>(seconds) * 1000000ull);
|
||||
double ts = t.GetTimeS();
|
||||
LOGF("\tactual: %g seconds\n", ts);
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelNanosleep(const KernelTimespec* rqtp, KernelTimespec* rmtp) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (rqtp == nullptr) {
|
||||
return KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
@@ -3880,13 +3857,7 @@ int KYTY_SYSV_ABI KernelNanosleep(const KernelTimespec* rqtp, KernelTimespec* rm
|
||||
uint64_t nanos =
|
||||
static_cast<uint64_t>(rqtp->tv_sec) * 1000000000ull + static_cast<uint64_t>(rqtp->tv_nsec);
|
||||
|
||||
LOGF("\tnanosleep: %" PRIu64 "\n", nanos);
|
||||
|
||||
Common::Timer t;
|
||||
t.Start();
|
||||
SleepNanoWithSignalPoll(nanos);
|
||||
double ts = t.GetTimeS();
|
||||
LOGF("\tactual: %g nanoseconds\n", ts * 1000000000.0);
|
||||
|
||||
if (rmtp != nullptr) {
|
||||
rmtp->tv_sec = 0;
|
||||
|
||||
Reference in New Issue
Block a user