kernel: block short sleep requests instead of busy waiting

This commit is contained in:
nmzik
2026-09-29 16:38:07 +04:00
parent 0c844e4bc2
commit 6f24b031f5
2 changed files with 3 additions and 70 deletions
+2 -40
View File
@@ -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
View File
@@ -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;