wip

Max Brunsfeld created

Change summary

crates/ai2/src/auth.rs                            |  9 
crates/ai2/src/providers/open_ai/completion.rs    | 51 ++++-----
crates/audio2/src/audio2.rs                       | 72 +++---------
crates/client2/src/client2.rs                     | 90 +++++++---------
crates/client2/src/telemetry.rs                   |  4 
crates/editor/src/test/editor_lsp_test_context.rs |  6 
crates/fuzzy2/src/strings.rs                      |  4 
crates/gpui2/src/app.rs                           | 24 ++--
crates/gpui2/src/element.rs                       | 12 +-
crates/gpui2_macros/src/derive_component.rs       |  4 
crates/gpui2_macros/src/test.rs                   |  8 
crates/install_cli2/src/install_cli2.rs           |  4 
crates/language2/src/outline.rs                   |  4 
crates/lsp2/src/lsp2.rs                           |  3 
crates/terminal2/src/terminal2.rs                 | 18 +-
crates/vim/src/test/vim_test_context.rs           |  6 
16 files changed, 138 insertions(+), 181 deletions(-)

Detailed changes

crates/ai2/src/auth.rs 🔗

@@ -8,10 +8,9 @@ pub enum ProviderCredential {
     NotNeeded,
 }
 
-#[async_trait]
-pub trait CredentialProvider: Send + Sync {
+pub trait CredentialProvider {
     fn has_credentials(&self) -> bool;
-    async fn retrieve_credentials(&self, cx: &mut AppContext) -> ProviderCredential;
-    async fn save_credentials(&self, cx: &mut AppContext, credential: ProviderCredential);
-    async fn delete_credentials(&self, cx: &mut AppContext);
+    fn retrieve_credentials(&self, cx: &mut AppContext) -> ProviderCredential;
+    fn save_credentials(&self, cx: &mut AppContext, credential: ProviderCredential);
+    fn delete_credentials(&self, cx: &mut AppContext);
 }

crates/ai2/src/providers/open_ai/completion.rs 🔗

@@ -213,7 +213,6 @@ impl OpenAICompletionProvider {
     }
 }
 
