Suspend macOS threads during crashes (#36520)

Julia Ryan created

This should improve our detection of which thread crashed since they
wont be able to resume while the minidump is being generated.

Release Notes:

- N/A

Change summary

Cargo.lock                    | 20 +++++++++++++++-----
Cargo.toml                    |  1 +
crates/crashes/Cargo.toml     |  3 +++
crates/crashes/src/crashes.rs | 20 ++++++++++++++++++++
4 files changed, 39 insertions(+), 5 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3872,7 +3872,7 @@ dependencies = [
  "jni",
  "js-sys",
  "libc",
- "mach2",
+ "mach2 0.4.2",
  "ndk",
  "ndk-context",
  "num-derive",
@@ -4022,7 +4022,7 @@ checksum = "031ed29858d90cfdf27fe49fae28028a1f20466db97962fa2f4ea34809aeebf3"
 dependencies = [
  "cfg-if",
  "libc",
- "mach2",
+ "mach2 0.4.2",
 ]
 
 [[package]]
@@ -4034,7 +4034,7 @@ dependencies = [
  "cfg-if",
  "crash-context",
  "libc",
- "mach2",
+ "mach2 0.4.2",
  "parking_lot",
 ]
 
@@ -4044,6 +4044,7 @@ version = "0.1.0"
 dependencies = [
  "crash-handler",
  "log",
+ "mach2 0.5.0",
  "minidumper",
  "paths",
  "release_channel",
@@ -9866,6 +9867,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "mach2"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a1b95cd5421ec55b445b5ae102f5ea0e768de1f82bd3001e11f426c269c3aea"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "malloc_buf"
 version = "0.0.6"
@@ -10202,7 +10212,7 @@ dependencies = [
  "goblin",
  "libc",
  "log",
- "mach2",
+ "mach2 0.4.2",
  "memmap2",
  "memoffset",
  "minidump-common",
@@ -18292,7 +18302,7 @@ dependencies = [
  "indexmap",
  "libc",
  "log",
- "mach2",
+ "mach2 0.4.2",
  "memfd",
  "object",
  "once_cell",

Cargo.toml 🔗

@@ -515,6 +515,7 @@ libsqlite3-sys = { version = "0.30.1", features = ["bundled"] }
 linkify = "0.10.0"
 log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] }
 lsp-types = { git = "https://github.com/zed-industries/lsp-types", rev = "39f629bdd03d59abd786ed9fc27e8bca02c0c0ec" }
+mach2 = "0.5"
 markup5ever_rcdom = "0.3.0"
 metal = "0.29"
 minidumper = "0.8"

crates/crashes/Cargo.toml 🔗

@@ -16,6 +16,9 @@ serde.workspace = true
 serde_json.workspace = true
 workspace-hack.workspace = true
 
+[target.'cfg(target_os = "macos")'.dependencies]
+mach2.workspace = true
+
 [lints]
 workspace = true
 

crates/crashes/src/crashes.rs 🔗

@@ -74,6 +74,9 @@ pub async fn init(crash_init: InitCrashHandler) {
                 .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
                 .is_ok()
             {
+                #[cfg(target_os = "macos")]
+                suspend_all_other_threads();
+
                 client.ping().unwrap();
                 client.request_dump(crash_context).is_ok()
             } else {
@@ -98,6 +101,23 @@ pub async fn init(crash_init: InitCrashHandler) {
     }
 }
 
+#[cfg(target_os = "macos")]
+unsafe fn suspend_all_other_threads() {
+    let task = unsafe { mach2::traps::current_task() };
+    let mut threads: mach2::mach_types::thread_act_array_t = std::ptr::null_mut();
+    let mut count = 0;
+    unsafe {
+        mach2::task::task_threads(task, &raw mut threads, &raw mut count);
+    }
+    let current = unsafe { mach2::mach_init::mach_thread_self() };
+    for i in 0..count {
+        let t = unsafe { *threads.add(i as usize) };
+        if t != current {
+            unsafe { mach2::thread_act::thread_suspend(t) };
+        }
+    }
+}
+
 pub struct CrashServer {
     initialization_params: OnceLock<InitCrashHandler>,
     panic_info: OnceLock<CrashPanic>,