Skip to content

Commit d28757e

Browse files
committed
fix: hold PTY lock for entire Terminal lifetime on musl
The previous SPAWN_LOCK only serialized the openpty+fork/exec call, but concurrent PTY I/O operations after spawn also trigger SIGSEGV/SIGBUS in musl internals. Store the MutexGuard in the Terminal struct so the lock is held for the Terminal's entire lifetime, ensuring only one PTY is active at a time on musl. https://claude.ai/code/session_011H8UR3gS6hoyQAf2x7Dfw8
1 parent 8f87238 commit d28757e

File tree

1 file changed

+14
-5
lines changed

1 file changed

+14
-5
lines changed

crates/pty_terminal/src/terminal.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ pub struct Terminal {
5151
pub pty_reader: PtyReader,
5252
pub pty_writer: PtyWriter,
5353
pub child_handle: ChildHandle,
54+
55+
/// On musl libc, concurrent PTY operations (openpty, fork/exec, FD I/O)
56+
/// trigger SIGSEGV/SIGBUS in musl internals. This guard serializes the
57+
/// entire PTY lifecycle so only one Terminal is active at a time.
58+
#[cfg(target_env = "musl")]
59+
_pty_guard: std::sync::MutexGuard<'static, ()>,
5460
}
5561

5662
struct Vt100Callbacks {
@@ -256,14 +262,15 @@ impl Terminal {
256262
///
257263
/// Panics if the writer lock is poisoned when the background thread closes it.
258264
pub fn spawn(size: ScreenSize, cmd: CommandBuilder) -> anyhow::Result<Self> {
259-
// On musl libc (Alpine Linux), concurrent openpty + fork/exec operations
260-
// trigger SIGSEGV/SIGBUS in musl internals (observed in sysconf, fcntl).
261-
// Serialize PTY creation and child spawning to avoid the race.
265+
// On musl libc (Alpine Linux), concurrent PTY operations (openpty,
266+
// fork/exec, FD I/O) trigger SIGSEGV/SIGBUS in musl internals.
267+
// Hold the lock for the Terminal's entire lifetime so only one PTY
268+
// is active at a time.
262269
// See: https://github.com/voidzero-dev/vite-task/pull/278
263270
#[cfg(target_env = "musl")]
264-
static SPAWN_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
271+
static PTY_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
265272
#[cfg(target_env = "musl")]
266-
let _spawn_guard = SPAWN_LOCK.lock().unwrap_or_else(|e| e.into_inner());
273+
let pty_guard = PTY_LOCK.lock().unwrap_or_else(|e| e.into_inner());
267274

268275
let pty_pair = portable_pty::native_pty_system().openpty(portable_pty::PtySize {
269276
rows: size.rows,
@@ -315,6 +322,8 @@ impl Terminal {
315322
pty_reader: PtyReader { reader, parser: Arc::clone(&parser) },
316323
pty_writer: PtyWriter { writer, parser, master },
317324
child_handle: ChildHandle { child_killer, exit_status },
325+
#[cfg(target_env = "musl")]
326+
_pty_guard: pty_guard,
318327
})
319328
}
320329
}

0 commit comments

Comments
 (0)