-#[async_trait]
 impl CredentialProvider for OpenAICompletionProvider {
     fn has_credentials(&self) -> bool {
         match *self.credential.read() {
@@ -221,50 +220,44 @@ impl CredentialProvider for OpenAICompletionProvider {
             _ => false,
         }
     }
-    async fn retrieve_credentials(&self, cx: &mut AppContext) -> ProviderCredential {
-        let existing_credential = self.credential.read().clone();
-
-        let retrieved_credential = cx
-            .run_on_main(move |cx| match existing_credential {
-                ProviderCredential::Credentials { .. } => {
-                    return existing_credential.clone();
-                }
-                _ => {
-                    if let Some(api_key) = env::var("OPENAI_API_KEY").log_err() {
-                        return ProviderCredential::Credentials { api_key };
-                    }
 
-                    if let Some(Some((_, api_key))) = cx.read_credentials(OPENAI_API_URL).log_err()
-                    {
-                        if let Some(api_key) = String::from_utf8(api_key).log_err() {
-                            return ProviderCredential::Credentials { api_key };
-                        } else {
-                            return ProviderCredential::NoCredentials;
-                        }
+    fn retrieve_credentials(&self, cx: &mut AppContext) -> ProviderCredential {
+        let existing_credential = self.credential.read().clone();
+        let retrieved_credential = match existing_credential {
+            ProviderCredential::Credentials { .. } => existing_credential.clone(),
+            _ => {
+                if let Some(api_key) = env::var("OPENAI_API_KEY").log_err() {
+                    ProviderCredential::Credentials { api_key }
+                } else if let Some(Some((_, api_key))) =
+                    cx.read_credentials(OPENAI_API_URL).log_err()
+                {
+                    if let Some(api_key) = String::from_utf8(api_key).log_err() {
+                        ProviderCredential::Credentials { api_key }
                     } else {
-                        return ProviderCredential::NoCredentials;
+                        ProviderCredential::NoCredentials
                     }
+                } else {
+                    ProviderCredential::NoCredentials
                 }
-            })
-            .await;
-
+            }
+        };
         *self.credential.write() = retrieved_credential.clone();
         retrieved_credential
     }
 
-    async fn save_credentials(&self, cx: &mut AppContext, credential: ProviderCredential) {
+    fn save_credentials(&self, cx: &mut AppContext, credential: ProviderCredential) {
         *self.credential.write() = credential.clone();
         let credential = credential.clone();
-        cx.run_on_main(move |cx| match credential {
+        match credential {
             ProviderCredential::Credentials { api_key } => {
                 cx.write_credentials(OPENAI_API_URL, "Bearer", api_key.as_bytes())
                     .log_err();
             }
             _ => {}
-        })
-        .await;
+        }
     }
-    async fn delete_credentials(&self, cx: &mut AppContext) {
+
+    fn delete_credentials(&self, cx: &mut AppContext) {
         cx.run_on_main(move |cx| cx.delete_credentials(OPENAI_API_URL).log_err())
             .await;
         *self.credential.write() = ProviderCredential::NoCredentials;

crates/audio2/src/audio2.rs 🔗

@@ -1,14 +1,13 @@
 use assets::SoundRegistry;
-use futures::{channel::mpsc, StreamExt};
-use gpui2::{AppContext, AssetSource, BackgroundExecutor};
+use gpui2::{AppContext, AssetSource};
 use rodio::{OutputStream, OutputStreamHandle};
 use util::ResultExt;
 
 mod assets;
 
 pub fn init(source: impl AssetSource, cx: &mut AppContext) {
-    cx.set_global(Audio::new(cx.executor()));
     cx.set_global(SoundRegistry::new(source));
+    cx.set_global(Audio::new());
 }
 
 pub enum Sound {
@@ -34,15 +33,18 @@ impl Sound {
 }
 
 pub struct Audio {
-    tx: mpsc::UnboundedSender<Box<dyn FnOnce(&mut AudioState)>>,
-}
-
-struct AudioState {
     _output_stream: Option<OutputStream>,
     output_handle: Option<OutputStreamHandle>,
 }
 
-impl AudioState {
+impl Audio {
+    pub fn new() -> Self {
+        Self {
+            _output_stream: None,
+            output_handle: None,
+        }
+    }
+
     fn ensure_output_exists(&mut self) -> Option<&OutputStreamHandle> {
         if self.output_handle.is_none() {
             let (_output_stream, output_handle) = OutputStream::try_default().log_err().unzip();
@@ -53,59 +55,27 @@ impl AudioState {
         self.output_handle.as_ref()
     }
 
-    fn take(&mut self) {
-        self._output_stream.take();
-        self.output_handle.take();
-    }
-}
-
-impl Audio {
-    pub fn new(executor: &BackgroundExecutor) -> Self {
-        let (tx, mut rx) = mpsc::unbounded::<Box<dyn FnOnce(&mut AudioState)>>();
-        executor
-            .spawn_on_main(|| async move {
-                let mut audio = AudioState {
-                    _output_stream: None,
-                    output_handle: None,
-                };
-
-                while let Some(f) = rx.next().await {
-                    (f)(&mut audio);
-                }
-            })
-            .detach();
-
-        Self { tx }
-    }
-
     pub fn play_sound(sound: Sound, cx: &mut AppContext) {
         if !cx.has_global::<Self>() {
             return;
         }
 
-        let Some(source) = SoundRegistry::global(cx).get(sound.file()).log_err() else {
-            return;
-        };
-
-        let this = cx.global::<Self>();
-        this.tx
-            .unbounded_send(Box::new(move |state| {
-                if let Some(output_handle) = state.ensure_output_exists() {
-                    output_handle.play_raw(source).log_err();
-                }
-            }))
-            .ok();
+        cx.update_global::<Self, _>(|this, cx| {
+            let output_handle = this.ensure_output_exists()?;
+            let source = SoundRegistry::global(cx).get(sound.file()).log_err()?;
+            output_handle.play_raw(source).log_err()?;
+            Some(())
+        });
     }
 
-    pub fn end_call(cx: &AppContext) {
+    pub fn end_call(cx: &mut AppContext) {
         if !cx.has_global::<Self>() {
             return;
         }
 
-        let this = cx.global::<Self>();
-
-        this.tx
-            .unbounded_send(Box::new(move |state| state.take()))
-            .ok();
+        cx.update_global::<Self, _>(|this, _| {
+            this._output_stream.take();
+            this.output_handle.take();
+        });
     }
 }

crates/client2/src/client2.rs 🔗

@@ -11,7 +11,8 @@ use async_tungstenite::tungstenite::{
     http::{Request, StatusCode},
 };
 use futures::{
-    future::BoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt, TryFutureExt as _, TryStreamExt,
+    future::LocalBoxFuture, AsyncReadExt, FutureExt, SinkExt, StreamExt, TryFutureExt as _,
+    TryStreamExt,
 };
 use gpui2::{
     serde_json, AnyModel, AnyWeakModel, AppContext, AsyncAppContext, Model, SemanticVersion, Task,
@@ -233,14 +234,12 @@ struct ClientState {
     message_handlers: HashMap<
         TypeId,
         Arc<
-            dyn Send
-                + Sync
-                + Fn(
-                    AnyModel,
-                    Box<dyn AnyTypedEnvelope>,
-                    &Arc<Client>,
-                    AsyncAppContext,
-                ) -> BoxFuture<'static, Result<()>>,
+            dyn Fn(
+                AnyModel,
+                Box<dyn AnyTypedEnvelope>,
+                &Arc<Client>,
+                AsyncAppContext,
+            ) -> LocalBoxFuture<'static, Result<()>>,
         >,
     >,
 }
@@ -310,10 +309,7 @@ pub struct PendingEntitySubscription<T: 'static> {
     consumed: bool,
 }
 
-impl<T> PendingEntitySubscription<T>
-where
-    T: 'static + Send,
-{
+impl<T: 'static> PendingEntitySubscription<T> {
     pub fn set_model(mut self, model: &Model<T>, cx: &mut AsyncAppContext) -> Subscription {
         self.consumed = true;
         let mut state = self.client.state.write();
@@ -341,10 +337,7 @@ where
     }
 }
 
-impl<T> Drop for PendingEntitySubscription<T>
-where
-    T: 'static,
-{
+impl<T: 'static> Drop for PendingEntitySubscription<T> {
     fn drop(&mut self) {
         if !self.consumed {
             let mut state = self.client.state.write();
@@ -529,7 +522,7 @@ impl Client {
         remote_id: u64,
     ) -> Result<PendingEntitySubscription<T>>
     where
-        T: 'static + Send,
+        T: 'static,
     {
         let id = (TypeId::of::<T>(), remote_id);
 
@@ -557,9 +550,9 @@ impl Client {
     ) -> Subscription
     where
         M: EnvelopedMessage,
-        E: 'static + Send,
-        H: 'static + Send + Sync + Fn(Model<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
-        F: 'static + Future<Output = Result<()>> + Send,
+        E: 'static,
+        H: 'static + Sync + Fn(Model<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
+        F: 'static + Future<Output = Result<()>>,
     {
         let message_type_id = TypeId::of::<M>();
 
@@ -573,7 +566,7 @@ impl Client {
             Arc::new(move |subscriber, envelope, client, cx| {
                 let subscriber = subscriber.downcast::<E>().unwrap();
                 let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
-                handler(subscriber, *envelope, client.clone(), cx).boxed()
+                handler(subscriber, *envelope, client.clone(), cx).boxed_local()
             }),
         );
         if prev_handler.is_some() {
@@ -599,9 +592,9 @@ impl Client {
     ) -> Subscription
     where
         M: RequestMessage,
-        E: 'static + Send,
-        H: 'static + Send + Sync + Fn(Model<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
-        F: 'static + Future<Output = Result<M::Response>> + Send,
+        E: 'static,
+        H: 'static + Sync + Fn(Model<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
+        F: 'static + Future<Output = Result<M::Response>>,
     {
         self.add_message_handler(model, move |handle, envelope, this, cx| {
             Self::respond_to_request(
@@ -615,9 +608,9 @@ impl Client {
     pub fn add_model_message_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
     where
         M: EntityMessage,
-        E: 'static + Send,
-        H: 'static + Send + Sync + Fn(Model<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
-        F: 'static + Future<Output = Result<()>> + Send,
+        E: 'static,
+        H: 'static + Fn(Model<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
+        F: 'static + Future<Output = Result<()>>,
     {
         self.add_entity_message_handler::<M, E, _, _>(move |subscriber, message, client, cx| {
             handler(subscriber.downcast::<E>().unwrap(), message, client, cx)
@@ -627,9 +620,9 @@ impl Client {
     fn add_entity_message_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
     where
         M: EntityMessage,
-        E: 'static + Send,
-        H: 'static + Send + Sync + Fn(AnyModel, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
-        F: 'static + Future<Output = Result<()>> + Send,
+        E: 'static,
+        H: 'static + Fn(AnyModel, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
+        F: 'static + Future<Output = Result<()>>,
     {
         let model_type_id = TypeId::of::<E>();
         let message_type_id = TypeId::of::<M>();
@@ -655,7 +648,7 @@ impl Client {
             message_type_id,
             Arc::new(move |handle, envelope, client, cx| {
                 let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
-                handler(handle, *envelope, client.clone(), cx).boxed()
+                handler(handle, *envelope, client.clone(), cx).boxed_local()
             }),
         );
         if prev_handler.is_some() {
@@ -666,9 +659,9 @@ impl Client {
     pub fn add_model_request_handler<M, E, H, F>(self: &Arc<Self>, handler: H)
     where
         M: EntityMessage + RequestMessage,
-        E: 'static + Send,
-        H: 'static + Send + Sync + Fn(Model<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
-        F: 'static + Future<Output = Result<M::Response>> + Send,
+        E: 'static,
+        H: 'static + Fn(Model<E>, TypedEnvelope<M>, Arc<Self>, AsyncAppContext) -> F,
+        F: 'static + Future<Output = Result<M::Response>>,
     {
         self.add_model_message_handler(move |entity, envelope, client, cx| {
             Self::respond_to_request::<M, _>(
@@ -705,7 +698,7 @@ impl Client {
         read_credentials_from_keychain(cx).await.is_some()
     }
 
-    #[async_recursion]
+    #[async_recursion(?Send)]
     pub async fn authenticate_and_connect(
         self: &Arc<Self>,
         try_keychain: bool,
@@ -1050,7 +1043,7 @@ impl Client {
                 write!(&mut url, "&impersonate={}", impersonate_login).unwrap();
             }
 
-            cx.run_on_main(move |cx| cx.open_url(&url))?.await;
+            cx.update(|cx| cx.open_url(&url))?;
 
             // Receive the HTTP request from the user's browser. Retrieve the user id and encrypted
             // access token from the query params.
@@ -1101,7 +1094,7 @@ impl Client {
             let access_token = private_key
                 .decrypt_string(&access_token)
                 .context("failed to decrypt access token")?;
-            cx.run_on_main(|cx| cx.activate(true))?.await;
+            cx.update(|cx| cx.activate(true))?;
 
             Ok(Credentials {
                 user_id: user_id.parse()?,
@@ -1293,7 +1286,7 @@ impl Client {
                 sender_id,
                 type_name
             );
-            cx.spawn_on_main(move |_| async move {
+            cx.spawn(move |_| async move {
                     match future.await {
                         Ok(()) => {
                             log::debug!(
@@ -1332,9 +1325,8 @@ async fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option<Credenti
     }
 
     let (user_id, access_token) = cx
-        .run_on_main(|cx| cx.read_credentials(&ZED_SERVER_URL).log_err().flatten())
-        .ok()?
-        .await?;
+        .update(|cx| cx.read_credentials(&ZED_SERVER_URL).log_err().flatten())
+        .ok()??;
 
     Some(Credentials {
         user_id: user_id.parse().ok()?,
@@ -1346,19 +1338,17 @@ async fn write_credentials_to_keychain(
     credentials: Credentials,
     cx: &AsyncAppContext,
 ) -> Result<()> {
-    cx.run_on_main(move |cx| {
+    cx.update(move |cx| {
         cx.write_credentials(
             &ZED_SERVER_URL,
             &credentials.user_id.to_string(),
             credentials.access_token.as_bytes(),
         )
     })?
-    .await
 }
 
 async fn delete_credentials_from_keychain(cx: &AsyncAppContext) -> Result<()> {
-    cx.run_on_main(move |cx| cx.delete_credentials(&ZED_SERVER_URL))?
-        .await
+    cx.update(move |cx| cx.delete_credentials(&ZED_SERVER_URL))?
 }
 
 const WORKTREE_URL_PREFIX: &str = "zed://worktrees/";
@@ -1430,7 +1420,7 @@ mod tests {
 
         // Time out when client tries to connect.
         client.override_authenticate(move |cx| {
-            cx.executor().spawn(async move {
+            cx.background_executor().spawn(async move {
                 Ok(Credentials {
                     user_id,
                     access_token: "token".into(),
@@ -1438,7 +1428,7 @@ mod tests {
             })
         });
         client.override_establish_connection(|_, cx| {
-            cx.executor().spawn(async move {
+            cx.background_executor().spawn(async move {
                 future::pending::<()>().await;
                 unreachable!()
             })
@@ -1472,7 +1462,7 @@ mod tests {
         // Time out when re-establishing the connection.
         server.allow_connections();
         client.override_establish_connection(|_, cx| {
-            cx.executor().spawn(async move {
+            cx.background_executor().spawn(async move {
                 future::pending::<()>().await;
                 unreachable!()
             })
@@ -1504,7 +1494,7 @@ mod tests {
             move |cx| {
                 let auth_count = auth_count.clone();
                 let dropped_auth_count = dropped_auth_count.clone();
-                cx.executor().spawn(async move {
+                cx.background_executor().spawn(async move {
                     *auth_count.lock() += 1;
                     let _drop = util::defer(move || *dropped_auth_count.lock() += 1);
                     future::pending::<()>().await;

crates/client2/src/telemetry.rs 🔗

@@ -1,5 +1,5 @@
 use crate::{TelemetrySettings, ZED_SECRET_CLIENT_TOKEN, ZED_SERVER_URL};
-use gpui2::{serde_json, AppContext, AppMetadata, Executor, Task};
+use gpui2::{serde_json, AppContext, AppMetadata, BackgroundExecutor, Task};
 use lazy_static::lazy_static;
 use parking_lot::Mutex;
 use serde::Serialize;
@@ -14,7 +14,7 @@ use util::{channel::ReleaseChannel, TryFutureExt};
 
 pub struct Telemetry {
     http_client: Arc<dyn HttpClient>,
-    executor: Executor,
+    executor: BackgroundExecutor,
     state: Mutex<TelemetryState>,
 }
 

crates/editor/src/test/editor_lsp_test_context.rs 🔗

@@ -266,9 +266,9 @@ impl<'a> EditorLspTestContext<'a> {
     ) -> futures::channel::mpsc::UnboundedReceiver<()>
     where
         T: 'static + request::Request,
-        T::Params: 'static,
-        F: 'static + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut,
-        Fut: 'static + Future<Output = Result<T::Result>>,
+        T::Params: 'static + Send,
+        F: 'static + Send + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut,
+        Fut: 'static + Send + Future<Output = Result<T::Result>>,
     {
         let url = self.buffer_lsp_url.clone();
         self.lsp.handle_request::<T, _, _>(move |params, cx| {

crates/fuzzy2/src/strings.rs 🔗

@@ -2,7 +2,7 @@ use crate::{
     matcher::{Match, MatchCandidate, Matcher},
     CharBag,
 };
-use gpui2::Executor;
+use gpui2::BackgroundExecutor;
 use std::{
     borrow::Cow,
     cmp::{self, Ordering},
@@ -83,7 +83,7 @@ pub async fn match_strings(
     smart_case: bool,
     max_results: usize,
     cancel_flag: &AtomicBool,
-    executor: Executor,
+    executor: BackgroundExecutor,
 ) -> Vec<StringMatch> {
     if candidates.is_empty() || max_results == 0 {
         return Default::default();

crates/gpui2/src/app.rs 🔗

@@ -107,11 +107,11 @@ impl App {
 }
 
 type ActionBuilder = fn(json: Option<serde_json::Value>) -> anyhow::Result<Box<dyn Action>>;
-type FrameCallback = Box<dyn FnOnce(&mut WindowContext) + Send>;
-type Handler = Box<dyn FnMut(&mut AppContext) -> bool + Send + 'static>;
-type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + Send + 'static>;
-type QuitHandler = Box<dyn FnMut(&mut AppContext) -> BoxFuture<'static, ()> + Send + 'static>;
-type ReleaseListener = Box<dyn FnMut(&mut dyn Any, &mut AppContext) + Send + 'static>;
+type FrameCallback = Box<dyn FnOnce(&mut WindowContext)>;
+type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
+type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;
+type QuitHandler = Box<dyn FnMut(&mut AppContext) -> BoxFuture<'static, ()> + 'static>;
+type ReleaseListener = Box<dyn FnMut(&mut dyn Any, &mut AppContext) + 'static>;
 
 pub struct AppContext {
     this: Weak<RefCell<AppContext>>,
@@ -133,7 +133,7 @@ pub struct AppContext {
     pub(crate) windows: SlotMap<WindowId, Option<Window>>,
     pub(crate) keymap: Arc<Mutex<Keymap>>,
     pub(crate) global_action_listeners:
-        HashMap<TypeId, Vec<Box<dyn Fn(&dyn Action, DispatchPhase, &mut Self) + Send>>>,
+        HashMap<TypeId, Vec<Box<dyn Fn(&dyn Action, DispatchPhase, &mut Self)>>>,
     action_builders: HashMap<SharedString, ActionBuilder>,
     pending_effects: VecDeque<Effect>,
     pub(crate) pending_notifications: HashSet<EntityId>,
@@ -295,7 +295,7 @@ impl AppContext {
     pub fn open_window<V: Render>(
         &mut self,
         options: crate::WindowOptions,
-        build_root_view: impl FnOnce(&mut WindowContext) -> View<V> + Send + 'static,
+        build_root_view: impl FnOnce(&mut WindowContext) -> View<V> + 'static,
     ) -> WindowHandle<V> {
         self.update(|cx| {
             let id = cx.windows.insert(None);
@@ -520,7 +520,7 @@ impl AppContext {
             .retain(&type_id, |observer| observer(self));
     }
 
-    fn apply_defer_effect(&mut self, callback: Box<dyn FnOnce(&mut Self) + Send + 'static>) {
+    fn apply_defer_effect(&mut self, callback: Box<dyn FnOnce(&mut Self) + 'static>) {
         callback(self);
     }
 
@@ -551,7 +551,7 @@ impl AppContext {
 
     /// Schedules the given function to be run at the end of the current effect cycle, allowing entities
     /// that are currently on the stack to be returned to the app.
-    pub fn defer(&mut self, f: impl FnOnce(&mut AppContext) + 'static + Send) {
+    pub fn defer(&mut self, f: impl FnOnce(&mut AppContext) + 'static) {
         self.push_effect(Effect::Defer {
             callback: Box::new(f),
         });
@@ -639,7 +639,7 @@ impl AppContext {
     /// Register a callback to be invoked when a global of the given type is updated.
     pub fn observe_global<G: 'static>(
         &mut self,
-        mut f: impl FnMut(&mut Self) + Send + 'static,
+        mut f: impl FnMut(&mut Self) + 'static,
     ) -> Subscription {
         self.global_observers.insert(
             TypeId::of::<G>(),
@@ -686,7 +686,7 @@ impl AppContext {
     }
 
     /// Register a global listener for actions invoked via the keyboard.
-    pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Self) + Send + 'static) {
+    pub fn on_action<A: Action>(&mut self, listener: impl Fn(&A, &mut Self) + 'static) {
         self.global_action_listeners
             .entry(TypeId::of::<A>())
             .or_default()
@@ -778,7 +778,7 @@ pub(crate) enum Effect {
         global_type: TypeId,
     },
     Defer {
-        callback: Box<dyn FnOnce(&mut AppContext) + Send + 'static>,
+        callback: Box<dyn FnOnce(&mut AppContext) + 'static>,
     },
 }
 

crates/gpui2/src/element.rs 🔗

@@ -218,8 +218,8 @@ impl<V> Component<V> for AnyElement<V> {
 impl<V, E, F> Element<V> for Option<F>
 where
     V: 'static,
-    E: 'static + Component<V> + Send,
-    F: FnOnce(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static,
+    E: 'static + Component<V>,
+    F: FnOnce(&mut V, &mut ViewContext<'_, '_, V>) -> E + 'static,
 {
     type ElementState = AnyElement<V>;
 
@@ -262,8 +262,8 @@ where
 impl<V, E, F> Component<V> for Option<F>
 where
     V: 'static,
-    E: 'static + Component<V> + Send,
-    F: FnOnce(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static,
+    E: 'static + Component<V>,
+    F: FnOnce(&mut V, &mut ViewContext<'_, '_, V>) -> E + 'static,
 {
     fn render(self) -> AnyElement<V> {
         AnyElement::new(self)
@@ -273,8 +273,8 @@ where
 impl<V, E, F> Component<V> for F
 where
     V: 'static,
-    E: 'static + Component<V> + Send,
-    F: FnOnce(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static,
+    E: 'static + Component<V>,
+    F: FnOnce(&mut V, &mut ViewContext<'_, '_, V>) -> E + 'static,
 {
     fn render(self) -> AnyElement<V> {
         AnyElement::new(Some(self))

crates/gpui2_macros/src/test.rs 🔗

@@ -89,8 +89,8 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
                             inner_fn_args.extend(quote!(rand::SeedableRng::seed_from_u64(_seed),));
                             continue;
                         }
-                        Some("Executor") => {
-                            inner_fn_args.extend(quote!(gpui2::Executor::new(
+                        Some("BackgroundExecutor") => {
+                            inner_fn_args.extend(quote!(gpui2::BackgroundExecutor::new(
                                 std::sync::Arc::new(dispatcher.clone())
                             ),));
                             continue;
@@ -134,7 +134,7 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
                     #num_iterations as u64,
                     #max_retries,
                     &mut |dispatcher, _seed| {
-                        let executor = gpui2::Executor::new(std::sync::Arc::new(dispatcher.clone()));
+                        let executor = gpui2::BackgroundExecutor::new(std::sync::Arc::new(dispatcher.clone()));
                         #cx_vars
                         executor.block(#inner_fn_name(#inner_fn_args));
                         #cx_teardowns
@@ -170,7 +170,7 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream {
                                     let mut #cx_varname = gpui2::TestAppContext::new(
                                        dispatcher.clone()
                                     );
-                                    let mut #cx_varname_lock = #cx_varname.app.lock();
+                                    let mut #cx_varname_lock = #cx_varname.app.borrow_mut();
                                 ));
                                 inner_fn_args.extend(quote!(&mut #cx_varname_lock,));
                                 cx_teardowns.extend(quote!(

crates/install_cli2/src/install_cli2.rs 🔗

@@ -7,9 +7,7 @@ use util::ResultExt;
 // actions!(cli, [Install]);
 
 pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> {
-    let cli_path = cx
-        .run_on_main(|cx| cx.path_for_auxiliary_executable("cli"))?
-        .await?;
+    let cli_path = cx.update(|cx| cx.path_for_auxiliary_executable("cli"))??;
     let link_path = Path::new("/usr/local/bin/zed");
     let bin_dir_path = link_path.parent().unwrap();
 

crates/language2/src/outline.rs 🔗

@@ -1,5 +1,5 @@
 use fuzzy2::{StringMatch, StringMatchCandidate};
-use gpui2::{Executor, HighlightStyle};
+use gpui2::{BackgroundExecutor, HighlightStyle};
 use std::ops::Range;
 
 #[derive(Debug)]
@@ -57,7 +57,7 @@ impl<T> Outline<T> {
         }
     }
 
-    pub async fn search(&self, query: &str, executor: Executor) -> Vec<StringMatch> {
+    pub async fn search(&self, query: &str, executor: BackgroundExecutor) -> Vec<StringMatch> {
         let query = query.trim_start();
         let is_path_query = query.contains(' ');
         let smart_case = query.chars().any(|c| c.is_uppercase());

crates/lsp2/src/lsp2.rs 🔗

@@ -1047,8 +1047,9 @@ impl FakeLanguageServer {
             .on_request::<T, _, _>(move |params, cx| {
                 let result = handler(params, cx.clone());
                 let responded_tx = responded_tx.clone();
+                let executor = cx.background_executor().clone();
                 async move {
-                    cx.background_executor().simulate_random_delay().await;
+                    executor.simulate_random_delay().await;
                     let result = result.await;
                     responded_tx.unbounded_send(()).ok();
                     result

crates/terminal2/src/terminal2.rs 🔗

@@ -51,8 +51,8 @@ use thiserror::Error;
 
 use gpui2::{
     px, AnyWindowHandle, AppContext, Bounds, ClipboardItem, EventEmitter, Hsla, Keystroke,
-    MainThread, ModelContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
-    Pixels, Point, ScrollWheelEvent, Size, Task, TouchPhase,
+    ModelContext, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
+    Point, ScrollWheelEvent, Size, Task, TouchPhase,
 };
 
 use crate::mappings::{colors::to_alac_rgb, keys::to_esc_str};
@@ -403,7 +403,7 @@ impl TerminalBuilder {
 
     pub fn subscribe(mut self, cx: &mut ModelContext<Terminal>) -> Terminal {
         //Event loop
-        cx.spawn_on_main(|this, mut cx| async move {
+        cx.spawn(|this, mut cx| async move {
             use futures::StreamExt;
 
             while let Some(event) = self.events_rx.next().await {
@@ -414,7 +414,10 @@ impl TerminalBuilder {
 
                 'outer: loop {
                     let mut events = vec![];
-                    let mut timer = cx.executor().timer(Duration::from_millis(4)).fuse();
+                    let mut timer = cx
+                        .background_executor()
+                        .timer(Duration::from_millis(4))
+                        .fuse();
                     let mut wakeup = false;
                     loop {
                         futures::select_biased! {
@@ -551,7 +554,7 @@ pub struct Terminal {
 }
 
 impl Terminal {
-    fn process_event(&mut self, event: &AlacTermEvent, cx: &mut MainThread<ModelContext<Self>>) {
+    fn process_event(&mut self, event: &AlacTermEvent, cx: &mut ModelContext<Self>) {
         match event {
             AlacTermEvent::Title(title) => {
                 self.breadcrumb_text = title.to_string();
@@ -708,8 +711,7 @@ impl Terminal {
 
             InternalEvent::Copy => {
                 if let Some(txt) = term.selection_to_string() {
-                    cx.run_on_main(|cx| cx.write_to_clipboard(ClipboardItem::new(txt)))
-                        .detach();
+                    cx.write_to_clipboard(ClipboardItem::new(txt))
                 }
             }
             InternalEvent::ScrollToAlacPoint(point) => {
@@ -1189,7 +1191,7 @@ impl Terminal {
         &mut self,
         e: &MouseUpEvent,
         origin: Point<Pixels>,
-        cx: &mut MainThread<ModelContext<Self>>,
+        cx: &mut ModelContext<Self>,
     ) {
         let setting = TerminalSettings::get_global(cx);
 

crates/vim/src/test/vim_test_context.rs 🔗

@@ -133,9 +133,9 @@ impl<'a> VimTestContext<'a> {
     ) -> futures::channel::mpsc::UnboundedReceiver<()>
     where
         T: 'static + request::Request,
-        T::Params: 'static,
-        F: 'static + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut,
-        Fut: 'static + Future<Output = Result<T::Result>>,
+        T::Params: 'static + Send,
+        F: 'static + Send + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut,
+        Fut: 'static + Send + Future<Output = Result<T::Result>>,
     {
         self.cx.handle_request::<T, F, Fut>(handler)
     }