From 249a6da54a10228d2f77119486d4042631ab10c4 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Wed, 24 Jan 2024 19:40:01 -0700 Subject: [PATCH] Fix circular locking in prompts Sometimes Cocoa calls app delegate methods (notably the display link) while we're calling Cocoa methods. This causes a deadlock unless we are careful to run cocao methods while we're not holding our internal locks --- crates/gpui/src/platform/mac/platform.rs | 114 ++++++++++++----------- 1 file changed, 62 insertions(+), 52 deletions(-) diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index 58a759865e..7c6f3df266 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -534,67 +534,77 @@ impl Platform for MacPlatform { &self, options: PathPromptOptions, ) -> oneshot::Receiver>> { - unsafe { - let panel = NSOpenPanel::openPanel(nil); - panel.setCanChooseDirectories_(options.directories.to_objc()); - panel.setCanChooseFiles_(options.files.to_objc()); - panel.setAllowsMultipleSelection_(options.multiple.to_objc()); - panel.setResolvesAliases_(false.to_objc()); - let (done_tx, done_rx) = oneshot::channel(); - let done_tx = Cell::new(Some(done_tx)); - let block = ConcreteBlock::new(move |response: NSModalResponse| { - let result = if response == NSModalResponse::NSModalResponseOk { - let mut result = Vec::new(); - let urls = panel.URLs(); - for i in 0..urls.count() { - let url = urls.objectAtIndex(i); - if url.isFileURL() == YES { - if let Ok(path) = ns_url_to_path(url) { - result.push(path) + let (done_tx, done_rx) = oneshot::channel(); + self.foreground_executor() + .spawn(async move { + unsafe { + let panel = NSOpenPanel::openPanel(nil); + panel.setCanChooseDirectories_(options.directories.to_objc()); + panel.setCanChooseFiles_(options.files.to_objc()); + panel.setAllowsMultipleSelection_(options.multiple.to_objc()); + panel.setResolvesAliases_(false.to_objc()); + let done_tx = Cell::new(Some(done_tx)); + let block = ConcreteBlock::new(move |response: NSModalResponse| { + let result = if response == NSModalResponse::NSModalResponseOk { + let mut result = Vec::new(); + let urls = panel.URLs(); + for i in 0..urls.count() { + let url = urls.objectAtIndex(i); + if url.isFileURL() == YES { + if let Ok(path) = ns_url_to_path(url) { + result.push(path) + } + } } - } - } - Some(result) - } else { - None - }; + Some(result) + } else { + None + }; - if let Some(done_tx) = done_tx.take() { - let _ = done_tx.send(result); + if let Some(done_tx) = done_tx.take() { + let _ = done_tx.send(result); + } + }); + let block = block.copy(); + let _: () = msg_send![panel, beginWithCompletionHandler: block]; } - }); - let block = block.copy(); - let _: () = msg_send![panel, beginWithCompletionHandler: block]; - done_rx - } + }) + .detach(); + done_rx } fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver> { - unsafe { - let panel = NSSavePanel::savePanel(nil); - let path = ns_string(directory.to_string_lossy().as_ref()); - let url = NSURL::fileURLWithPath_isDirectory_(nil, path, true.to_objc()); - panel.setDirectoryURL(url); + let directory = directory.to_owned(); + let (done_tx, done_rx) = oneshot::channel(); + self.foreground_executor() + .spawn(async move { + unsafe { + let panel = NSSavePanel::savePanel(nil); + let path = ns_string(directory.to_string_lossy().as_ref()); + let url = NSURL::fileURLWithPath_isDirectory_(nil, path, true.to_objc()); + panel.setDirectoryURL(url); - let (done_tx, done_rx) = oneshot::channel(); - let done_tx = Cell::new(Some(done_tx)); - let block = ConcreteBlock::new(move |response: NSModalResponse| { - let mut result = None; - if response == NSModalResponse::NSModalResponseOk { - let url = panel.URL(); - if url.isFileURL() == YES { - result = ns_url_to_path(panel.URL()).ok() - } - } + let done_tx = Cell::new(Some(done_tx)); + let block = ConcreteBlock::new(move |response: NSModalResponse| { + let mut result = None; + if response == NSModalResponse::NSModalResponseOk { + let url = panel.URL(); + if url.isFileURL() == YES { + result = ns_url_to_path(panel.URL()).ok() + } + } - if let Some(done_tx) = done_tx.take() { - let _ = done_tx.send(result); + if let Some(done_tx) = done_tx.take() { + let _ = done_tx.send(result); + } + }); + let block = block.copy(); + let _: () = msg_send![panel, beginWithCompletionHandler: block]; } - }); - let block = block.copy(); - let _: () = msg_send![panel, beginWithCompletionHandler: block]; - done_rx - } + }) + .detach(); + + done_rx } fn reveal_path(&self, path: &Path) {