Conversation
pkg/app/app.go
Outdated
| startupEvents := make(chan runtime.Event, 10) | ||
| go func() { | ||
| defer close(startupEvents) | ||
| a.runtime.EmitStartupInfo(context.Background(), a.session, startupEvents) |
There was a problem hiding this comment.
🔴 HIGH SEVERITY: Goroutine leak - context cancellation removed
The refactored reEmitStartupInfo() method uses context.Background() instead of accepting a context parameter. This removes the cancellation capability that existed in the original code.
Original code (lines 690-703 in SetCurrentAgentModel and 850-862 in ReplaceSession):
case <-ctx.Done():
returnNew code (line 582):
a.runtime.EmitStartupInfo(context.Background(), a.session, startupEvents)Impact:
- Goroutine leaks on shutdown: When the app is closed, these goroutines cannot be cancelled and will continue running until they complete naturally
- Stale event delivery: If users rapidly call
/clear(which triggersNewSession()), multiple uncancellable goroutines will stack up, potentially sending events from old sessions to the new session - Resource exhaustion: Repeated
/clearcommands will spawn goroutines that cannot be cleaned up
Recommendation:
Change reEmitStartupInfo() to accept a context.Context parameter:
func (a *App) reEmitStartupInfo(ctx context.Context) {
a.runtime.ResetStartupInfo()
go func() {
startupEvents := make(chan runtime.Event, 10)
go func() {
defer close(startupEvents)
a.runtime.EmitStartupInfo(ctx, a.session, startupEvents)
}()
for event := range startupEvents {
select {
case a.events <- event:
case <-ctx.Done():
return
default:
}
}
}()
}For NewSession() which doesn't have a context, you could use a.ctx if the App struct has one, or create a cancellable context that's stored and cancelled on the next /clear.
| }() | ||
| for event := range startupEvents { | ||
| select { | ||
| case a.events <- event: |
There was a problem hiding this comment.
🟡 MEDIUM SEVERITY: Silent event drops without cancellation fallback
The select statement at line 586 has a default case that silently drops events when a.events is full, but lacks the ctx.Done() case that was present in the original code.
Original code (lines 698, 856):
select {
case a.events <- event:
case <-ctx.Done():
return
}New code (line 586):
select {
case a.events <- event:
default:
}Impact:
Without the ctx.Done() case, if the goroutine is blocked or slow, there's no way to interrupt it during app shutdown or session replacement. This makes the pattern less robust than the original implementation.
Recommendation:
Add context cancellation to the select statement (see the recommendation in the previous comment for the complete fix).
Signed-off-by: David Gageot <david.gageot@docker.com>
Properly re-initialize the TUI after a
/clear