diff --git a/crates/gpui3/build.rs b/crates/gpui3/build.rs index 60849484268b93204d95db695cb41a2eb3c32403..861534aa6eb75874fa25c1213e0c118bf3ee33aa 100644 --- a/crates/gpui3/build.rs +++ b/crates/gpui3/build.rs @@ -19,6 +19,8 @@ fn generate_dispatch_bindings() { let bindings = bindgen::Builder::default() .header("src/platform/mac/dispatch.h") .allowlist_var("_dispatch_main_q") + .allowlist_var("DISPATCH_QUEUE_PRIORITY_DEFAULT") + .allowlist_function("dispatch_get_global_queue") .allowlist_function("dispatch_async_f") .parse_callbacks(Box::new(bindgen::CargoCallbacks)) .layout_tests(false) diff --git a/crates/gpui3/src/app.rs b/crates/gpui3/src/app.rs index 94025ff9a844391b5153bf15ca1c20510001accb..a05f9bb792c1aed7813aff03f28d18c79d6e6b9f 100644 --- a/crates/gpui3/src/app.rs +++ b/crates/gpui3/src/app.rs @@ -8,9 +8,9 @@ pub use model_context::*; use refineable::Refineable; use crate::{ - current_platform, image_cache::ImageCache, run_on_main, spawn_on_main, AssetSource, Context, - LayoutId, MainThread, MainThreadOnly, Platform, PlatformDispatcher, RootView, SvgRenderer, - TextStyle, TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId, + current_platform, image_cache::ImageCache, AssetSource, Context, Executor, LayoutId, + MainThread, MainThreadOnly, Platform, RootView, SvgRenderer, Task, TextStyle, + TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId, }; use anyhow::{anyhow, Result}; use collections::{HashMap, VecDeque}; @@ -50,15 +50,15 @@ impl App { asset_source: Arc, http_client: Arc, ) -> Self { - let dispatcher = platform.dispatcher(); + let executor = platform.executor(); let text_system = Arc::new(TextSystem::new(platform.text_system())); let entities = EntityMap::new(); let unit_entity = entities.redeem(entities.reserve(), ()); Self(Arc::new_cyclic(|this| { Mutex::new(MainThread::new(AppContext { this: this.clone(), - platform: MainThreadOnly::new(platform, dispatcher.clone()), - dispatcher, + platform: MainThreadOnly::new(platform, executor.clone()), + executor, text_system, svg_renderer: SvgRenderer::new(asset_source), image_cache: ImageCache::new(http_client), @@ -95,7 +95,7 @@ pub struct AppContext { platform: MainThreadOnly, text_system: Arc, pending_updates: usize, - pub(crate) dispatcher: Arc, + pub(crate) executor: Executor, pub(crate) svg_renderer: SvgRenderer, pub(crate) image_cache: ImageCache, pub(crate) text_style_stack: Vec, @@ -184,6 +184,10 @@ impl AppContext { AsyncContext(unsafe { mem::transmute(self.this.clone()) }) } + pub fn executor(&self) -> &Executor { + &self.executor + } + pub fn run_on_main( &mut self, f: impl FnOnce(&mut MainThread) -> R + Send + 'static, @@ -192,20 +196,22 @@ impl AppContext { R: Send + 'static, { let (tx, rx) = oneshot::channel(); - if self.dispatcher.is_main_thread() { + if self.executor.is_main_thread() { let _ = tx.send(f(unsafe { mem::transmute::<&mut AppContext, &mut MainThread>(self) })); } else { let this = self.this.upgrade().unwrap(); - let _ = run_on_main(self.dispatcher.clone(), move || { - let cx = &mut *this.lock(); - cx.update(|cx| { - let _ = tx.send(f(unsafe { - mem::transmute::<&mut Self, &mut MainThread>(cx) - })); + self.executor + .run_on_main(move || { + let cx = &mut *this.lock(); + cx.update(|cx| { + let _ = tx.send(f(unsafe { + mem::transmute::<&mut Self, &mut MainThread>(cx) + })); + }) }) - }); + .detach(); } async move { rx.await.unwrap() } } @@ -213,13 +219,13 @@ impl AppContext { pub fn spawn_on_main( &self, f: impl FnOnce(&mut MainThread) -> F + Send + 'static, - ) -> impl Future + ) -> Task where F: Future + 'static, R: Send + 'static, { let this = self.this.upgrade().unwrap(); - spawn_on_main(self.dispatcher.clone(), move || { + self.executor.spawn_on_main(move || { let cx = &mut *this.lock(); cx.update(|cx| { f(unsafe { mem::transmute::<&mut AppContext, &mut MainThread>(cx) }) diff --git a/crates/gpui3/src/executor.rs b/crates/gpui3/src/executor.rs index 6a4f7a13e9bd278a7afe415673b61cd315b5253d..7c4ab3b8e50c647dcafbc162f6d80acd2122602e 100644 --- a/crates/gpui3/src/executor.rs +++ b/crates/gpui3/src/executor.rs @@ -1,1103 +1,113 @@ -use crate::util; use crate::PlatformDispatcher; -use anyhow::{anyhow, Result}; -use async_task::Runnable; -use futures::channel::{mpsc, oneshot}; -use smol::{channel, prelude::*, Executor}; +use smol::prelude::*; use std::{ - any::Any, - fmt::{self}, - marker::PhantomData, - mem, pin::Pin, - rc::Rc, sync::Arc, task::{Context, Poll}, - thread, - time::Duration, }; -/// Enqueues the given closure to run on the application's event loop. -/// Returns the result asynchronously. -pub(crate) fn run_on_main( +#[derive(Clone)] +pub struct Executor { dispatcher: Arc, - func: F, -) -> impl Future -where - F: FnOnce() -> R + Send + 'static, - R: Send + 'static, -{ - let (tx, rx) = oneshot::channel(); - if dispatcher.is_main_thread() { - let _ = tx.send(func()); - } else { - let _ = spawn_on_main(dispatcher, move || async move { - let _ = tx.send(func()); - }); - } - async move { rx.await.unwrap() } -} - -/// Enqueues the given closure to be run on the application's event loop. The -/// closure returns a future which will be run to completion on the main thread. -pub(crate) fn spawn_on_main( - dispatcher: Arc, - func: impl FnOnce() -> F + Send + 'static, -) -> impl Future -where - F: Future + 'static, - R: Send + 'static, -{ - let (tx, rx) = oneshot::channel(); - let (runnable, task) = async_task::spawn( - { - let dispatcher = dispatcher.clone(); - async move { - let future = func(); - let _ = spawn_on_main_local(dispatcher, async move { - let result = future.await; - let _ = tx.send(result); - }); - } - }, - move |runnable| dispatcher.run_on_main_thread(runnable), - ); - runnable.schedule(); - task.detach(); - async move { rx.await.unwrap() } -} - -/// Enqueues the given closure to be run on the application's event loop. Must -/// be called on the main thread. -pub(crate) fn spawn_on_main_local( - dispatcher: Arc, - future: impl Future + 'static, -) -> impl Future -where - R: 'static, -{ - assert!(dispatcher.is_main_thread(), "must be called on main thread"); - - let (tx, rx) = oneshot::channel(); - let (runnable, task) = async_task::spawn_local( - async move { - let result = future.await; - let _ = tx.send(result); - }, - move |runnable| dispatcher.run_on_main_thread(runnable), - ); - runnable.schedule(); - task.detach(); - async move { rx.await.unwrap() } } -pub enum ForegroundExecutor { - Platform { - dispatcher: Arc, - _not_send_or_sync: PhantomData>, - }, - #[cfg(any(test, feature = "test"))] - Deterministic { - cx_id: usize, - executor: Arc, - }, -} - -pub enum BackgroundExecutor { - #[cfg(any(test, feature = "test"))] - Deterministic { executor: Arc }, - Production { - executor: Arc>, - _stop: channel::Sender<()>, - }, -} - -type AnyLocalFuture = Pin>>>; -type AnyFuture = Pin>>>; -type AnyTask = async_task::Task>; -type AnyLocalTask = async_task::Task>; - -#[must_use] pub enum Task { Ready(Option), - Local { - any_task: AnyLocalTask, - result_type: PhantomData, - }, - Send { - any_task: AnyTask, - result_type: PhantomData, - }, -} - -unsafe impl Send for Task {} - -#[cfg(any(test, feature = "test"))] -struct DeterministicState { - rng: rand::prelude::StdRng, - seed: u64, - scheduled_from_foreground: collections::HashMap>, - scheduled_from_background: Vec, - forbid_parking: bool, - block_on_ticks: std::ops::RangeInclusive, - now: std::time::Instant, - next_timer_id: usize, - pending_timers: Vec<(usize, std::time::Instant, postage::barrier::Sender)>, - waiting_backtrace: Option, - next_runnable_id: usize, - poll_history: Vec, - previous_poll_history: Option>, - enable_runnable_backtraces: bool, - runnable_backtraces: collections::HashMap, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum ExecutorEvent { - PollRunnable { id: usize }, - EnqueuRunnable { id: usize }, -} - -#[cfg(any(test, feature = "test"))] -struct ForegroundRunnable { - id: usize, - runnable: Runnable, - main: bool, -} - -#[cfg(any(test, feature = "test"))] -struct BackgroundRunnable { - id: usize, - runnable: Runnable, -} - -#[cfg(any(test, feature = "test"))] -pub struct Deterministic { - state: Arc>, - parker: parking_lot::Mutex, -} - -#[must_use] -pub enum Timer { - Production(smol::Timer), - #[cfg(any(test, feature = "test"))] - Deterministic(DeterministicTimer), -} - -#[cfg(any(test, feature = "test"))] -pub struct DeterministicTimer { - rx: postage::barrier::Receiver, - id: usize, - state: Arc>, -} - -#[cfg(any(test, feature = "test"))] -impl Deterministic { - pub fn new(seed: u64) -> Arc { - use rand::prelude::*; - - Arc::new(Self { - state: Arc::new(parking_lot::Mutex::new(DeterministicState { - rng: StdRng::seed_from_u64(seed), - seed, - scheduled_from_foreground: Default::default(), - scheduled_from_background: Default::default(), - forbid_parking: false, - block_on_ticks: 0..=1000, - now: std::time::Instant::now(), - next_timer_id: Default::default(), - pending_timers: Default::default(), - waiting_backtrace: None, - next_runnable_id: 0, - poll_history: Default::default(), - previous_poll_history: Default::default(), - enable_runnable_backtraces: false, - runnable_backtraces: Default::default(), - })), - parker: Default::default(), - }) - } - - pub fn execution_history(&self) -> Vec { - self.state.lock().poll_history.clone() - } - - pub fn set_previous_execution_history(&self, history: Option>) { - self.state.lock().previous_poll_history = history; - } - - pub fn enable_runnable_backtrace(&self) { - self.state.lock().enable_runnable_backtraces = true; - } - - pub fn runnable_backtrace(&self, runnable_id: usize) -> backtrace::Backtrace { - let mut backtrace = self.state.lock().runnable_backtraces[&runnable_id].clone(); - backtrace.resolve(); - backtrace - } - - pub fn build_background(self: &Arc) -> Arc { - Arc::new(BackgroundExecutor::Deterministic { - executor: self.clone(), - }) - } - - pub fn build_foreground(self: &Arc, id: usize) -> Rc { - Rc::new(ForegroundExecutor::Deterministic { - cx_id: id, - executor: self.clone(), - }) - } - - fn spawn_from_foreground( - &self, - cx_id: usize, - future: AnyLocalFuture, - main: bool, - ) -> AnyLocalTask { - let state = self.state.clone(); - let id; - { - let mut state = state.lock(); - id = util::post_inc(&mut state.next_runnable_id); - if state.enable_runnable_backtraces { - state - .runnable_backtraces - .insert(id, backtrace::Backtrace::new_unresolved()); - } - } - - let unparker = self.parker.lock().unparker(); - let (runnable, task) = async_task::spawn_local(future, move |runnable| { - let mut state = state.lock(); - state.push_to_history(ExecutorEvent::EnqueuRunnable { id }); - state - .scheduled_from_foreground - .entry(cx_id) - .or_default() - .push(ForegroundRunnable { id, runnable, main }); - unparker.unpark(); - }); - runnable.schedule(); - task - } - - fn spawn(&self, future: AnyFuture) -> AnyTask { - let state = self.state.clone(); - let id; - { - let mut state = state.lock(); - id = util::post_inc(&mut state.next_runnable_id); - if state.enable_runnable_backtraces { - state - .runnable_backtraces - .insert(id, backtrace::Backtrace::new_unresolved()); - } - } - - let unparker = self.parker.lock().unparker(); - let (runnable, task) = async_task::spawn(future, move |runnable| { - let mut state = state.lock(); - state - .poll_history - .push(ExecutorEvent::EnqueuRunnable { id }); - state - .scheduled_from_background - .push(BackgroundRunnable { id, runnable }); - unparker.unpark(); - }); - runnable.schedule(); - task - } - - fn run<'a>( - &self, - cx_id: usize, - main_future: Pin>>>, - ) -> Box { - use std::sync::atomic::{AtomicBool, Ordering::SeqCst}; - - let woken = Arc::new(AtomicBool::new(false)); - - let state = self.state.clone(); - let id; - { - let mut state = state.lock(); - id = util::post_inc(&mut state.next_runnable_id); - if state.enable_runnable_backtraces { - state - .runnable_backtraces - .insert(id, backtrace::Backtrace::new_unresolved()); - } - } - - let unparker = self.parker.lock().unparker(); - let (runnable, mut main_task) = unsafe { - async_task::spawn_unchecked(main_future, move |runnable| { - let state = &mut *state.lock(); - state - .scheduled_from_foreground - .entry(cx_id) - .or_default() - .push(ForegroundRunnable { - id: util::post_inc(&mut state.next_runnable_id), - runnable, - main: true, - }); - unparker.unpark(); - }) - }; - runnable.schedule(); - - loop { - if let Some(result) = self.run_internal(woken.clone(), Some(&mut main_task)) { - return result; - } - - if !woken.load(SeqCst) { - self.state.lock().will_park(); - } - - woken.store(false, SeqCst); - self.parker.lock().park(); - } - } - - pub fn run_until_parked(&self) { - use std::sync::atomic::AtomicBool; - let woken = Arc::new(AtomicBool::new(false)); - self.run_internal(woken, None); - } - - fn run_internal( - &self, - woken: Arc, - mut main_task: Option<&mut AnyLocalTask>, - ) -> Option> { - use rand::prelude::*; - use std::sync::atomic::Ordering::SeqCst; - - let unparker = self.parker.lock().unparker(); - let waker = waker_fn::waker_fn(move || { - woken.store(true, SeqCst); - unparker.unpark(); - }); - - let mut cx = Context::from_waker(&waker); - loop { - let mut state = self.state.lock(); - - if state.scheduled_from_foreground.is_empty() - && state.scheduled_from_background.is_empty() - { - if let Some(main_task) = main_task { - if let Poll::Ready(result) = main_task.poll(&mut cx) { - return Some(result); - } - } - - return None; - } - - if !state.scheduled_from_background.is_empty() && state.rng.gen() { - let background_len = state.scheduled_from_background.len(); - let ix = state.rng.gen_range(0..background_len); - let background_runnable = state.scheduled_from_background.remove(ix); - state.push_to_history(ExecutorEvent::PollRunnable { - id: background_runnable.id, - }); - drop(state); - background_runnable.runnable.run(); - } else if !state.scheduled_from_foreground.is_empty() { - let available_cx_ids = state - .scheduled_from_foreground - .keys() - .copied() - .collect::>(); - let cx_id_to_run = *available_cx_ids.iter().choose(&mut state.rng).unwrap(); - let scheduled_from_cx = state - .scheduled_from_foreground - .get_mut(&cx_id_to_run) - .unwrap(); - let foreground_runnable = scheduled_from_cx.remove(0); - if scheduled_from_cx.is_empty() { - state.scheduled_from_foreground.remove(&cx_id_to_run); - } - state.push_to_history(ExecutorEvent::PollRunnable { - id: foreground_runnable.id, - }); - - drop(state); - - foreground_runnable.runnable.run(); - if let Some(main_task) = main_task.as_mut() { - if foreground_runnable.main { - if let Poll::Ready(result) = main_task.poll(&mut cx) { - return Some(result); - } - } - } - } - } - } - - fn block(&self, future: &mut F, max_ticks: usize) -> Option - where - F: Unpin + Future, - { - use rand::prelude::*; - - let unparker = self.parker.lock().unparker(); - let waker = waker_fn::waker_fn(move || { - unparker.unpark(); - }); - - let mut cx = Context::from_waker(&waker); - for _ in 0..max_ticks { - let mut state = self.state.lock(); - let runnable_count = state.scheduled_from_background.len(); - let ix = state.rng.gen_range(0..=runnable_count); - if ix < state.scheduled_from_background.len() { - let background_runnable = state.scheduled_from_background.remove(ix); - state.push_to_history(ExecutorEvent::PollRunnable { - id: background_runnable.id, - }); - drop(state); - background_runnable.runnable.run(); - } else { - drop(state); - if let Poll::Ready(result) = future.poll(&mut cx) { - return Some(result); - } - let mut state = self.state.lock(); - if state.scheduled_from_background.is_empty() { - state.will_park(); - drop(state); - self.parker.lock().park(); - } - - continue; - } - } - - None - } - - pub fn timer(&self, duration: Duration) -> Timer { - let (tx, rx) = postage::barrier::channel(); - let mut state = self.state.lock(); - let wakeup_at = state.now + duration; - let id = util::post_inc(&mut state.next_timer_id); - match state - .pending_timers - .binary_search_by_key(&wakeup_at, |e| e.1) - { - Ok(ix) | Err(ix) => state.pending_timers.insert(ix, (id, wakeup_at, tx)), - } - let state = self.state.clone(); - Timer::Deterministic(DeterministicTimer { rx, id, state }) - } - - pub fn now(&self) -> std::time::Instant { - let state = self.state.lock(); - state.now - } - - pub fn advance_clock(&self, duration: Duration) { - let new_now = self.state.lock().now + duration; - loop { - self.run_until_parked(); - let mut state = self.state.lock(); - - if let Some((_, wakeup_time, _)) = state.pending_timers.first() { - let wakeup_time = *wakeup_time; - if wakeup_time <= new_now { - let timer_count = state - .pending_timers - .iter() - .take_while(|(_, t, _)| *t == wakeup_time) - .count(); - state.now = wakeup_time; - let timers_to_wake = state - .pending_timers - .drain(0..timer_count) - .collect::>(); - drop(state); - drop(timers_to_wake); - continue; - } - } - - break; - } - - self.state.lock().now = new_now; - } - - pub fn start_waiting(&self) { - self.state.lock().waiting_backtrace = Some(backtrace::Backtrace::new_unresolved()); - } - - pub fn finish_waiting(&self) { - self.state.lock().waiting_backtrace.take(); - } - - pub fn forbid_parking(&self) { - use rand::prelude::*; - - let mut state = self.state.lock(); - state.forbid_parking = true; - state.rng = StdRng::seed_from_u64(state.seed); - } - - pub fn allow_parking(&self) { - use rand::prelude::*; - - let mut state = self.state.lock(); - state.forbid_parking = false; - state.rng = StdRng::seed_from_u64(state.seed); - } - - pub async fn simulate_random_delay(&self) { - use rand::prelude::*; - use smol::future::yield_now; - if self.state.lock().rng.gen_bool(0.2) { - let yields = self.state.lock().rng.gen_range(1..=10); - for _ in 0..yields { - yield_now().await; - } - } - } - - pub fn record_backtrace(&self) { - let mut state = self.state.lock(); - if state.enable_runnable_backtraces { - let current_id = state - .poll_history - .iter() - .rev() - .find_map(|event| match event { - ExecutorEvent::PollRunnable { id } => Some(*id), - _ => None, - }); - if let Some(id) = current_id { - state - .runnable_backtraces - .insert(id, backtrace::Backtrace::new_unresolved()); - } - } - } -} - -impl Drop for Timer { - fn drop(&mut self) { - #[cfg(any(test, feature = "test"))] - if let Timer::Deterministic(DeterministicTimer { state, id, .. }) = self { - state - .lock() - .pending_timers - .retain(|(timer_id, _, _)| timer_id != id) - } - } -} - -impl Future for Timer { - type Output = (); - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match &mut *self { - #[cfg(any(test, feature = "test"))] - Self::Deterministic(DeterministicTimer { rx, .. }) => { - use postage::stream::{PollRecv, Stream as _}; - smol::pin!(rx); - match rx.poll_recv(&mut postage::Context::from_waker(cx.waker())) { - PollRecv::Ready(()) | PollRecv::Closed => Poll::Ready(()), - PollRecv::Pending => Poll::Pending, - } - } - Self::Production(timer) => { - smol::pin!(timer); - match timer.poll(cx) { - Poll::Ready(_) => Poll::Ready(()), - Poll::Pending => Poll::Pending, - } - } - } - } + Spawned(async_task::Task), } -#[cfg(any(test, feature = "test"))] -impl DeterministicState { - fn push_to_history(&mut self, event: ExecutorEvent) { - use std::fmt::Write as _; - - self.poll_history.push(event); - if let Some(prev_history) = &self.previous_poll_history { - let ix = self.poll_history.len() - 1; - let prev_event = prev_history[ix]; - if event != prev_event { - let mut message = String::new(); - writeln!( - &mut message, - "current runnable backtrace:\n{:?}", - self.runnable_backtraces.get_mut(&event.id()).map(|trace| { - trace.resolve(); - crate::util::CwdBacktrace(trace) - }) - ) - .unwrap(); - writeln!( - &mut message, - "previous runnable backtrace:\n{:?}", - self.runnable_backtraces - .get_mut(&prev_event.id()) - .map(|trace| { - trace.resolve(); - util::CwdBacktrace(trace) - }) - ) - .unwrap(); - panic!("detected non-determinism after {ix}. {message}"); - } - } - } - - fn will_park(&mut self) { - if self.forbid_parking { - let mut backtrace_message = String::new(); - #[cfg(any(test, feature = "test"))] - if let Some(backtrace) = self.waiting_backtrace.as_mut() { - backtrace.resolve(); - backtrace_message = format!( - "\nbacktrace of waiting future:\n{:?}", - util::CwdBacktrace(backtrace) - ); - } - - panic!( - "deterministic executor parked after a call to forbid_parking{}", - backtrace_message - ); - } +impl Task { + pub fn ready(val: T) -> Self { + Task::Ready(Some(val)) } -} -#[cfg(any(test, feature = "test"))] -impl ExecutorEvent { - pub fn id(&self) -> usize { + pub fn detach(self) { match self { - ExecutorEvent::PollRunnable { id } => *id, - ExecutorEvent::EnqueuRunnable { id } => *id, + Task::Ready(_) => {} + Task::Spawned(task) => task.detach(), } } } -impl ForegroundExecutor { - pub fn new(dispatcher: Arc) -> Result { - if dispatcher.is_main_thread() { - Ok(Self::Platform { - dispatcher, - _not_send_or_sync: PhantomData, - }) - } else { - Err(anyhow!("must be constructed on main thread")) - } - } - - pub fn spawn(&self, future: impl Future + 'static) -> Task { - let future = any_local_future(future); - let any_task = match self { - #[cfg(any(test, feature = "test"))] - Self::Deterministic { cx_id, executor } => { - executor.spawn_from_foreground(*cx_id, future, false) - } - Self::Platform { dispatcher, .. } => { - fn spawn_inner( - future: AnyLocalFuture, - dispatcher: &Arc, - ) -> AnyLocalTask { - let dispatcher = dispatcher.clone(); - let schedule = - move |runnable: Runnable| dispatcher.run_on_main_thread(runnable); - let (runnable, task) = async_task::spawn_local(future, schedule); - runnable.schedule(); - task - } - spawn_inner(future, dispatcher) - } - }; - Task::local(any_task) - } - - #[cfg(any(test, feature = "test"))] - pub fn run(&self, future: impl Future) -> T { - let future = async move { Box::new(future.await) as Box }.boxed_local(); - let result = match self { - Self::Deterministic { cx_id, executor } => executor.run(*cx_id, future), - Self::Platform { .. } => panic!("you can't call run on a platform foreground executor"), - }; - *result.downcast().unwrap() - } - - #[cfg(any(test, feature = "test"))] - pub fn run_until_parked(&self) { - match self { - Self::Deterministic { executor, .. } => executor.run_until_parked(), - _ => panic!("this method can only be called on a deterministic executor"), - } - } - - #[cfg(any(test, feature = "test"))] - pub fn parking_forbidden(&self) -> bool { - match self { - Self::Deterministic { executor, .. } => executor.state.lock().forbid_parking, - _ => panic!("this method can only be called on a deterministic executor"), - } - } - - #[cfg(any(test, feature = "test"))] - pub fn start_waiting(&self) { - match self { - Self::Deterministic { executor, .. } => executor.start_waiting(), - _ => panic!("this method can only be called on a deterministic executor"), - } - } - - #[cfg(any(test, feature = "test"))] - pub fn finish_waiting(&self) { - match self { - Self::Deterministic { executor, .. } => executor.finish_waiting(), - _ => panic!("this method can only be called on a deterministic executor"), - } - } - - #[cfg(any(test, feature = "test"))] - pub fn forbid_parking(&self) { - match self { - Self::Deterministic { executor, .. } => executor.forbid_parking(), - _ => panic!("this method can only be called on a deterministic executor"), - } - } - - #[cfg(any(test, feature = "test"))] - pub fn allow_parking(&self) { - match self { - Self::Deterministic { executor, .. } => executor.allow_parking(), - _ => panic!("this method can only be called on a deterministic executor"), - } - } - - #[cfg(any(test, feature = "test"))] - pub fn advance_clock(&self, duration: Duration) { - match self { - Self::Deterministic { executor, .. } => executor.advance_clock(duration), - _ => panic!("this method can only be called on a deterministic executor"), - } - } +impl Future for Task { + type Output = T; - #[cfg(any(test, feature = "test"))] - pub fn set_block_on_ticks(&self, range: std::ops::RangeInclusive) { - match self { - Self::Deterministic { executor, .. } => executor.state.lock().block_on_ticks = range, - _ => panic!("this method can only be called on a deterministic executor"), + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match unsafe { self.get_unchecked_mut() } { + Task::Ready(val) => Poll::Ready(val.take().unwrap()), + Task::Spawned(task) => task.poll(cx), } } } -impl BackgroundExecutor { - pub fn new() -> Self { - let executor = Arc::new(Executor::new()); - let stop = channel::unbounded::<()>(); - - for i in 0..2 * num_cpus::get() { - let executor = executor.clone(); - let stop = stop.1.clone(); - thread::Builder::new() - .name(format!("background-executor-{}", i)) - .spawn(move || smol::block_on(executor.run(stop.recv()))) - .unwrap(); - } - - Self::Production { - executor, - _stop: stop.0, - } - } - - pub fn num_cpus(&self) -> usize { - num_cpus::get() +impl Executor { + pub fn new(dispatcher: Arc) -> Self { + Self { dispatcher } } - pub fn spawn(&self, future: F) -> Task + /// Enqueues the given closure to be run on any thread. The closure returns + /// a future which will be run to completion on any available thread. + pub fn spawn(&self, future: impl Future + Send + 'static) -> Task where - T: 'static + Send, - F: Send + Future + 'static, + R: Send + 'static, { - let future = any_future(future); - let any_task = match self { - Self::Production { executor, .. } => executor.spawn(future), - #[cfg(any(test, feature = "test"))] - Self::Deterministic { executor } => executor.spawn(future), - }; - Task::send(any_task) - } - - pub fn block(&self, future: F) -> T - where - F: Future, - { - smol::pin!(future); - match self { - Self::Production { .. } => smol::block_on(&mut future), - #[cfg(any(test, feature = "test"))] - Self::Deterministic { executor, .. } => { - executor.block(&mut future, usize::MAX).unwrap() - } - } + let dispatcher = self.dispatcher.clone(); + let (runnable, task) = + async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable)); + runnable.schedule(); + Task::Spawned(task) } - pub fn block_with_timeout( - &self, - timeout: Duration, - future: F, - ) -> Result> + /// Enqueues the given closure to run on the application's event loop. + /// Returns the result asynchronously. + pub fn run_on_main(&self, func: F) -> Task where - T: 'static, - F: 'static + Unpin + Future, + F: FnOnce() -> R + Send + 'static, + R: Send + 'static, { - let mut future = any_local_future(future); - if !timeout.is_zero() { - let output = match self { - Self::Production { .. } => smol::block_on(util::timeout(timeout, &mut future)).ok(), - #[cfg(any(test, feature = "test"))] - Self::Deterministic { executor, .. } => { - use rand::prelude::*; - let max_ticks = { - let mut state = executor.state.lock(); - let range = state.block_on_ticks.clone(); - state.rng.gen_range(range) - }; - executor.block(&mut future, max_ticks) - } - }; - if let Some(output) = output { - return Ok(*output.downcast().unwrap()); - } + if self.dispatcher.is_main_thread() { + Task::ready(func()) + } else { + self.spawn_on_main(move || async move { func() }) } - Err(async { *future.await.downcast().unwrap() }) } - pub async fn scoped<'scope, F>(self: &Arc, scheduler: F) + /// Enqueues the given closure to be run on the application's event loop. The + /// closure returns a future which will be run to completion on the main thread. + pub fn spawn_on_main(&self, func: impl FnOnce() -> F + Send + 'static) -> Task where - F: FnOnce(&mut Scope<'scope>), + F: Future + 'static, + R: Send + 'static, { - let mut scope = Scope::new(self.clone()); - (scheduler)(&mut scope); - let spawned = mem::take(&mut scope.futures) - .into_iter() - .map(|f| self.spawn(f)) - .collect::>(); - for task in spawned { - task.await; - } - } - - pub fn timer(&self, duration: Duration) -> Timer { - match self { - BackgroundExecutor::Production { .. } => { - Timer::Production(smol::Timer::after(duration)) - } - #[cfg(any(test, feature = "test"))] - BackgroundExecutor::Deterministic { executor } => executor.timer(duration), - } - } - - pub fn now(&self) -> std::time::Instant { - match self { - BackgroundExecutor::Production { .. } => std::time::Instant::now(), - #[cfg(any(test, feature = "test"))] - BackgroundExecutor::Deterministic { executor } => executor.now(), - } - } - - #[cfg(any(test, feature = "test"))] - pub fn rng<'a>(&'a self) -> impl 'a + std::ops::DerefMut { - match self { - Self::Deterministic { executor, .. } => { - parking_lot::lock_api::MutexGuard::map(executor.state.lock(), |s| &mut s.rng) - } - _ => panic!("this method can only be called on a deterministic executor"), - } - } - - #[cfg(any(test, feature = "test"))] - pub async fn simulate_random_delay(&self) { - match self { - Self::Deterministic { executor, .. } => { - executor.simulate_random_delay().await; - } - _ => { - panic!("this method can only be called on a deterministic executor") - } - } - } - - #[cfg(any(test, feature = "test"))] - pub fn record_backtrace(&self) { - match self { - Self::Deterministic { executor, .. } => executor.record_backtrace(), - _ => { - panic!("this method can only be called on a deterministic executor") - } - } - } - - #[cfg(any(test, feature = "test"))] - pub fn start_waiting(&self) { - match self { - Self::Deterministic { executor, .. } => executor.start_waiting(), - _ => panic!("this method can only be called on a deterministic executor"), - } - } -} - -impl Default for BackgroundExecutor { - fn default() -> Self { - Self::new() - } -} - -pub struct Scope<'a> { - executor: Arc, - futures: Vec + Send + 'static>>>, - tx: Option>, - rx: mpsc::Receiver<()>, - _phantom: PhantomData<&'a ()>, -} - -impl<'a> Scope<'a> { - fn new(executor: Arc) -> Self { - let (tx, rx) = mpsc::channel(1); - Self { - executor, - tx: Some(tx), - rx, - futures: Default::default(), - _phantom: PhantomData, - } + let dispatcher = self.dispatcher.clone(); + let (runnable, task) = + async_task::spawn_local(async move { func().await }, move |runnable| { + dispatcher.dispatch_on_main_thread(runnable) + }); + runnable.schedule(); + Task::Spawned(task) } - pub fn spawn(&mut self, f: F) + /// Enqueues the given closure to be run on the application's event loop. Must + /// be called on the main thread. + pub fn spawn_on_main_local(&self, future: impl Future + 'static) -> Task where - F: Future + Send + 'a, + R: 'static, { - let tx = self.tx.clone().unwrap(); - - // Safety: The 'a lifetime is guaranteed to outlive any of these futures because - // dropping this `Scope` blocks until all of the futures have resolved. - let f = unsafe { - mem::transmute::< - Pin + Send + 'a>>, - Pin + Send + 'static>>, - >(Box::pin(async move { - f.await; - drop(tx); - })) - }; - self.futures.push(f); - } -} - -impl<'a> Drop for Scope<'a> { - fn drop(&mut self) { - self.tx.take().unwrap(); - - // Wait until the channel is closed, which means that all of the spawned - // futures have resolved. - self.executor.block(self.rx.next()); - } -} + assert!( + self.dispatcher.is_main_thread(), + "must be called on main thread" + ); -impl Task { - pub fn ready(value: T) -> Self { - Self::Ready(Some(value)) - } - - fn local(any_task: AnyLocalTask) -> Self { - Self::Local { - any_task, - result_type: PhantomData, - } - } - - pub fn detach(self) { - match self { - Task::Ready(_) => {} - Task::Local { any_task, .. } => any_task.detach(), - Task::Send { any_task, .. } => any_task.detach(), - } - } -} - -// impl Task> { -// #[track_caller] -// pub fn detach_and_log_err(self, cx: &mut AppContext) { -// let caller = Location::caller(); -// cx.spawn(|_| async move { -// if let Err(err) = self.await { -// log::error!("{}:{}: {:#}", caller.file(), caller.line(), err); -// } -// }) -// .detach(); -// } -// } - -impl Task { - fn send(any_task: AnyTask) -> Self { - Self::Send { - any_task, - result_type: PhantomData, - } - } -} - -impl fmt::Debug for Task { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Task::Ready(value) => value.fmt(f), - Task::Local { any_task, .. } => any_task.fmt(f), - Task::Send { any_task, .. } => any_task.fmt(f), - } + let dispatcher = self.dispatcher.clone(); + let (runnable, task) = async_task::spawn_local(future, move |runnable| { + dispatcher.dispatch_on_main_thread(runnable) + }); + runnable.schedule(); + Task::Spawned(task) } -} -impl Future for Task { - type Output = T; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match unsafe { self.get_unchecked_mut() } { - Task::Ready(value) => Poll::Ready(value.take().unwrap()), - Task::Local { any_task, .. } => { - any_task.poll(cx).map(|value| *value.downcast().unwrap()) - } - Task::Send { any_task, .. } => { - any_task.poll(cx).map(|value| *value.downcast().unwrap()) - } - } + pub fn is_main_thread(&self) -> bool { + self.dispatcher.is_main_thread() } } - -fn any_future(future: F) -> AnyFuture -where - T: 'static + Send, - F: Future + Send + 'static, -{ - async { Box::new(future.await) as Box }.boxed() -} - -fn any_local_future(future: F) -> AnyLocalFuture -where - T: 'static, - F: Future + 'static, -{ - async { Box::new(future.await) as Box }.boxed_local() -} diff --git a/crates/gpui3/src/gpui3.rs b/crates/gpui3/src/gpui3.rs index 7c8c7c498c3303c9cd3e5c226d1375151c7f03e8..cb14679948f3b1f9905dba885e352b0263945825 100644 --- a/crates/gpui3/src/gpui3.rs +++ b/crates/gpui3/src/gpui3.rs @@ -227,14 +227,14 @@ impl<'a, T> DerefMut for Reference<'a, T> { } pub(crate) struct MainThreadOnly { - dispatcher: Arc, + executor: Executor, value: Arc, } impl Clone for MainThreadOnly { fn clone(&self) -> Self { Self { - dispatcher: self.dispatcher.clone(), + executor: self.executor.clone(), value: self.value.clone(), } } @@ -243,12 +243,12 @@ impl Clone for MainThreadOnly { /// Allows a value to be accessed only on the main thread, allowing a non-`Send` type /// to become `Send`. impl MainThreadOnly { - pub(crate) fn new(value: Arc, dispatcher: Arc) -> Self { - Self { dispatcher, value } + pub(crate) fn new(value: Arc, executor: Executor) -> Self { + Self { executor, value } } pub(crate) fn borrow_on_main_thread(&self) -> &T { - assert!(self.dispatcher.is_main_thread()); + assert!(self.executor.is_main_thread()); &self.value } } diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 91de2805606f8718966c6700a5064cbc06d9a844..9feec5a9c50dad1437377e31d128c5249a417787 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -7,8 +7,8 @@ mod test; use crate::image_cache::RenderImageParams; use crate::{ - AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point, - RenderGlyphParams, RenderSvgParams, Result, Scene, ShapedLine, SharedString, Size, + AnyWindowHandle, Bounds, DevicePixels, Executor, Font, FontId, FontMetrics, GlyphId, Pixels, + Point, RenderGlyphParams, RenderSvgParams, Result, Scene, ShapedLine, SharedString, Size, }; use anyhow::anyhow; use async_task::Runnable; @@ -43,7 +43,7 @@ pub(crate) fn current_platform() -> Arc { } pub trait Platform: 'static { - fn dispatcher(&self) -> Arc; + fn executor(&self) -> Executor; fn text_system(&self) -> Arc; fn run(&self, on_finish_launching: Box); @@ -154,7 +154,8 @@ pub trait PlatformWindow { pub trait PlatformDispatcher: Send + Sync { fn is_main_thread(&self) -> bool; - fn run_on_main_thread(&self, task: Runnable); + fn dispatch(&self, task: Runnable); + fn dispatch_on_main_thread(&self, task: Runnable); } pub trait PlatformTextSystem: Send + Sync { diff --git a/crates/gpui3/src/platform/mac/dispatcher.rs b/crates/gpui3/src/platform/mac/dispatcher.rs index f20ba602b0de3c4e510f1a069fdde557ad259b0e..df2f4785215228cc1820eeefcbc85e79a3858503 100644 --- a/crates/gpui3/src/platform/mac/dispatcher.rs +++ b/crates/gpui3/src/platform/mac/dispatcher.rs @@ -25,18 +25,49 @@ impl PlatformDispatcher for MacDispatcher { is_main_thread == YES } - fn run_on_main_thread(&self, runnable: Runnable) { + fn dispatch(&self, runnable: Runnable) { unsafe { dispatch_async_f( - dispatch_get_main_queue(), + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0), runnable.into_raw() as *mut c_void, Some(trampoline), ); } + } - extern "C" fn trampoline(runnable: *mut c_void) { - let task = unsafe { Runnable::from_raw(runnable as *mut ()) }; - task.run(); + fn dispatch_on_main_thread(&self, runnable: Runnable) { + unsafe { + dispatch_async_f( + dispatch_get_main_queue(), + runnable.into_raw() as *mut c_void, + Some(trampoline), + ); } } } + +extern "C" fn trampoline(runnable: *mut c_void) { + let task = unsafe { Runnable::from_raw(runnable as *mut ()) }; + task.run(); +} + +// #include + +// int main(void) { + +// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ +// // Do some lengthy background work here... +// printf("Background Work\n"); + +// dispatch_async(dispatch_get_main_queue(), ^{ +// // Once done, update your UI on the main queue here. +// printf("UI Updated\n"); + +// }); +// }); + +// sleep(3); // prevent the program from terminating immediately + +// return 0; +// } +// ``` diff --git a/crates/gpui3/src/platform/mac/platform.rs b/crates/gpui3/src/platform/mac/platform.rs index cde57f8bcfdd262fbd14b1326c4c7637f853dfc4..d4110a114ed6f9dfacc4e77827b40faab6d3563d 100644 --- a/crates/gpui3/src/platform/mac/platform.rs +++ b/crates/gpui3/src/platform/mac/platform.rs @@ -1,8 +1,8 @@ use super::BoolExt; use crate::{ - AnyWindowHandle, ClipboardItem, CursorStyle, Event, MacDispatcher, MacScreen, MacTextSystem, - MacWindow, PathPromptOptions, Platform, PlatformScreen, PlatformTextSystem, PlatformWindow, - Result, ScreenId, SemanticVersion, WindowOptions, + AnyWindowHandle, ClipboardItem, CursorStyle, Event, Executor, MacDispatcher, MacScreen, + MacTextSystem, MacWindow, PathPromptOptions, Platform, PlatformScreen, PlatformTextSystem, + PlatformWindow, Result, ScreenId, SemanticVersion, WindowOptions, }; use anyhow::anyhow; use block::ConcreteBlock; @@ -142,7 +142,7 @@ unsafe fn build_classes() { pub struct MacPlatform(Mutex); pub struct MacPlatformState { - dispatcher: Arc, + executor: Executor, text_system: Arc, pasteboard: id, text_hash_pasteboard_type: id, @@ -163,7 +163,7 @@ pub struct MacPlatformState { impl MacPlatform { pub fn new() -> Self { Self(Mutex::new(MacPlatformState { - dispatcher: Arc::new(MacDispatcher), + executor: Executor::new(Arc::new(MacDispatcher)), text_system: Arc::new(MacTextSystem::new()), pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) }, text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") }, @@ -343,8 +343,8 @@ impl MacPlatform { } impl Platform for MacPlatform { - fn dispatcher(&self) -> Arc { - Arc::new(MacDispatcher) + fn executor(&self) -> Executor { + self.0.lock().executor.clone() } fn text_system(&self) -> Arc { @@ -479,7 +479,7 @@ impl Platform for MacPlatform { handle: AnyWindowHandle, options: WindowOptions, ) -> Box { - Box::new(MacWindow::open(handle, options, self)) + Box::new(MacWindow::open(handle, options, self.executor())) } fn open_url(&self, url: &str) { @@ -566,17 +566,20 @@ impl Platform for MacPlatform { fn reveal_path(&self, path: &Path) { unsafe { let path = path.to_path_buf(); - let dispatcher = self.0.lock().dispatcher.clone(); - let _ = crate::spawn_on_main_local(dispatcher, async move { - let full_path = ns_string(path.to_str().unwrap_or("")); - let root_full_path = ns_string(""); - let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace]; - let _: BOOL = msg_send![ - workspace, - selectFile: full_path - inFileViewerRootedAtPath: root_full_path - ]; - }); + self.0 + .lock() + .executor + .spawn_on_main_local(async move { + let full_path = ns_string(path.to_str().unwrap_or("")); + let root_full_path = ns_string(""); + let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace]; + let _: BOOL = msg_send![ + workspace, + selectFile: full_path + inFileViewerRootedAtPath: root_full_path + ]; + }) + .detach(); } } diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index 6d52e44316b49ed7833de4adbfb032d8464d49b9..080aa74350551728a2226bb32d8da16cb9db4811 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -1,10 +1,10 @@ use super::{ns_string, MetalRenderer, NSRange}; use crate::{ - point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers, - ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt, - Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen, - PlatformWindow, Point, Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, - WindowOptions, WindowPromptLevel, + point, px, size, AnyWindowHandle, Bounds, Event, Executor, KeyDownEvent, Keystroke, MacScreen, + Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, + NSRectExt, Pixels, PlatformAtlas, PlatformInputHandler, PlatformScreen, PlatformWindow, Point, + Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, + WindowPromptLevel, }; use block::ConcreteBlock; use cocoa::{ @@ -279,7 +279,7 @@ struct InsertText { struct MacWindowState { handle: AnyWindowHandle, - dispatcher: Arc, + executor: Executor, native_window: id, renderer: MetalRenderer, scene_to_render: Option, @@ -415,7 +415,7 @@ unsafe impl Send for MacWindowState {} pub struct MacWindow(Arc>); impl MacWindow { - pub fn open(handle: AnyWindowHandle, options: WindowOptions, platform: &dyn Platform) -> Self { + pub fn open(handle: AnyWindowHandle, options: WindowOptions, executor: Executor) -> Self { unsafe { let pool = NSAutoreleasePool::new(nil); @@ -479,7 +479,7 @@ impl MacWindow { let window = Self(Arc::new(Mutex::new(MacWindowState { handle, - dispatcher: platform.dispatcher(), + executor, native_window, renderer: MetalRenderer::new(true), scene_to_render: None, @@ -616,12 +616,12 @@ impl MacWindow { impl Drop for MacWindow { fn drop(&mut self) { let this = self.0.clone(); - let dispatcher = self.0.lock().dispatcher.clone(); - let _ = crate::spawn_on_main(dispatcher, || async move { - unsafe { + let executor = self.0.lock().executor.clone(); + executor + .run_on_main(move || unsafe { this.lock().native_window.close(); - } - }); + }) + .detach(); } } @@ -739,14 +739,16 @@ impl PlatformWindow for MacWindow { }); let block = block.copy(); let native_window = self.0.lock().native_window; - let dispatcher = self.0.lock().dispatcher.clone(); - let _ = crate::spawn_on_main_local(dispatcher, async move { - let _: () = msg_send![ - alert, - beginSheetModalForWindow: native_window - completionHandler: block - ]; - }); + let executor = self.0.lock().executor.clone(); + executor + .spawn_on_main_local(async move { + let _: () = msg_send![ + alert, + beginSheetModalForWindow: native_window + completionHandler: block + ]; + }) + .detach(); done_rx } @@ -754,12 +756,14 @@ impl PlatformWindow for MacWindow { fn activate(&self) { let window = self.0.lock().native_window; - let dispatcher = self.0.lock().dispatcher.clone(); - let _ = crate::spawn_on_main_local(dispatcher.clone(), async move { - unsafe { - let _: () = msg_send![window, makeKeyAndOrderFront: nil]; - } - }); + let executor = self.0.lock().executor.clone(); + executor + .spawn_on_main_local(async move { + unsafe { + let _: () = msg_send![window, makeKeyAndOrderFront: nil]; + } + }) + .detach(); } fn set_title(&mut self, title: &str) { @@ -802,23 +806,25 @@ impl PlatformWindow for MacWindow { fn zoom(&self) { let this = self.0.lock(); let window = this.native_window; - let dispatcher = this.dispatcher.clone(); - let _ = crate::spawn_on_main_local(dispatcher, async move { - unsafe { - window.zoom_(nil); - } - }); + this.executor + .spawn_on_main_local(async move { + unsafe { + window.zoom_(nil); + } + }) + .detach(); } fn toggle_full_screen(&self) { let this = self.0.lock(); let window = this.native_window; - let dispatcher = this.dispatcher.clone(); - let _ = crate::spawn_on_main_local(dispatcher, async move { - unsafe { - window.toggleFullScreen_(nil); - } - }); + this.executor + .spawn_on_main_local(async move { + unsafe { + window.toggleFullScreen_(nil); + } + }) + .detach(); } fn on_event(&self, callback: Box bool>) { @@ -1114,15 +1120,14 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { }, ) => { lock.synthetic_drag_counter += 1; - let dispatcher = lock.dispatcher.clone(); - let _ = crate::spawn_on_main_local( - dispatcher, - synthetic_drag( + let executor = lock.executor.clone(); + executor + .spawn_on_main_local(synthetic_drag( weak_window_state, lock.synthetic_drag_counter, event.clone(), - ), - ); + )) + .detach(); } Event::MouseMoved(_) if !(is_active || lock.kind == WindowKind::PopUp) => return, @@ -1241,16 +1246,18 @@ extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) } } - let dispatcher = lock.dispatcher.clone(); + let executor = lock.executor.clone(); drop(lock); - let _ = crate::spawn_on_main_local(dispatcher, async move { - let mut lock = window_state.as_ref().lock(); - if let Some(mut callback) = lock.activate_callback.take() { - drop(lock); - callback(is_active); - window_state.lock().activate_callback = Some(callback); - }; - }); + executor + .spawn_on_main_local(async move { + let mut lock = window_state.as_ref().lock(); + if let Some(mut callback) = lock.activate_callback.take() { + drop(lock); + callback(is_active); + window_state.lock().activate_callback = Some(callback); + }; + }) + .detach(); } extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL { diff --git a/crates/gpui3/src/platform/test.rs b/crates/gpui3/src/platform/test.rs index 3b709f503f5211445fd08b790357260cc2cd75fb..e170e21e9554ff3faed657936fca613e80ba2cd4 100644 --- a/crates/gpui3/src/platform/test.rs +++ b/crates/gpui3/src/platform/test.rs @@ -1,5 +1,5 @@ use super::Platform; -use crate::ScreenId; +use crate::{Executor, ScreenId}; pub struct TestPlatform; @@ -11,7 +11,7 @@ impl TestPlatform { // todo!("implement out what our tests needed in GPUI 1") impl Platform for TestPlatform { - fn dispatcher(&self) -> std::sync::Arc { + fn executor(&self) -> Executor { unimplemented!() } diff --git a/crates/gpui3/src/util.rs b/crates/gpui3/src/util.rs index c76408392402a7a151ec93f54ea0a6aaeca0b720..5e60ab091aaada1d43b5d8e4055b27a103ead1bc 100644 --- a/crates/gpui3/src/util.rs +++ b/crates/gpui3/src/util.rs @@ -1,18 +1,16 @@ -use smol::future::FutureExt; -use std::{future::Future, time::Duration}; pub use util::*; -pub async fn timeout(timeout: Duration, f: F) -> Result -where - F: Future, -{ - let timer = async { - smol::Timer::after(timeout).await; - Err(()) - }; - let future = async move { Ok(f.await) }; - timer.race(future).await -} +// pub async fn timeout(timeout: Duration, f: F) -> Result +// where +// F: Future, +// { +// let timer = async { +// smol::Timer::after(timeout).await; +// Err(()) +// }; +// let future = async move { Ok(f.await) }; +// timer.race(future).await +// } #[cfg(any(test, feature = "test"))] pub struct CwdBacktrace<'a>(pub &'a backtrace::Backtrace); diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index e1a75791e4590733f721bb0b346f12d2baafacd4..5cf40e0a6698480e0ffe17d2564cc03ec1f3d826 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -53,8 +53,7 @@ impl Window { } })); - let platform_window = - MainThreadOnly::new(Arc::new(platform_window), cx.platform().dispatcher()); + let platform_window = MainThreadOnly::new(Arc::new(platform_window), cx.executor.clone()); Window { handle, @@ -122,7 +121,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { R: Send + 'static, { let (tx, rx) = oneshot::channel(); - if self.dispatcher.is_main_thread() { + if self.executor.is_main_thread() { let _ = tx.send(f(unsafe { mem::transmute::<&mut Self, &mut MainThread>(self) })); @@ -600,7 +599,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { R: Send + 'static, { let (tx, rx) = oneshot::channel(); - if self.dispatcher.is_main_thread() { + if self.executor.is_main_thread() { let cx = unsafe { mem::transmute::<&mut Self, &mut MainThread>(self) }; let _ = tx.send(f(view, cx)); } else {