Skip to content

fix: prevent data race when Kill() is called shortly after Run()#1699

Open
kimjune01 wants to merge 1 commit into
charmbracelet:mainfrom
kimjune01:fix/issue-1689-kill-startup-race
Open

fix: prevent data race when Kill() is called shortly after Run()#1699
kimjune01 wants to merge 1 commit into
charmbracelet:mainfrom
kimjune01:fix/issue-1689-kill-startup-race

Conversation

@kimjune01
Copy link
Copy Markdown

Summary

  • Kill() now calls cancel() instead of shutdown() directly, preventing data races on handlers, cancelReader, and renderer when shutdown() reads fields that Run() is still initializing
  • Run() checks ctx.Err() before terminal initialization, so calling Kill() before Run() finishes returns ErrProgramKilled cleanly
  • Adds TestKillDuringStartupRace (100 iterations of Kill-during-Run) to cover the fix

Fixes #1689

Test plan

  • go test -race -v ./... passes, including TestKillDuringStartupRace
  • channelHandlers.shutdown() on zero-value is safe (no-op)
  • Kill() remains async, matching existing API contract

Kill() now cancels the context instead of calling shutdown() directly.
This eliminates data races on handlers, cancelReader, and renderer
that occur when shutdown() reads these fields while Run() writes them
during initialization.

Run() detects the cancelled context, exits the event loop, and calls
shutdown() from the same goroutine that initialized the fields. An
early context check in Run() avoids unnecessary terminal initialization
when Kill() was called first.

Fixes charmbracelet#1689
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.

bug: Kill() invoked shortly after Run() results in data races

1 participant