diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index dbd630896f111d12ed4cdc44a5400021f26cf67f..f8107760b9e810347fbfa60248fe5f6a69beb04d 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -611,6 +611,19 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { #[doc(hidden)] pub type RunnableVariant = Runnable; +#[doc(hidden)] +pub struct TimerResolutionGuard { + cleanup: Option>, +} + +impl Drop for TimerResolutionGuard { + fn drop(&mut self) { + if let Some(cleanup) = self.cleanup.take() { + cleanup(); + } + } +} + /// This type is public so that our test macro can generate and use it, but it should not /// be considered part of our public API. #[doc(hidden)] @@ -627,6 +640,10 @@ pub trait PlatformDispatcher: Send + Sync { Instant::now() } + fn increase_timer_resolution(&self) -> TimerResolutionGuard { + TimerResolutionGuard { cleanup: None } + } + #[cfg(any(test, feature = "test-support"))] fn as_test(&self) -> Option<&TestDispatcher> { None diff --git a/crates/gpui/src/platform/windows/dispatcher.rs b/crates/gpui/src/platform/windows/dispatcher.rs index d474a385f0829cd0f4226da05911b3d34298feac..407c5dd17d8cc359e610b736577a487913370ddc 100644 --- a/crates/gpui/src/platform/windows/dispatcher.rs +++ b/crates/gpui/src/platform/windows/dispatcher.rs @@ -12,6 +12,7 @@ use windows::{ }, Win32::{ Foundation::{LPARAM, WPARAM}, + Media::{timeBeginPeriod, timeEndPeriod}, System::Threading::{ GetCurrentThread, HIGH_PRIORITY_CLASS, SetPriorityClass, SetThreadPriority, THREAD_PRIORITY_TIME_CRITICAL, @@ -22,7 +23,7 @@ use windows::{ use crate::{ GLOBAL_THREAD_TIMINGS, HWND, PlatformDispatcher, Priority, PriorityQueueSender, - RunnableVariant, SafeHwnd, THREAD_TIMINGS, TaskTiming, ThreadTaskTimings, + RunnableVariant, SafeHwnd, THREAD_TIMINGS, TaskTiming, ThreadTaskTimings, TimerResolutionGuard, WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD, profiler, }; @@ -193,4 +194,15 @@ impl PlatformDispatcher for WindowsDispatcher { f(); }); } + + fn increase_timer_resolution(&self) -> TimerResolutionGuard { + unsafe { + timeBeginPeriod(1); + } + TimerResolutionGuard { + cleanup: Some(Box::new(|| unsafe { + timeEndPeriod(1); + })), + } + } } diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 1e69c7d8f9d0a9ed305fc8e5e7d388b51246cfc4..2c8163d9c1df67413109ff96ec03e91542becbd6 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -20,7 +20,6 @@ use windows::{ Win32::{ Foundation::*, Graphics::{Direct3D11::ID3D11Device, Gdi::*}, - Media::{timeBeginPeriod, timeEndPeriod}, Security::Credentials::*, System::{Com::*, LibraryLoader::*, Ole::*, SystemInformation::*}, UI::{Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*}, @@ -98,10 +97,6 @@ impl WindowsPlatform { pub(crate) fn new(headless: bool) -> Result { unsafe { OleInitialize(None).context("unable to initialize Windows OLE")?; - // Set the system timer resolution to 1ms so that short timeouts - // (e.g. in Scheduler::block) are not rounded up to the default - // ~15.6ms tick interval. - timeBeginPeriod(1); } let (directx_devices, text_system, direct_write_text_system) = if !headless { let devices = DirectXDevices::new().context("Creating DirectX devices")?; @@ -991,7 +986,6 @@ impl WindowsPlatformInner { impl Drop for WindowsPlatform { fn drop(&mut self) { unsafe { - timeEndPeriod(1); DestroyWindow(self.handle) .context("Destroying platform window") .log_err(); diff --git a/crates/gpui/src/platform_scheduler.rs b/crates/gpui/src/platform_scheduler.rs index 6ed965c068dafe0cdbdb4bd625c0ce7c33206ba7..74868bb07f06e6d00ea7a2cf52aae68e255624e0 100644 --- a/crates/gpui/src/platform_scheduler.rs +++ b/crates/gpui/src/platform_scheduler.rs @@ -56,9 +56,17 @@ impl Scheduler for PlatformScheduler { if let Poll::Ready(()) = future.as_mut().poll(&mut cx) { return true; } + + let park_deadline = |deadline: Instant| { + // Timer expirations are only delivered every ~15.6 milliseconds by default on Windows. + // We increase the resolution during this wait so that short timeouts stay reasonably short. + let _timer_guard = self.dispatcher.increase_timer_resolution(); + parker.park_deadline(deadline) + }; + loop { match deadline { - Some(deadline) if !parker.park_deadline(deadline) && deadline <= Instant::now() => { + Some(deadline) if !park_deadline(deadline) && deadline <= Instant::now() => { return false; } Some(_) => (),