Avoid suspending panicking thread while crashing (#36645)

On the latest build @maxbrunsfeld got a panic that hung zed. It appeared
that the hang occured after the minidump had been successfully written,
so our theory on what happened is that the `suspend_all_other_threads`
call in the crash handler suspended the panicking thread (due to the
signal from simulate_exception being received on a different thread),
and then when the crash handler returned everything was suspended so the
panic hook never made it to the `process::abort`.

This change makes the crash handler avoid _both_ the current and the
panicking thread which should avoid that scenario.

Release Notes:

- N/A
This commit is contained in:
Julia Ryan 2025-08-21 05:33:45 -05:00
parent c5d96e1ef8
commit ca897fcd2f
No known key found for this signature in database

View file

@ -4,6 +4,8 @@ use minidumper::{Client, LoopAction, MinidumpBinary};
use release_channel::{RELEASE_CHANNEL, ReleaseChannel}; use release_channel::{RELEASE_CHANNEL, ReleaseChannel};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(target_os = "macos")]
use std::sync::atomic::AtomicU32;
use std::{ use std::{
env, env,
fs::{self, File}, fs::{self, File},
@ -26,6 +28,9 @@ pub static REQUESTED_MINIDUMP: AtomicBool = AtomicBool::new(false);
const CRASH_HANDLER_PING_TIMEOUT: Duration = Duration::from_secs(60); const CRASH_HANDLER_PING_TIMEOUT: Duration = Duration::from_secs(60);
const CRASH_HANDLER_CONNECT_TIMEOUT: Duration = Duration::from_secs(10); const CRASH_HANDLER_CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
#[cfg(target_os = "macos")]
static PANIC_THREAD_ID: AtomicU32 = AtomicU32::new(0);
pub async fn init(crash_init: InitCrashHandler) { pub async fn init(crash_init: InitCrashHandler) {
if *RELEASE_CHANNEL == ReleaseChannel::Dev && env::var("ZED_GENERATE_MINIDUMPS").is_err() { if *RELEASE_CHANNEL == ReleaseChannel::Dev && env::var("ZED_GENERATE_MINIDUMPS").is_err() {
return; return;
@ -110,9 +115,10 @@ unsafe fn suspend_all_other_threads() {
mach2::task::task_threads(task, &raw mut threads, &raw mut count); mach2::task::task_threads(task, &raw mut threads, &raw mut count);
} }
let current = unsafe { mach2::mach_init::mach_thread_self() }; let current = unsafe { mach2::mach_init::mach_thread_self() };
let panic_thread = PANIC_THREAD_ID.load(Ordering::SeqCst);
for i in 0..count { for i in 0..count {
let t = unsafe { *threads.add(i as usize) }; let t = unsafe { *threads.add(i as usize) };
if t != current { if t != current && t != panic_thread {
unsafe { mach2::thread_act::thread_suspend(t) }; unsafe { mach2::thread_act::thread_suspend(t) };
} }
} }
@ -238,6 +244,13 @@ pub fn handle_panic(message: String, span: Option<&Location>) {
) )
.ok(); .ok();
log::error!("triggering a crash to generate a minidump..."); log::error!("triggering a crash to generate a minidump...");
#[cfg(target_os = "macos")]
PANIC_THREAD_ID.store(
unsafe { mach2::mach_init::mach_thread_self() },
Ordering::SeqCst,
);
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
CrashHandler.simulate_signal(crash_handler::Signal::Trap as u32); CrashHandler.simulate_signal(crash_handler::Signal::Trap as u32);
#[cfg(not(target_os = "linux"))] #[cfg(not(target_os = "linux"))]