yoi/.yoi/tickets/00001KTJF465G/item.md

3.2 KiB

title state created_at updated_at queued_by queued_at
Abort PodClient reader task on drop closed 2026-06-08T01:56:30Z 2026-06-08T02:54:31Z workspace-panel 2026-06-08T02:40:58Z

Background

When yoi panel is left open, file descriptor count can increase steadily even while the user does nothing. Observed behavior: FD count rises by roughly 10 per poll cycle, and eventually panel diagnostics show unrelated-looking errors such as:

Panel local role registry unavailable: local role session registry I/O error: Too many open files (os error 24)
Ticket config is unusable: failed to read Ticket config .../.yoi/ticket.config.toml: Too many open files (os error 24)

The likely root cause is client::PodClient::connect(): it splits the Unix socket and spawns a background reader task, but PodClient does not keep the JoinHandle or abort the task on drop.

Current shape:

pub struct PodClient {
    writer: JsonLineWriter<tokio::io::WriteHalf<UnixStream>>,
    event_rx: mpsc::Receiver<Event>,
}

pub async fn connect(path: &Path) -> Result<Self, io::Error> {
    let stream = UnixStream::connect(path).await?;
    let (reader, writer) = tokio::io::split(stream);
    ...
    tokio::spawn(async move {
        let mut reader = JsonLineReader::new(reader);
        while let Ok(Some(event)) = reader.next::<Event>().await {
            if event_tx.send(event).await.is_err() {
                break;
            }
        }
    });
    Ok(Self { writer, event_rx })
}

Panel live Pod probing repeatedly creates short-lived PodClients. Dropping the PodClient drops the writer and receiver, but the spawned reader task can remain blocked on socket read while holding the read half/FD. If there are ~10 live probe targets, each panel poll can leak ~10 FDs.

Goal

Make PodClient own and clean up its background reader task so short-lived clients do not leak socket file descriptors.

Requirements

  • Store the reader task JoinHandle inside PodClient or otherwise provide owned cancellation.
  • Implement Drop for PodClient to abort/cancel the reader task.
  • Ensure aborting the reader task drops the read half of the Unix socket promptly.
  • Preserve normal long-lived TUI client behavior: events should continue to be received while PodClient is alive.
  • Ensure PodClient remains usable for existing one-shot clients and communication tools.
  • Avoid relying on remote socket close to terminate the reader task.
  • Add regression coverage that repeatedly creates/drops PodClient connections and verifies server-side/client-side tasks/connections are closed or at least that the reader task is aborted.
  • Add/adjust tests for status probing if needed.

Acceptance criteria

  • Dropping a PodClient aborts its background reader task.
  • Repeated live Pod probing no longer increases open FD count monotonically.
  • Panel can remain open through multiple poll cycles without leaking one Unix socket per live Pod per cycle.
  • Existing Pod client send/receive behavior still works.
  • Focused tests cover reader task cleanup.
  • cargo test -p client ... --lib and relevant TUI/pod tests pass.
  • cargo fmt --check, git diff --check, and target/debug/yoi ticket doctor pass.