mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
Use high-resolution waitable timers for timed waits on Windows
The millisecond timeout of a Win32 wait is rounded up to the timer tick, so `sleep(0.001)` took 15.6 ms on average. A waitable timer created with `CREATE_WAITABLE_TIMER_HIGH_RESOLUTION`, accepted since Windows 10 1803, brings that to 1.5 ms without raising the timer resolution of the whole process as `timeBeginPeriod` would. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f9ce29e0e6
commit
3071798546
2 files changed
+33
-6
No files matched your search
@@ -131,6 +131,9 @@ struct rb_native_thread {
|
||||
// signalled by native_thread_interrupt() to break this thread out of a
|
||||
// blocking w32_wait_events()
|
||||
HANDLE interrupt_event;
|
||||
// a high-resolution timer that ends a timed wait on readyq at its
|
||||
// deadline rather than at the next timer tick (NULL if unavailable)
|
||||
HANDLE wait_timer;
|
||||
#endif
|
||||
|
||||
struct rb_thread_struct *running_thread;
|
||||
|
||||
+30
-6
@@ -267,8 +267,9 @@ rb_native_cond_broadcast(rb_nativethread_cond_t *cond)
|
||||
}
|
||||
}
|
||||
|
||||
// An armed timer, if given, ends the wait the way msec running out does.
|
||||
static int
|
||||
native_cond_timedwait_ms(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex, unsigned long msec)
|
||||
native_cond_timedwait_ms(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex, HANDLE timer, unsigned long msec)
|
||||
{
|
||||
DWORD r;
|
||||
struct cond_event_entry entry;
|
||||
@@ -284,9 +285,11 @@ native_cond_timedwait_ms(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *m
|
||||
|
||||
rb_native_mutex_unlock(mutex);
|
||||
{
|
||||
r = WaitForSingleObject(entry.event, msec);
|
||||
HANDLE events[2] = {entry.event, timer};
|
||||
r = WaitForMultipleObjects(timer ? 2 : 1, events, FALSE, msec);
|
||||
if (r == WAIT_OBJECT_0 + 1) r = WAIT_TIMEOUT;
|
||||
if ((r != WAIT_OBJECT_0) && (r != WAIT_TIMEOUT)) {
|
||||
rb_bug("rb_native_cond_wait: WaitForSingleObject returns %lu", r);
|
||||
rb_bug("rb_native_cond_wait: WaitForMultipleObjects returns %lu", r);
|
||||
}
|
||||
}
|
||||
rb_native_mutex_lock(mutex);
|
||||
@@ -301,13 +304,13 @@ native_cond_timedwait_ms(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *m
|
||||
void
|
||||
rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
|
||||
{
|
||||
native_cond_timedwait_ms(cond, mutex, INFINITE);
|
||||
native_cond_timedwait_ms(cond, mutex, NULL, INFINITE);
|
||||
}
|
||||
|
||||
void
|
||||
rb_native_cond_timedwait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex, unsigned long msec)
|
||||
{
|
||||
native_cond_timedwait_ms(cond, mutex, msec);
|
||||
native_cond_timedwait_ms(cond, mutex, NULL, msec);
|
||||
}
|
||||
|
||||
// The scheduler parks threads with an absolute deadline; on this platform the
|
||||
@@ -330,12 +333,24 @@ native_cond_timedwait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mute
|
||||
if (*abs <= now) return ETIMEDOUT;
|
||||
|
||||
rb_hrtime_t rel = *abs - now;
|
||||
HANDLE timer = ruby_thread_from_native()->nt->wait_timer;
|
||||
|
||||
if (timer) {
|
||||
// a negative due time is relative, in 100ns units
|
||||
LARGE_INTEGER due;
|
||||
due.QuadPart = -(LONGLONG)roomof(rel, 100);
|
||||
if (!SetWaitableTimer(timer, &due, 0, NULL, NULL, FALSE)) {
|
||||
w32_error("native_cond_timedwait");
|
||||
}
|
||||
return native_cond_timedwait_ms(cond, mutex, timer, INFINITE);
|
||||
}
|
||||
|
||||
unsigned long msec = (unsigned long)(rel / RB_HRTIME_PER_MSEC);
|
||||
|
||||
// do not busy loop on a sub-millisecond deadline
|
||||
if (msec == 0) msec = 1;
|
||||
|
||||
return native_cond_timedwait_ms(cond, mutex, msec);
|
||||
return native_cond_timedwait_ms(cond, mutex, NULL, msec);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -549,6 +564,14 @@ native_thread_setup(struct rb_native_thread *nt)
|
||||
if (nt->interrupt_event == NULL) {
|
||||
w32_error("native_thread_setup");
|
||||
}
|
||||
|
||||
#if _WIN32_WINNT >= 0x0A00 && defined(CREATE_WAITABLE_TIMER_HIGH_RESOLUTION)
|
||||
// The headers define the flag for any target version, but only Windows
|
||||
// 10 1803 and later accept it. Failing to create the timer only leaves
|
||||
// timed waits on the millisecond timeout.
|
||||
nt->wait_timer = CreateWaitableTimerExW(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
|
||||
TIMER_MODIFY_STATE | SYNCHRONIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -588,6 +611,7 @@ native_thread_destroy(struct rb_native_thread *nt)
|
||||
HANDLE intr = InterlockedExchangePointer(&nt->interrupt_event, 0);
|
||||
RUBY_DEBUG_LOG("close handle intr:%p, thid:%p\n", intr, nt->thread_id);
|
||||
if (intr) w32_close_handle(intr);
|
||||
if (nt->wait_timer) w32_close_handle(nt->wait_timer);
|
||||
|
||||
rb_native_cond_destroy(&nt->readyq);
|
||||
rb_native_mutex_destroy(&nt->running_th_lock);
|
||||
|
||||
Reference in new issue
Block a user