Skip to content

fix: synchronize canceled input cleanup - #1876

Open
johnnychen94 wants to merge 2 commits into
charmbracelet:mainfrom
johnnychen94:fix/canceled-input-reader-lifetime
Open

johnnychen94 wants to merge 2 commits into
charmbracelet:mainfrom
johnnychen94:fix/canceled-input-reader-lifetime

Conversation

@johnnychen94

Copy link
Copy Markdown

Fixes #1875.

Wait for cancelable input on killed shutdowns as well as normal exits before closing the cancel reader. Context cancellation currently takes the killed path and can close the reader while its worker still accesses the cancellation pipe.

Preserve the existing 500ms shutdown bound. If the input loop is still active at that deadline, retain the original reader and completion channel and close that reader only after completion. The Cancel() == false fallback remains unchanged; this does not make arbitrary blocking readers cancelable or promise that delayed cleanup has finished when Run returns.

Regression coverage

The added suite fails on the unmodified base and covers:

  • Quit/Kill cleanup ordering and repeated shutdown.
  • Slow input beyond the timeout: bounded return, no premature Close, and eventual cleanup of the captured reader rather than a replacement field.
  • Nil reader, missing input loop, completed loop, and uncancelable-input fallbacks.
  • Context cancellation and Kill through the public Program API using a real file, retaining the external cancellation error and borrowed-input ownership.

Validation

Passed locally on macOS arm64, Go 1.26.1:

go test -race -timeout=120s -run 'TestShutdown.*Input|TestProgramCanceledInput' -count=100 -cpu=1,4 ./...
go test -race -count=4 -cpu=1,4 -timeout=180s ./...
golangci-lint run

The standalone terminal MWE in #1875 completed 100 cancellations under -race with this Bubble Tea change and the existing released Ultraviolet dependency. Examples also built with Go 1.26.7, as required by their module. Other platforms and GitHub CI have not yet been verified.

Related work

Ultraviolet #221 separately makes pending event delivery cancelable. Without that fix, an exiting consumer may keep an input loop blocked; this PR retains the timeout and safe deferred cleanup for that case. No fork dependency or dependency upgrade is included here.

Prepared with Codex (GPT 6.1 sol). The reproduction, regression suites, and validation commands above were run locally.

Wait for cancelable input on killed shutdowns as well as normal exits. Retain the existing shutdown timeout, but defer reader Close until its captured input loop finishes if that timeout expires.

Cover completion ordering, timeout ownership, fallback inputs, repeated shutdown, and cancellation through the public Program API.

AI-assisted-by: Codex (GPT 6.1 sol)
AI-assisted-by: Codex (GPT 6.1 sol)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

v2: context cancellation races cancelreader cleanup after startup

1 participant