Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions internal/im/daemon_bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -1234,11 +1234,15 @@ func daemonSessionTurnIndex(ses *session.Session) int {
return 0
}
last := 0
if n := len(ses.UsageHistory); n > 0 && ses.UsageHistory[n-1].TurnIndex > last {
last = ses.UsageHistory[n-1].TurnIndex
}
if n := len(ses.Metrics); n > 0 && ses.Metrics[n-1].TurnIndex > last {
last = ses.Metrics[n-1].TurnIndex
// #3091: read through the #3086/#3087 lock-guarded snapshot accessors -
// the bare slice reads raced the append callbacks on the same slices.
// Construction-time-only caller keeps the window near zero (low), but
// the accessor comments name exactly this cross-goroutine use.
if hist := ses.UsageHistorySnapshot(); len(hist) > 0 && hist[len(hist)-1].TurnIndex > last {
last = hist[len(hist)-1].TurnIndex
}
if mets := ses.MetricsSnapshot(); len(mets) > 0 && mets[len(mets)-1].TurnIndex > last {
last = mets[len(mets)-1].TurnIndex
}
return last
}
Expand Down
35 changes: 35 additions & 0 deletions internal/im/zz_issue3091_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package im

// Regression probe for #3091: daemonSessionTurnIndex must read the shared
// UsageHistory/Metrics slices through the #3086/#3087 lock-guarded
// snapshot accessors instead of bare slice indexing.

import (
"testing"

"github.com/topcheer/ggcode/internal/metrics"
"github.com/topcheer/ggcode/internal/session"
)

func TestIssue3091_TurnIndexUsesSnapshots(t *testing.T) {
if got := daemonSessionTurnIndex(nil); got != 0 {
t.Fatalf("nil session: got %d, want 0", got)
}
ses := &session.Session{}
if got := daemonSessionTurnIndex(ses); got != 0 {
t.Fatalf("empty session: got %d, want 0", got)
}

// TurnIndex from the last usage entry wins over an older metric.
ses.AddUsageHistoryEntry(session.UsageEntry{TurnIndex: 3})
ses.AppendMetricEvent(metrics.MetricEvent{TurnIndex: 1})
if got := daemonSessionTurnIndex(ses); got != 3 {
t.Fatalf("usage-max: got %d, want 3", got)
}

// A newer metric entry wins over the older usage entry.
ses.AppendMetricEvent(metrics.MetricEvent{TurnIndex: 7})
if got := daemonSessionTurnIndex(ses); got != 7 {
t.Fatalf("metric-max: got %d, want 7", got)
}
}
Loading