Make tasks inherit their callers priority (#46179)

Additionally this extracts more functionality into the RunnableVariant which is renamed to GpuiRunnable. 

Release Notes:

- N/A

---------

Co-authored-by: Lukas Wirth <lukas@zed.dev>
Co-authored-by: Cole Miller <cole@zed.dev>
This commit is contained in:
Yara 🏳️‍⚧️ 2026-01-07 16:52:10 +01:00 committed by GitHub
parent c56b2253b4
commit 1ac2b977fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 210 additions and 368 deletions

View file

@ -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<Self>) {
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);
}
})

View file

@ -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<Priority> = 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<R>(&self, future: impl Future<Output = R> + 'static) -> Task<R>
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),
},
);

View file

@ -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<std::sync::Weak<()>>,
@ -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<RunnableMeta>),
Compat(Runnable),
pub enum GpuiRunnable {
/// Spawned by us, we set useful metadata for profiling and scheduling.
/// Yay we have nice things!
GpuiSpawned(Runnable<RunnableMeta>),
/// 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<ThreadTaskTimings>;
fn get_current_thread_timings(&self) -> Vec<TaskTiming>;
fn is_main_thread(&self) -> bool;
fn dispatch(&self, runnable: RunnableVariant, label: Option<TaskLabel>, 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<TaskLabel>);
fn dispatch_on_main_thread(&self, runnable: GpuiRunnable);
fn dispatch_after(&self, duration: Duration, runnable: GpuiRunnable);
fn spawn_realtime(&self, priority: RealtimePriority, f: Box<dyn FnOnce() + Send>);
fn now(&self) -> Instant {

View file

@ -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<RunnableVariant>,
main_sender: PriorityQueueCalloopSender<GpuiRunnable>,
timer_sender: Sender<TimerAfter>,
background_sender: PriorityQueueSender<RunnableVariant>,
background_sender: PriorityQueueSender<GpuiRunnable>,
_background_threads: Vec<thread::JoinHandle<()>>,
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<RunnableVariant>) -> Self {
pub fn new(main_sender: PriorityQueueCalloopSender<GpuiRunnable>) -> 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<GpuiRunnable> = 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<TaskLabel>, priority: Priority) {
fn dispatch(&self, runnable: GpuiRunnable, _: Option<TaskLabel>) {
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();

View file

@ -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();

View file

@ -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<RunnableVariant>) {
) -> (Self, PriorityQueueCalloopReceiver<GpuiRunnable>) {
let (main_sender, main_receiver) = PriorityQueueCalloopReceiver::new();
#[cfg(any(feature = "wayland", feature = "x11"))]

View file

@ -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();
});
}
}

View file

@ -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();
});
}
}

View file

@ -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<TaskLabel>, priority: Priority) {
let (context, trampoline) = match runnable {
RunnableVariant::Meta(runnable) => (
runnable.into_raw().as_ptr() as *mut c_void,
Some(trampoline as unsafe extern "C" fn(*mut c_void)),
),
RunnableVariant::Compat(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 {
fn dispatch(&self, runnable: GpuiRunnable, _: Option<TaskLabel>) {
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 {
GpuiRunnable::GpuiSpawned(runnable) => (
runnable.into_raw().as_ptr() as *mut c_void,
Some(trampoline as unsafe extern "C" fn(*mut c_void)),
),
GpuiRunnable::DependencySpawned(runnable) => (
runnable.into_raw().as_ptr() as *mut c_void,
Some(trampoline_compat as unsafe extern "C" fn(*mut c_void)),
),
};
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::<RunnableMeta>::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();
}

View file

@ -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<TestDispatcherId, VecDeque<RunnableVariant>>,
background: Vec<RunnableVariant>,
deprioritized_background: Vec<RunnableVariant>,
delayed: Vec<(Duration, RunnableVariant)>,
foreground: HashMap<TestDispatcherId, VecDeque<GpuiRunnable>>,
background: Vec<GpuiRunnable>,
deprioritized_background: Vec<GpuiRunnable>,
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<TaskLabel>, _priority: Priority) {
fn dispatch(&self, runnable: GpuiRunnable, label: Option<TaskLabel>) {
{
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) {

View file

@ -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<RunnableVariant>,
main_sender: PriorityQueueSender<GpuiRunnable>,
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<RunnableVariant>,
main_sender: PriorityQueueSender<GpuiRunnable>,
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<TaskLabel>, priority: Priority) {
let priority = match priority {
fn dispatch(&self, runnable: GpuiRunnable, label: Option<TaskLabel>) {
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);
}

View file

@ -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 {

View file

@ -51,7 +51,7 @@ struct WindowsPlatformInner {
raw_window_handles: std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
// The below members will never change throughout the entire lifecycle of the app.
validation_number: usize,
main_receiver: PriorityQueueReceiver<RunnableVariant>,
main_receiver: PriorityQueueReceiver<GpuiRunnable>,
dispatcher: Arc<WindowsDispatcher>,
}
@ -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<RunnableVariant>,
pub(crate) main_receiver: PriorityQueueReceiver<GpuiRunnable>,
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<Result<Rc<WindowsPlatformInner>>>,
raw_window_handles: std::sync::Weak<RwLock<SmallVec<[SafeHwnd; 4]>>>,
validation_number: usize,
main_sender: Option<PriorityQueueSender<RunnableVariant>>,
main_receiver: Option<PriorityQueueReceiver<RunnableVariant>>,
main_sender: Option<PriorityQueueSender<GpuiRunnable>>,
main_receiver: Option<PriorityQueueReceiver<GpuiRunnable>>,
directx_devices: Option<DirectXDevices>,
dispatcher: Option<Arc<WindowsDispatcher>>,
}

View file

@ -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<RunnableVariant>,
pub(crate) main_receiver: PriorityQueueReceiver<GpuiRunnable>,
pub(crate) platform_window_handle: HWND,
pub(crate) parent_hwnd: Option<HWND>,
}
@ -366,7 +366,7 @@ struct WindowCreateContext {
windows_version: WindowsVersion,
drop_target_helper: IDropTargetHelper,
validation_number: usize,
main_receiver: PriorityQueueReceiver<RunnableVariant>,
main_receiver: PriorityQueueReceiver<GpuiRunnable>,
platform_window_handle: HWND,
appearance: WindowAppearance,
disable_direct_composition: bool,

View file

@ -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));
}
}