diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index d65fd5c10bc12b..41ace67b4df001 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -80,6 +80,7 @@ use workspace::{ dock::{DockPosition, Panel, PanelEvent}, notifications::{DetachAndPromptErr, ErrorMessagePrompt, NotificationId, NotifyResultExt}, }; +use ztracing::instrument; actions!( git_panel, [ @@ -1197,6 +1198,7 @@ impl GitPanel { self.selected_entry.and_then(|i| self.entries.get(i)) } + #[instrument(skip_all)] fn open_diff(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context) { maybe!({ let entry = self.entries.get(self.selected_entry?)?.status_entry()?; @@ -1247,6 +1249,7 @@ impl GitPanel { }); } + #[instrument(skip_all)] fn open_file( &mut self, _: &menu::SecondaryConfirm, @@ -5082,9 +5085,9 @@ impl GitPanel { this.selected_entry = Some(ix); cx.notify(); if event.modifiers().secondary() { - this.open_file(&Default::default(), window, cx) + this.open_file(&Default::default(), window, cx) // here? } else { - this.open_diff(&Default::default(), window, cx); + this.open_diff(&Default::default(), window, cx); // here? this.focus_handle.focus(window, cx); } }) diff --git a/crates/gpui/src/executor.rs b/crates/gpui/src/executor.rs index fedd285dca4939..57b199e1dc0122 100644 --- a/crates/gpui/src/executor.rs +++ b/crates/gpui/src/executor.rs @@ -1,9 +1,10 @@ -use crate::{App, PlatformDispatcher, RunnableMeta, RunnableVariant, TaskTiming, profiler}; +use crate::{App, GpuiRunnable, PlatformDispatcher, RunnableMeta, TaskTiming, profiler}; use async_task::Runnable; use futures::channel::mpsc; use parking_lot::{Condvar, Mutex}; use smol::prelude::*; use std::{ + cell::Cell, fmt::Debug, marker::PhantomData, mem::{self, ManuallyDrop}, @@ -81,7 +82,21 @@ pub enum Priority { Low, } +thread_local! { +static CURRENT_TASKS_PRIORITY: Cell = const { Cell::new(Priority::Medium) }; } + impl Priority { + /// Sets the priority any spawn call from the runnable about + /// to be run will use + pub(crate) fn set_as_default_for_spawns(&self) { + CURRENT_TASKS_PRIORITY.set(*self); + } + + /// Returns the priority from the currently running task + pub fn inherit() -> Self { + CURRENT_TASKS_PRIORITY.get() + } + #[allow(dead_code)] pub(crate) const fn probability(&self) -> u32 { match self { @@ -329,19 +344,14 @@ impl BackgroundExecutor { .metadata(RunnableMeta { location, app: None, + priority: Priority::inherit(), }) .spawn_unchecked( move |_| async { let _notify_guard = NotifyOnDrop(pair); future.await }, - move |runnable| { - dispatcher.dispatch( - RunnableVariant::Meta(runnable), - None, - Priority::default(), - ) - }, + move |runnable| dispatcher.dispatch(GpuiRunnable::GpuiSpawned(runnable), None), ) }; runnable.schedule(); @@ -387,6 +397,7 @@ impl BackgroundExecutor { }; profiler::add_task_timing(timing); + Priority::Realtime(realtime).set_as_default_for_spawns(); runnable.run(); let end = Instant::now(); @@ -399,6 +410,7 @@ impl BackgroundExecutor { async_task::Builder::new() .metadata(RunnableMeta { location, + priority, app: None, }) .spawn( @@ -412,13 +424,12 @@ impl BackgroundExecutor { async_task::Builder::new() .metadata(RunnableMeta { location, + priority, app: None, }) .spawn( move |_| future, - move |runnable| { - dispatcher.dispatch(RunnableVariant::Meta(runnable), label, priority) - }, + move |runnable| dispatcher.dispatch(GpuiRunnable::GpuiSpawned(runnable), label), ) }; @@ -674,11 +685,14 @@ impl BackgroundExecutor { let (runnable, task) = async_task::Builder::new() .metadata(RunnableMeta { location, + priority: Priority::inherit(), app: None, }) .spawn(move |_| async move {}, { let dispatcher = self.dispatcher.clone(); - move |runnable| dispatcher.dispatch_after(duration, RunnableVariant::Meta(runnable)) + move |runnable| { + dispatcher.dispatch_after(duration, GpuiRunnable::GpuiSpawned(runnable)) + } }); runnable.schedule(); Task(TaskState::Spawned(task)) @@ -785,7 +799,10 @@ impl ForegroundExecutor { } } - /// Enqueues the given Task to run on the main thread at some point in the future. + /// Enqueues the given Task to run on the main thread at some point in the + /// future. This inherits the priority of the caller. Use + /// [`spawn_with_priority`](Self::spawn_with_priority) if you want to + /// overwrite that. #[track_caller] pub fn spawn(&self, future: impl Future + 'static) -> Task where @@ -831,10 +848,11 @@ impl ForegroundExecutor { let (runnable, task) = spawn_local_with_source_location( future, move |runnable| { - dispatcher.dispatch_on_main_thread(RunnableVariant::Meta(runnable), priority) + dispatcher.dispatch_on_main_thread(GpuiRunnable::GpuiSpawned(runnable)) }, RunnableMeta { location, + priority, app: Some(app), }, ); diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 50045932493a74..d9f477b11d602b 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -590,7 +590,8 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { /// be considered part of our public API. #[doc(hidden)] pub struct RunnableMeta { - /// Location of the runnable + pub priority: Priority, + /// Location of the runnable, only set for futures we spawn pub location: &'static core::panic::Location<'static>, /// Weak reference to check if the app is still alive before running this task pub app: Option>, @@ -599,6 +600,7 @@ pub struct RunnableMeta { impl std::fmt::Debug for RunnableMeta { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("RunnableMeta") + .field("priority", &self.priority) .field("location", &self.location) .field("app_alive", &self.is_app_alive()) .finish() @@ -615,10 +617,69 @@ impl RunnableMeta { } } +/// Both of these need meta for priority #[doc(hidden)] -pub enum RunnableVariant { - Meta(Runnable), - Compat(Runnable), +pub enum GpuiRunnable { + /// Spawned by us, we set useful metadata for profiling and scheduling. + /// Yay we have nice things! + GpuiSpawned(Runnable), + /// Spawned by a dependency through runtimelib. We only have the + /// runnable ('task'). No access to metadata. + DependencySpawned(Runnable<()>), +} + +impl GpuiRunnable { + fn run_and_profile(self) -> Instant { + if self.app_dropped() { + // optimizer will cut it out if it doesnt it does not really matter + // cause we are closing the app anyway. + return Instant::now(); + } + + let mut timing = TaskTiming { + // use a placeholder location if we dont have one + location: self.location().unwrap_or(core::panic::Location::caller()), + start: Instant::now(), + end: None, + }; + + crate::profiler::add_task_timing(timing); + self.run_unprofiled(); // surrounded by profiling so its ok + timing.end = Some(Instant::now()); + crate::profiler::add_task_timing(timing); + timing.start + } + + fn app_dropped(&self) -> bool { + match self { + GpuiRunnable::GpuiSpawned(runnable) => !runnable.metadata().is_app_alive(), + GpuiRunnable::DependencySpawned(_) => false, + } + } + + fn location(&self) -> Option<&'static core::panic::Location<'static>> { + match self { + GpuiRunnable::GpuiSpawned(runnable) => runnable.metadata().location.into(), + GpuiRunnable::DependencySpawned(_) => None, + } + } + + /// ONLY use for tests or headless client. + /// ideally everything should be profiled + fn run_unprofiled(self) { + self.priority().set_as_default_for_spawns(); + match self { + GpuiRunnable::GpuiSpawned(r) => r.run(), + GpuiRunnable::DependencySpawned(r) => r.run(), + }; + } + + fn priority(&self) -> Priority { + match self { + GpuiRunnable::GpuiSpawned(r) => r.metadata().priority, + GpuiRunnable::DependencySpawned(_) => Priority::Medium, + } + } } /// This type is public so that our test macro can generate and use it, but it should not @@ -628,9 +689,9 @@ pub trait PlatformDispatcher: Send + Sync { fn get_all_timings(&self) -> Vec; fn get_current_thread_timings(&self) -> Vec; fn is_main_thread(&self) -> bool; - fn dispatch(&self, runnable: RunnableVariant, label: Option, priority: Priority); - fn dispatch_on_main_thread(&self, runnable: RunnableVariant, priority: Priority); - fn dispatch_after(&self, duration: Duration, runnable: RunnableVariant); + fn dispatch(&self, runnable: GpuiRunnable, label: Option); + fn dispatch_on_main_thread(&self, runnable: GpuiRunnable); + fn dispatch_after(&self, duration: Duration, runnable: GpuiRunnable); fn spawn_realtime(&self, priority: RealtimePriority, f: Box); fn now(&self) -> Instant { diff --git a/crates/gpui/src/platform/linux/dispatcher.rs b/crates/gpui/src/platform/linux/dispatcher.rs index c8ae7269edd495..40243be6bba941 100644 --- a/crates/gpui/src/platform/linux/dispatcher.rs +++ b/crates/gpui/src/platform/linux/dispatcher.rs @@ -5,27 +5,22 @@ use calloop::{ }; use util::ResultExt; -use std::{ - mem::MaybeUninit, - thread, - time::{Duration, Instant}, -}; +use std::{mem::MaybeUninit, thread, time::Duration}; use crate::{ - GLOBAL_THREAD_TIMINGS, PlatformDispatcher, Priority, PriorityQueueReceiver, - PriorityQueueSender, RealtimePriority, RunnableVariant, THREAD_TIMINGS, TaskLabel, TaskTiming, - ThreadTaskTimings, profiler, + GLOBAL_THREAD_TIMINGS, GpuiRunnable, PlatformDispatcher, Priority, PriorityQueueReceiver, + PriorityQueueSender, RealtimePriority, THREAD_TIMINGS, TaskLabel, ThreadTaskTimings, }; struct TimerAfter { duration: Duration, - runnable: RunnableVariant, + runnable: GpuiRunnable, } pub(crate) struct LinuxDispatcher { - main_sender: PriorityQueueCalloopSender, + main_sender: PriorityQueueCalloopSender, timer_sender: Sender, - background_sender: PriorityQueueSender, + background_sender: PriorityQueueSender, _background_threads: Vec>, main_thread_id: thread::ThreadId, } @@ -33,7 +28,7 @@ pub(crate) struct LinuxDispatcher { const MIN_THREADS: usize = 2; impl LinuxDispatcher { - pub fn new(main_sender: PriorityQueueCalloopSender) -> Self { + pub fn new(main_sender: PriorityQueueCalloopSender) -> Self { let (background_sender, background_receiver) = PriorityQueueReceiver::new(); let thread_count = std::thread::available_parallelism().map_or(MIN_THREADS, |i| i.get().max(MIN_THREADS)); @@ -42,48 +37,16 @@ impl LinuxDispatcher { // executor let mut background_threads = (0..thread_count) .map(|i| { - let mut receiver = background_receiver.clone(); + let mut receiver: PriorityQueueReceiver = background_receiver.clone(); std::thread::Builder::new() .name(format!("Worker-{i}")) .spawn(move || { for runnable in receiver.iter() { - let start = Instant::now(); - - let mut location = match runnable { - RunnableVariant::Meta(runnable) => { - let location = runnable.metadata().location; - let timing = TaskTiming { - location, - start, - end: None, - }; - profiler::add_task_timing(timing); - - runnable.run(); - timing - } - RunnableVariant::Compat(runnable) => { - let location = core::panic::Location::caller(); - let timing = TaskTiming { - location, - start, - end: None, - }; - profiler::add_task_timing(timing); - - runnable.run(); - timing - } - }; - - let end = Instant::now(); - location.end = Some(end); - profiler::add_task_timing(location); - + let started = runnable.run_and_profile(); log::trace!( "background thread {}: ran runnable. took: {:?}", i, - start.elapsed() + started.elapsed() ); } }) @@ -110,36 +73,7 @@ impl LinuxDispatcher { calloop::timer::Timer::from_duration(timer.duration), move |_, _, _| { if let Some(runnable) = runnable.take() { - let start = Instant::now(); - let mut timing = match runnable { - RunnableVariant::Meta(runnable) => { - let location = runnable.metadata().location; - let timing = TaskTiming { - location, - start, - end: None, - }; - profiler::add_task_timing(timing); - - runnable.run(); - timing - } - RunnableVariant::Compat(runnable) => { - let timing = TaskTiming { - location: core::panic::Location::caller(), - start, - end: None, - }; - profiler::add_task_timing(timing); - - runnable.run(); - timing - } - }; - let end = Instant::now(); - - timing.end = Some(end); - profiler::add_task_timing(timing); + runnable.run_and_profile(); } TimeoutAction::Drop }, @@ -189,15 +123,15 @@ impl PlatformDispatcher for LinuxDispatcher { thread::current().id() == self.main_thread_id } - fn dispatch(&self, runnable: RunnableVariant, _: Option, priority: Priority) { + fn dispatch(&self, runnable: GpuiRunnable, _: Option) { self.background_sender - .send(priority, runnable) + .send(runnable.priority(), runnable) .unwrap_or_else(|_| panic!("blocking sender returned without value")); } - fn dispatch_on_main_thread(&self, runnable: RunnableVariant, priority: Priority) { + fn dispatch_on_main_thread(&self, runnable: GpuiRunnable) { self.main_sender - .send(priority, runnable) + .send(runnable.priority(), runnable) .unwrap_or_else(|runnable| { // NOTE: Runnable may wrap a Future that is !Send. // @@ -211,7 +145,7 @@ impl PlatformDispatcher for LinuxDispatcher { }); } - fn dispatch_after(&self, duration: Duration, runnable: RunnableVariant) { + fn dispatch_after(&self, duration: Duration, runnable: GpuiRunnable) { self.timer_sender .send(TimerAfter { duration, runnable }) .ok(); diff --git a/crates/gpui/src/platform/linux/headless/client.rs b/crates/gpui/src/platform/linux/headless/client.rs index 214e9c12aec9d8..becedf920ae075 100644 --- a/crates/gpui/src/platform/linux/headless/client.rs +++ b/crates/gpui/src/platform/linux/headless/client.rs @@ -31,10 +31,7 @@ impl HeadlessClient { handle .insert_source(main_receiver, |event, _, _: &mut HeadlessClient| { if let calloop::channel::Event::Msg(runnable) = event { - match runnable { - crate::RunnableVariant::Meta(runnable) => runnable.run(), - crate::RunnableVariant::Compat(runnable) => runnable.run(), - }; + runnable.run_unprofiled(); } }) .ok(); diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 381ab7cf577ad4..1fd703bc4523a1 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -23,10 +23,10 @@ use xkbcommon::xkb::{self, Keycode, Keysym, State}; use crate::{ Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, - ForegroundExecutor, Keymap, LinuxDispatcher, Menu, MenuItem, OwnedMenu, PathPromptOptions, - Pixels, Platform, PlatformDisplay, PlatformKeyboardLayout, PlatformKeyboardMapper, - PlatformTextSystem, PlatformWindow, Point, PriorityQueueCalloopReceiver, Result, - RunnableVariant, Task, WindowAppearance, WindowParams, px, + ForegroundExecutor, GpuiRunnable, Keymap, LinuxDispatcher, Menu, MenuItem, OwnedMenu, + PathPromptOptions, Pixels, Platform, PlatformDisplay, PlatformKeyboardLayout, + PlatformKeyboardMapper, PlatformTextSystem, PlatformWindow, Point, + PriorityQueueCalloopReceiver, Result, Task, WindowAppearance, WindowParams, px, }; #[cfg(any(feature = "wayland", feature = "x11"))] @@ -152,7 +152,7 @@ impl LinuxCommon { pub fn new( signal: LoopSignal, liveness: std::sync::Weak<()>, - ) -> (Self, PriorityQueueCalloopReceiver) { + ) -> (Self, PriorityQueueCalloopReceiver) { let (main_sender, main_receiver) = PriorityQueueCalloopReceiver::new(); #[cfg(any(feature = "wayland", feature = "x11"))] diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 227791324efe55..bceb1eb0f9aa5d 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -73,17 +73,14 @@ use super::{ window::{ImeInput, WaylandWindowStatePtr}, }; +use crate::platform::{PlatformWindow, blade::BladeContext}; use crate::{ AnyWindowHandle, Bounds, Capslock, CursorStyle, DOUBLE_CLICK_INTERVAL, DevicePixels, DisplayId, FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon, LinuxKeyboardLayout, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, PlatformInput, PlatformKeyboardLayout, Point, ResultExt as _, SCROLL_LINES, ScrollDelta, - ScrollWheelEvent, Size, TouchPhase, WindowParams, point, profiler, px, size, -}; -use crate::{ - RunnableVariant, TaskTiming, - platform::{PlatformWindow, blade::BladeContext}, + ScrollWheelEvent, Size, TouchPhase, WindowParams, point, px, size, }; use crate::{ SharedString, @@ -498,38 +495,8 @@ impl WaylandClient { let handle = handle.clone(); move |event, _, _: &mut WaylandClientStatePtr| { if let calloop::channel::Event::Msg(runnable) = event { - handle.insert_idle(|_| { - let start = Instant::now(); - let mut timing = match runnable { - RunnableVariant::Meta(runnable) => { - let location = runnable.metadata().location; - let timing = TaskTiming { - location, - start, - end: None, - }; - profiler::add_task_timing(timing); - - runnable.run(); - timing - } - RunnableVariant::Compat(runnable) => { - let location = core::panic::Location::caller(); - let timing = TaskTiming { - location, - start, - end: None, - }; - profiler::add_task_timing(timing); - - runnable.run(); - timing - } - }; - - let end = Instant::now(); - timing.end = Some(end); - profiler::add_task_timing(timing); + handle.insert_idle(move |_| { + runnable.run_and_profile(); }); } } diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index 02e83518bc440c..66e214c888703e 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -1,4 +1,4 @@ -use crate::{Capslock, ResultExt as _, RunnableVariant, TaskTiming, profiler, xcb_flush}; +use crate::{Capslock, ResultExt as _, xcb_flush}; use anyhow::{Context as _, anyhow}; use ashpd::WindowIdentifier; use calloop::{ @@ -313,37 +313,7 @@ impl X11Client { // events have higher priority and runnables are only worked off after the event // callbacks. handle.insert_idle(|_| { - let start = Instant::now(); - let mut timing = match runnable { - RunnableVariant::Meta(runnable) => { - let location = runnable.metadata().location; - let timing = TaskTiming { - location, - start, - end: None, - }; - profiler::add_task_timing(timing); - - runnable.run(); - timing - } - RunnableVariant::Compat(runnable) => { - let location = core::panic::Location::caller(); - let timing = TaskTiming { - location, - start, - end: None, - }; - profiler::add_task_timing(timing); - - runnable.run(); - timing - } - }; - - let end = Instant::now(); - timing.end = Some(end); - profiler::add_task_timing(timing); + runnable.run_and_profile(); }); } } diff --git a/crates/gpui/src/platform/mac/dispatcher.rs b/crates/gpui/src/platform/mac/dispatcher.rs index c10998ed3da49d..ba9f37e1ccc281 100644 --- a/crates/gpui/src/platform/mac/dispatcher.rs +++ b/crates/gpui/src/platform/mac/dispatcher.rs @@ -3,8 +3,8 @@ #![allow(non_snake_case)] use crate::{ - GLOBAL_THREAD_TIMINGS, PlatformDispatcher, Priority, RealtimePriority, RunnableMeta, - RunnableVariant, THREAD_TIMINGS, TaskLabel, TaskTiming, ThreadTaskTimings, + GLOBAL_THREAD_TIMINGS, GpuiRunnable, PlatformDispatcher, Priority, RealtimePriority, + RunnableMeta, THREAD_TIMINGS, TaskLabel, TaskTiming, ThreadTaskTimings, }; use anyhow::Context; @@ -28,7 +28,7 @@ use std::{ ffi::c_void, mem::MaybeUninit, ptr::{NonNull, addr_of}, - time::{Duration, Instant}, + time::Duration, }; use util::ResultExt; @@ -69,25 +69,25 @@ impl PlatformDispatcher for MacDispatcher { is_main_thread == YES } - fn dispatch(&self, runnable: RunnableVariant, _: Option, priority: Priority) { + fn dispatch(&self, runnable: GpuiRunnable, _: Option) { + let queue_priority = match runnable.priority() { + Priority::Realtime(_) => unreachable!(), + Priority::High => DISPATCH_QUEUE_PRIORITY_HIGH as isize, + Priority::Medium => DISPATCH_QUEUE_PRIORITY_DEFAULT as isize, + Priority::Low => DISPATCH_QUEUE_PRIORITY_LOW as isize, + }; + let (context, trampoline) = match runnable { - RunnableVariant::Meta(runnable) => ( + GpuiRunnable::GpuiSpawned(runnable) => ( runnable.into_raw().as_ptr() as *mut c_void, Some(trampoline as unsafe extern "C" fn(*mut c_void)), ), - RunnableVariant::Compat(runnable) => ( + GpuiRunnable::DependencySpawned(runnable) => ( runnable.into_raw().as_ptr() as *mut c_void, Some(trampoline_compat as unsafe extern "C" fn(*mut c_void)), ), }; - let queue_priority = match priority { - Priority::Realtime(_) => unreachable!(), - Priority::High => DISPATCH_QUEUE_PRIORITY_HIGH as isize, - Priority::Medium => DISPATCH_QUEUE_PRIORITY_DEFAULT as isize, - Priority::Low => DISPATCH_QUEUE_PRIORITY_LOW as isize, - }; - unsafe { dispatch_async_f( dispatch_get_global_queue(queue_priority, 0), @@ -97,13 +97,13 @@ impl PlatformDispatcher for MacDispatcher { } } - fn dispatch_on_main_thread(&self, runnable: RunnableVariant, _priority: Priority) { + fn dispatch_on_main_thread(&self, runnable: GpuiRunnable) { let (context, trampoline) = match runnable { - RunnableVariant::Meta(runnable) => ( + GpuiRunnable::GpuiSpawned(runnable) => ( runnable.into_raw().as_ptr() as *mut c_void, Some(trampoline as unsafe extern "C" fn(*mut c_void)), ), - RunnableVariant::Compat(runnable) => ( + GpuiRunnable::DependencySpawned(runnable) => ( runnable.into_raw().as_ptr() as *mut c_void, Some(trampoline_compat as unsafe extern "C" fn(*mut c_void)), ), @@ -113,13 +113,13 @@ impl PlatformDispatcher for MacDispatcher { } } - fn dispatch_after(&self, duration: Duration, runnable: RunnableVariant) { + fn dispatch_after(&self, duration: Duration, runnable: GpuiRunnable) { let (context, trampoline) = match runnable { - RunnableVariant::Meta(runnable) => ( + GpuiRunnable::GpuiSpawned(runnable) => ( runnable.into_raw().as_ptr() as *mut c_void, Some(trampoline as unsafe extern "C" fn(*mut c_void)), ), - RunnableVariant::Compat(runnable) => ( + GpuiRunnable::DependencySpawned(runnable) => ( runnable.into_raw().as_ptr() as *mut c_void, Some(trampoline_compat as unsafe extern "C" fn(*mut c_void)), ), @@ -247,82 +247,19 @@ fn set_audio_thread_priority() -> anyhow::Result<()> { Ok(()) } +// Note we can not send through a GpuiRunnable as that would require allocating +// to keep the pointer alive. So we recreate it here. extern "C" fn trampoline(runnable: *mut c_void) { let task = unsafe { Runnable::::from_raw(NonNull::new_unchecked(runnable as *mut ())) }; - - let metadata = task.metadata(); - let location = metadata.location; - - if !metadata.is_app_alive() { - drop(task); - return; - } - - let start = Instant::now(); - let timing = TaskTiming { - location, - start, - end: None, - }; - - THREAD_TIMINGS.with(|timings| { - let mut timings = timings.lock(); - let timings = &mut timings.timings; - if let Some(last_timing) = timings.iter_mut().rev().next() { - if last_timing.location == timing.location { - return; - } - } - - timings.push_back(timing); - }); - - task.run(); - let end = Instant::now(); - - THREAD_TIMINGS.with(|timings| { - let mut timings = timings.lock(); - let timings = &mut timings.timings; - let Some(last_timing) = timings.iter_mut().rev().next() else { - return; - }; - last_timing.end = Some(end); - }); + let task = GpuiRunnable::GpuiSpawned(task); + task.run_and_profile(); } +// Note we can not send through a GpuiRunnable as that would require allocating +// to keep the pointer alive. So we recreate it here. extern "C" fn trampoline_compat(runnable: *mut c_void) { let task = unsafe { Runnable::<()>::from_raw(NonNull::new_unchecked(runnable as *mut ())) }; - - let location = core::panic::Location::caller(); - - let start = Instant::now(); - let timing = TaskTiming { - location, - start, - end: None, - }; - THREAD_TIMINGS.with(|timings| { - let mut timings = timings.lock(); - let timings = &mut timings.timings; - if let Some(last_timing) = timings.iter_mut().rev().next() { - if last_timing.location == timing.location { - return; - } - } - - timings.push_back(timing); - }); - - task.run(); - let end = Instant::now(); - - THREAD_TIMINGS.with(|timings| { - let mut timings = timings.lock(); - let timings = &mut timings.timings; - let Some(last_timing) = timings.iter_mut().rev().next() else { - return; - }; - last_timing.end = Some(end); - }); + let task = GpuiRunnable::DependencySpawned(task); + task.run_and_profile(); } diff --git a/crates/gpui/src/platform/test/dispatcher.rs b/crates/gpui/src/platform/test/dispatcher.rs index a94f0737f5e43d..05aa0687311c14 100644 --- a/crates/gpui/src/platform/test/dispatcher.rs +++ b/crates/gpui/src/platform/test/dispatcher.rs @@ -1,4 +1,4 @@ -use crate::{PlatformDispatcher, Priority, RunnableVariant, TaskLabel}; +use crate::{GpuiRunnable, PlatformDispatcher, TaskLabel}; use backtrace::Backtrace; use collections::{HashMap, HashSet, VecDeque}; use parking::Unparker; @@ -25,10 +25,10 @@ pub struct TestDispatcher { struct TestDispatcherState { random: StdRng, - foreground: HashMap>, - background: Vec, - deprioritized_background: Vec, - delayed: Vec<(Duration, RunnableVariant)>, + foreground: HashMap>, + background: Vec, + deprioritized_background: Vec, + delayed: Vec<(Duration, GpuiRunnable)>, start_time: Instant, time: Duration, is_main_thread: bool, @@ -176,21 +176,10 @@ impl TestDispatcher { drop(state); // todo(localcc): add timings to tests - match runnable { - RunnableVariant::Meta(runnable) => { - if !runnable.metadata().is_app_alive() { - drop(runnable); - } else { - runnable.run(); - } - } - RunnableVariant::Compat(runnable) => { - runnable.run(); - } - }; - + if !runnable.app_dropped() { + runnable.run_unprofiled() + } self.state.lock().is_main_thread = was_main_thread; - true } @@ -292,7 +281,7 @@ impl PlatformDispatcher for TestDispatcher { state.start_time + state.time } - fn dispatch(&self, runnable: RunnableVariant, label: Option, _priority: Priority) { + fn dispatch(&self, runnable: GpuiRunnable, label: Option) { { let mut state = self.state.lock(); if label.is_some_and(|label| state.deprioritized_task_labels.contains(&label)) { @@ -304,7 +293,7 @@ impl PlatformDispatcher for TestDispatcher { self.unpark_all(); } - fn dispatch_on_main_thread(&self, runnable: RunnableVariant, _priority: Priority) { + fn dispatch_on_main_thread(&self, runnable: GpuiRunnable) { self.state .lock() .foreground @@ -314,7 +303,7 @@ impl PlatformDispatcher for TestDispatcher { self.unpark_all(); } - fn dispatch_after(&self, duration: std::time::Duration, runnable: RunnableVariant) { + fn dispatch_after(&self, duration: std::time::Duration, runnable: GpuiRunnable) { let mut state = self.state.lock(); let next_time = state.time + duration; let ix = match state.delayed.binary_search_by_key(&next_time, |e| e.0) { diff --git a/crates/gpui/src/platform/windows/dispatcher.rs b/crates/gpui/src/platform/windows/dispatcher.rs index 0720d414c9b44d..24990ce0613e3d 100644 --- a/crates/gpui/src/platform/windows/dispatcher.rs +++ b/crates/gpui/src/platform/windows/dispatcher.rs @@ -1,7 +1,7 @@ use std::{ sync::atomic::{AtomicBool, Ordering}, thread::{ThreadId, current}, - time::{Duration, Instant}, + time::Duration, }; use anyhow::Context; @@ -21,14 +21,14 @@ use windows::{ }; use crate::{ - GLOBAL_THREAD_TIMINGS, HWND, PlatformDispatcher, Priority, PriorityQueueSender, - RealtimePriority, RunnableVariant, SafeHwnd, THREAD_TIMINGS, TaskLabel, TaskTiming, - ThreadTaskTimings, WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD, profiler, + GLOBAL_THREAD_TIMINGS, GpuiRunnable, HWND, PlatformDispatcher, Priority, PriorityQueueSender, + RealtimePriority, SafeHwnd, THREAD_TIMINGS, TaskLabel, ThreadTaskTimings, + WM_GPUI_TASK_DISPATCHED_ON_MAIN_THREAD, }; pub(crate) struct WindowsDispatcher { pub(crate) wake_posted: AtomicBool, - main_sender: PriorityQueueSender, + main_sender: PriorityQueueSender, main_thread_id: ThreadId, pub(crate) platform_window_handle: SafeHwnd, validation_number: usize, @@ -36,7 +36,7 @@ pub(crate) struct WindowsDispatcher { impl WindowsDispatcher { pub(crate) fn new( - main_sender: PriorityQueueSender, + main_sender: PriorityQueueSender, platform_window_handle: HWND, validation_number: usize, ) -> Self { @@ -52,11 +52,14 @@ impl WindowsDispatcher { } } - fn dispatch_on_threadpool(&self, priority: WorkItemPriority, runnable: RunnableVariant) { + fn dispatch_on_threadpool(&self, runnable: GpuiRunnable, priority: WorkItemPriority) { let handler = { - let mut task_wrapper = Some(runnable); + let mut runnable = Some(runnable); WorkItemHandler::new(move |_| { - Self::execute_runnable(task_wrapper.take().unwrap()); + runnable + .take() + .expect("Takes FnMut but only runs once") + .run_and_profile(); Ok(()) }) }; @@ -64,54 +67,19 @@ impl WindowsDispatcher { ThreadPool::RunWithPriorityAsync(&handler, priority).log_err(); } - fn dispatch_on_threadpool_after(&self, runnable: RunnableVariant, duration: Duration) { + fn dispatch_on_threadpool_after(&self, runnable: GpuiRunnable, duration: Duration) { let handler = { - let mut task_wrapper = Some(runnable); + let mut runnable = Some(runnable); TimerElapsedHandler::new(move |_| { - Self::execute_runnable(task_wrapper.take().unwrap()); + runnable + .take() + .expect("Takes FnMut but only runs once") + .run_and_profile(); Ok(()) }) }; ThreadPoolTimer::CreateTimer(&handler, duration.into()).log_err(); } - - #[inline(always)] - pub(crate) fn execute_runnable(runnable: RunnableVariant) { - let start = Instant::now(); - - let mut timing = match runnable { - RunnableVariant::Meta(runnable) => { - let location = runnable.metadata().location; - let timing = TaskTiming { - location, - start, - end: None, - }; - profiler::add_task_timing(timing); - - runnable.run(); - - timing - } - RunnableVariant::Compat(runnable) => { - let timing = TaskTiming { - location: core::panic::Location::caller(), - start, - end: None, - }; - profiler::add_task_timing(timing); - - runnable.run(); - - timing - } - }; - - let end = Instant::now(); - timing.end = Some(end); - - profiler::add_task_timing(timing); - } } impl PlatformDispatcher for WindowsDispatcher { @@ -138,22 +106,21 @@ impl PlatformDispatcher for WindowsDispatcher { current().id() == self.main_thread_id } - fn dispatch(&self, runnable: RunnableVariant, label: Option, priority: Priority) { - let priority = match priority { + fn dispatch(&self, runnable: GpuiRunnable, label: Option) { + let priority = match runnable.priority() { Priority::Realtime(_) => unreachable!(), Priority::High => WorkItemPriority::High, Priority::Medium => WorkItemPriority::Normal, Priority::Low => WorkItemPriority::Low, }; - self.dispatch_on_threadpool(priority, runnable); - + self.dispatch_on_threadpool(runnable, priority); if let Some(label) = label { log::debug!("TaskLabel: {label:?}"); } } - fn dispatch_on_main_thread(&self, runnable: RunnableVariant, priority: Priority) { - match self.main_sender.send(priority, runnable) { + fn dispatch_on_main_thread(&self, runnable: GpuiRunnable) { + match self.main_sender.send(runnable.priority(), runnable) { Ok(_) => { if !self.wake_posted.swap(true, Ordering::AcqRel) { unsafe { @@ -181,7 +148,7 @@ impl PlatformDispatcher for WindowsDispatcher { } } - fn dispatch_after(&self, duration: Duration, runnable: RunnableVariant) { + fn dispatch_after(&self, duration: Duration, runnable: GpuiRunnable) { self.dispatch_on_threadpool_after(runnable, duration); } diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 97c3a12bb028cc..9ff2fc4e14cd94 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -250,7 +250,7 @@ impl WindowsWindowInner { if wparam.0 == SIZE_MOVE_LOOP_TIMER_ID { let mut runnables = self.main_receiver.clone().try_iter(); while let Some(Ok(runnable)) = runnables.next() { - WindowsDispatcher::execute_runnable(runnable); + runnable.run_and_profile(); } self.handle_paint_msg(handle) } else { diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 7b86a9ab876a27..3da902f6abab0c 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -51,7 +51,7 @@ struct WindowsPlatformInner { raw_window_handles: std::sync::Weak>>, // The below members will never change throughout the entire lifecycle of the app. validation_number: usize, - main_receiver: PriorityQueueReceiver, + main_receiver: PriorityQueueReceiver, dispatcher: Arc, } @@ -856,7 +856,7 @@ impl WindowsPlatformInner { } let mut main_receiver = self.main_receiver.clone(); match main_receiver.try_pop() { - Ok(Some(runnable)) => WindowsDispatcher::execute_runnable(runnable), + Ok(Some(runnable)) => _ = runnable.run_and_profile(), _ => break 'timeout_loop, } } @@ -868,8 +868,7 @@ impl WindowsPlatformInner { match main_receiver.try_pop() { Ok(Some(runnable)) => { self.dispatcher.wake_posted.store(true, Ordering::Release); - - WindowsDispatcher::execute_runnable(runnable); + runnable.run_and_profile(); } _ => break 'tasks, } @@ -933,7 +932,7 @@ pub(crate) struct WindowCreationInfo { pub(crate) windows_version: WindowsVersion, pub(crate) drop_target_helper: IDropTargetHelper, pub(crate) validation_number: usize, - pub(crate) main_receiver: PriorityQueueReceiver, + pub(crate) main_receiver: PriorityQueueReceiver, pub(crate) platform_window_handle: HWND, pub(crate) disable_direct_composition: bool, pub(crate) directx_devices: DirectXDevices, @@ -946,8 +945,8 @@ struct PlatformWindowCreateContext { inner: Option>>, raw_window_handles: std::sync::Weak>>, validation_number: usize, - main_sender: Option>, - main_receiver: Option>, + main_sender: Option>, + main_receiver: Option>, directx_devices: Option, dispatcher: Option>, } diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index 837474a9da3217..5d74b98e2a489f 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -82,7 +82,7 @@ pub(crate) struct WindowsWindowInner { pub(crate) executor: ForegroundExecutor, pub(crate) windows_version: WindowsVersion, pub(crate) validation_number: usize, - pub(crate) main_receiver: PriorityQueueReceiver, + pub(crate) main_receiver: PriorityQueueReceiver, pub(crate) platform_window_handle: HWND, pub(crate) parent_hwnd: Option, } @@ -366,7 +366,7 @@ struct WindowCreateContext { windows_version: WindowsVersion, drop_target_helper: IDropTargetHelper, validation_number: usize, - main_receiver: PriorityQueueReceiver, + main_receiver: PriorityQueueReceiver, platform_window_handle: HWND, appearance: WindowAppearance, disable_direct_composition: bool, diff --git a/crates/repl/src/repl.rs b/crates/repl/src/repl.rs index 346cca0211e43d..81ac8f72bc2a6a 100644 --- a/crates/repl/src/repl.rs +++ b/crates/repl/src/repl.rs @@ -12,7 +12,7 @@ mod session; use std::{sync::Arc, time::Duration}; use async_dispatcher::{Dispatcher, Runnable, set_dispatcher}; -use gpui::{App, PlatformDispatcher, Priority, RunnableVariant}; +use gpui::{App, GpuiRunnable, PlatformDispatcher}; use project::Fs; pub use runtimelib::ExecutionState; @@ -46,12 +46,12 @@ fn zed_dispatcher(cx: &mut App) -> impl Dispatcher { impl Dispatcher for ZedDispatcher { fn dispatch(&self, runnable: Runnable) { self.dispatcher - .dispatch(RunnableVariant::Compat(runnable), None, Priority::default()); + .dispatch(GpuiRunnable::DependencySpawned(runnable), None); } fn dispatch_after(&self, duration: Duration, runnable: Runnable) { self.dispatcher - .dispatch_after(duration, RunnableVariant::Compat(runnable)); + .dispatch_after(duration, GpuiRunnable::DependencySpawned(runnable)); } }