aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2026-03-16 22:06:41 +0100
committerGitHub <noreply@github.com>2026-03-16 22:06:41 +0100
commita0deec573bb80fc8320d792ae62abc6e2831daa0 (patch)
treedfc03517045a847f3d754a65031d4320983e9a88
parentfeat(themes): add Flexoki Dark theme (#4555) (diff)
downloadsl0ppij-a0deec573bb80fc8320d792ae62abc6e2831daa0.tar.gz
sl0ppij-a0deec573bb80fc8320d792ae62abc6e2831daa0.tar.xz
sl0ppij-a0deec573bb80fc8320d792ae62abc6e2831daa0.zip
fix: do not reuse terminal pane ids, clean up tokio tasks (#4859)
* don't reuse terminal pane_ids, perform some cleanups * rustfmt * add pr
-rw-r--r--CHANGELOG.md1
-rw-r--r--zellij-server/src/os_input_output_unix.rs20
-rw-r--r--zellij-server/src/pty.rs4
-rw-r--r--zellij-server/src/screen.rs10
4 files changed, 24 insertions, 11 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 779a92e9..c59edb1a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -83,6 +83,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
* feat: add --pane-id and --tab-id to all relevant CLI actions (https://github.com/zellij-org/zellij/pull/4846)
* feat: add --ansi flag to all relevant CLI/plugin-api screendump commands (https://github.com/zellij-org/zellij/pull/4848)
* fix: sixel aspect-ratio and padding issues (https://github.com/zellij-org/zellij/pull/4858)
+* fix: don't recycle pane ids for terminals (https://github.com/zellij-org/zellij/pull/4859)
## [0.43.1] - 2025-08-08
* fix: pane rename backspace regression (https://github.com/zellij-org/zellij/pull/4346)
diff --git a/zellij-server/src/os_input_output_unix.rs b/zellij-server/src/os_input_output_unix.rs
index 052636b8..8c8ae6e0 100644
--- a/zellij-server/src/os_input_output_unix.rs
+++ b/zellij-server/src/os_input_output_unix.rs
@@ -26,7 +26,10 @@ use std::{
process::CommandExt,
},
process::{Child, Command},
- sync::{Arc, Mutex},
+ sync::{
+ atomic::{AtomicU32, Ordering},
+ Arc, Mutex,
+ },
thread,
time::Duration,
};
@@ -276,6 +279,7 @@ fn handle_terminal(
pub(crate) struct UnixPtyBackend {
orig_termios: Arc<Mutex<Option<termios::Termios>>>,
terminal_id_to_raw_fd: Arc<Mutex<BTreeMap<u32, Option<RawFd>>>>,
+ next_terminal_id_counter: Arc<AtomicU32>,
}
/// Try to write as many bytes from `buf` as possible to `fd` without blocking.
@@ -307,6 +311,7 @@ impl UnixPtyBackend {
Ok(Self {
orig_termios: Arc::new(Mutex::new(current_termios)),
terminal_id_to_raw_fd: Arc::new(Mutex::new(BTreeMap::new())),
+ next_terminal_id_counter: Arc::new(AtomicU32::new(0)),
})
}
@@ -439,15 +444,10 @@ impl UnixPtyBackend {
}
pub fn next_terminal_id(&self) -> Option<u32> {
- self.terminal_id_to_raw_fd
- .lock()
- .unwrap()
- .keys()
- .copied()
- .collect::<BTreeSet<u32>>()
- .last()
- .map(|l| l + 1)
- .or(Some(0))
+ Some(
+ self.next_terminal_id_counter
+ .fetch_add(1, Ordering::Relaxed),
+ )
}
}
diff --git a/zellij-server/src/pty.rs b/zellij-server/src/pty.rs
index c277dd64..bb9c800c 100644
--- a/zellij-server/src/pty.rs
+++ b/zellij-server/src/pty.rs
@@ -1761,7 +1761,9 @@ impl Pty {
let err_context = || format!("failed to close for pane {id:?}");
match id {
PaneId::Terminal(id) => {
- self.task_handles.remove(&id);
+ if let Some(handle) = self.task_handles.remove(&id) {
+ handle.abort();
+ }
if let Some(child_pid) = self.id_to_child_pid.remove(&id) {
let err_context = || format!("failed to kill child processes for pane {id}");
self.bus
diff --git a/zellij-server/src/screen.rs b/zellij-server/src/screen.rs
index aa6c64da..39fde948 100644
--- a/zellij-server/src/screen.rs
+++ b/zellij-server/src/screen.rs
@@ -5924,6 +5924,16 @@ pub(crate) fn screen_thread_main(
},
}
+ // Clean up PTY-side resources (async reader task, child PID mapping,
+ // terminal_id_to_raw_fd entry). This is needed because the natural
+ // child exit path (quit_cb) only sends ScreenInstruction::ClosePane
+ // and never sends PtyInstruction::ClosePane. The handler in Pty is
+ // idempotent, so this is safe even if ClosePane was already sent.
+ let _ = screen
+ .bus
+ .senders
+ .send_to_pty(PtyInstruction::ClosePane(id, None));
+
screen.log_and_report_session_state()?;
screen.retain_only_existing_panes_in_pane_groups();
},