diff --git a/Cargo.lock b/Cargo.lock index accd0a0939..0e01793175 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2309,6 +2309,7 @@ dependencies = [ "etagere", "font-kit", "foreign-types", + "futures", "gpui_macros", "image 0.23.14", "lazy_static", diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 5378e05b4c..dc1edc6547 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -15,6 +15,7 @@ backtrace = "0.3" ctor = "0.1" env_logger = { version = "0.8", optional = true } etagere = "0.2" +futures = "0.3" image = "0.23" lazy_static = "1.4.0" log = "0.4" diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 0cf1069738..ed2759aa0c 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -265,6 +265,18 @@ impl App { self } + pub fn on_quit(self, mut callback: F) -> Self + where + F: 'static + FnMut(&mut MutableAppContext), + { + let cx = self.0.clone(); + self.0 + .borrow_mut() + .foreground_platform + .on_quit(Box::new(move || callback(&mut *cx.borrow_mut()))); + self + } + pub fn on_event(self, mut callback: F) -> Self where F: 'static + FnMut(Event, &mut MutableAppContext) -> bool, diff --git a/crates/gpui/src/executor.rs b/crates/gpui/src/executor.rs index 32ee8fc87f..5a2f244aa1 100644 --- a/crates/gpui/src/executor.rs +++ b/crates/gpui/src/executor.rs @@ -38,9 +38,13 @@ pub enum Foreground { } pub enum Background { - Deterministic(Arc), + Deterministic { + executor: Arc, + critical_tasks: Mutex>>, + }, Production { executor: Arc>, + critical_tasks: Mutex>>, _stop: channel::Sender<()>, }, } @@ -500,6 +504,7 @@ impl Background { Self::Production { executor, + critical_tasks: Default::default(), _stop: stop.0, } } @@ -516,11 +521,36 @@ impl Background { let future = any_future(future); let any_task = match self { Self::Production { executor, .. } => executor.spawn(future), - Self::Deterministic(executor) => executor.spawn(future), + Self::Deterministic { executor, .. } => executor.spawn(future), }; Task::send(any_task) } + pub fn spawn_critical(&self, future: F) + where + T: 'static + Send, + F: Send + Future + 'static, + { + let task = self.spawn(async move { + future.await; + }); + match self { + Self::Production { critical_tasks, .. } + | Self::Deterministic { critical_tasks, .. } => critical_tasks.lock().push(task), + } + } + + pub fn block_on_critical_tasks(&self, timeout: Duration) -> bool { + match self { + Background::Production { critical_tasks, .. } + | Self::Deterministic { critical_tasks, .. } => { + let tasks = mem::take(&mut *critical_tasks.lock()); + self.block_with_timeout(timeout, futures::future::join_all(tasks)) + .is_ok() + } + } + } + pub fn block_with_timeout( &self, timeout: Duration, @@ -534,7 +564,7 @@ impl Background { if !timeout.is_zero() { let output = match self { Self::Production { .. } => smol::block_on(util::timeout(timeout, &mut future)).ok(), - Self::Deterministic(executor) => executor.block_on(&mut future), + Self::Deterministic { executor, .. } => executor.block_on(&mut future), }; if let Some(output) = output { return Ok(*output.downcast().unwrap()); @@ -587,7 +617,10 @@ pub fn deterministic(seed: u64) -> (Rc, Arc) { let executor = Arc::new(Deterministic::new(seed)); ( Rc::new(Foreground::Deterministic(executor.clone())), - Arc::new(Background::Deterministic(executor)), + Arc::new(Background::Deterministic { + executor, + critical_tasks: Default::default(), + }), ) } diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 6f776524c2..c0229102a0 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -60,6 +60,7 @@ pub trait Platform: Send + Sync { pub(crate) trait ForegroundPlatform { fn on_become_active(&self, callback: Box); fn on_resign_active(&self, callback: Box); + fn on_quit(&self, callback: Box); fn on_event(&self, callback: Box bool>); fn on_open_files(&self, callback: Box)>); fn run(&self, on_finish_launching: Box ()>); diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 8a4dc8cdf9..9aec0b5c04 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -81,6 +81,10 @@ unsafe fn build_classes() { sel!(applicationDidResignActive:), did_resign_active as extern "C" fn(&mut Object, Sel, id), ); + decl.add_method( + sel!(applicationWillTerminate:), + will_terminate as extern "C" fn(&mut Object, Sel, id), + ); decl.add_method( sel!(handleGPUIMenuItem:), handle_menu_item as extern "C" fn(&mut Object, Sel, id), @@ -100,6 +104,7 @@ pub struct MacForegroundPlatform(RefCell); pub struct MacForegroundPlatformState { become_active: Option>, resign_active: Option>, + quit: Option>, event: Option bool>>, menu_command: Option>, open_files: Option)>>, @@ -196,6 +201,10 @@ impl platform::ForegroundPlatform for MacForegroundPlatform { self.0.borrow_mut().resign_active = Some(callback); } + fn on_quit(&self, callback: Box) { + self.0.borrow_mut().quit = Some(callback); + } + fn on_event(&self, callback: Box bool>) { self.0.borrow_mut().event = Some(callback); } @@ -664,6 +673,13 @@ extern "C" fn did_resign_active(this: &mut Object, _: Sel, _: id) { } } +extern "C" fn will_terminate(this: &mut Object, _: Sel, _: id) { + let platform = unsafe { get_foreground_platform(this) }; + if let Some(callback) = platform.0.borrow_mut().quit.as_mut() { + callback(); + } +} + extern "C" fn open_files(this: &mut Object, _: Sel, _: id, paths: id) { let paths = unsafe { (0..paths.count()) diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index c866a5d23f..eda430bc51 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -58,6 +58,8 @@ impl super::ForegroundPlatform for ForegroundPlatform { fn on_resign_active(&self, _: Box) {} + fn on_quit(&self, _: Box) {} + fn on_event(&self, _: Box bool>) {} fn on_open_files(&self, _: Box)>) {}