detached 71feaa7

fix(hints): reap late capture replies while idle

commit
71feaa7f230961e74779acd3ee24af8c7df9e96d
author
brice · · 2026-10-19 15:28 UTC
parent
61fd749
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +21 -0
M internal/adapters/hints/hints.go +8 -0
@@ -81,9 +81,17 @@ func run(ctx context.Context, requests <-chan ports.HintScanRequest, captures ch
8181 select {
8282 case <-ctx.Done():
8383 return nil
84+ case done := <-captured:
85+ // A capture abandoned by a timeout is finally done: release it.
86+ w.reap(done.ID)
8487 case req := <-requests:
8588 res := w.serve(ctx, req, requests, captures, captured, results, timeout)
8689 if !send(ctx, results, res) {
90+ // The dump job will never run: release its frame.
91+ if j := w.pending; j != nil {
92+ w.pending = nil
93+ j.m.unmap()
94+ }
8795 return nil
8896 }
8997 w.dump(ctx)
@@ -81,9 +81,17 @@ func run(ctx context.Context, requests <-chan ports.HintScanRequest, captures ch
81 select {81 select {
82 case <-ctx.Done():82 case <-ctx.Done():
83 return nil83 return nil
84+ case done := <-captured:
85+ // A capture abandoned by a timeout is finally done: release it.
86+ w.reap(done.ID)
84 case req := <-requests:87 case req := <-requests:
85 res := w.serve(ctx, req, requests, captures, captured, results, timeout)88 res := w.serve(ctx, req, requests, captures, captured, results, timeout)
86 if !send(ctx, results, res) {89 if !send(ctx, results, res) {
90+ // The dump job will never run: release its frame.
91+ if j := w.pending; j != nil {
92+ w.pending = nil
93+ j.m.unmap()
94+ }
87 return nil95 return nil
88 }96 }
89 w.dump(ctx)97 w.dump(ctx)
M internal/adapters/hints/hints_test.go +13 -0
@@ -418,3 +418,16 @@ func TestNoTesseractDumpStillCaptures(t *testing.T) {
418418 require.NoError(t, (<-h.results).Err)
419419 waitFiles(t, dir, 3)
420420 }
421+
422+// A capture that completes after its scan timed out is released while the
423+// worker is idle, without another scan, and its reply does not linger.
424+func TestHintLateCaptureReaped(t *testing.T) {
425+ h := startCapturing(t, 50*time.Millisecond)
426+ h.requests <- ports.HintScanRequest{Seq: 1, Output: "o", Width: 64, Height: 32, Scale: 1}
427+ creq := <-h.captures
428+ res := <-h.results
429+ require.ErrorIs(t, res.Err, ErrTimeout)
430+ // The worker is idle; the output finally answers.
431+ h.captured <- ports.CaptureDone{ID: creq.ID, Output: creq.Output}
432+ require.Eventually(t, func() bool { return len(h.captured) == 0 }, time.Second, time.Millisecond)
433+}
@@ -418,3 +418,16 @@ func TestNoTesseractDumpStillCaptures(t *testing.T) {
418 require.NoError(t, (<-h.results).Err)418 require.NoError(t, (<-h.results).Err)
419 waitFiles(t, dir, 3)419 waitFiles(t, dir, 3)
420 }420 }
421+
422+// A capture that completes after its scan timed out is released while the
423+// worker is idle, without another scan, and its reply does not linger.
424+func TestHintLateCaptureReaped(t *testing.T) {
425+ h := startCapturing(t, 50*time.Millisecond)
426+ h.requests <- ports.HintScanRequest{Seq: 1, Output: "o", Width: 64, Height: 32, Scale: 1}
427+ creq := <-h.captures
428+ res := <-h.results
429+ require.ErrorIs(t, res.Err, ErrTimeout)
430+ // The worker is idle; the output finally answers.
431+ h.captured <- ports.CaptureDone{ID: creq.ID, Output: creq.Output}
432+ require.Eventually(t, func() bool { return len(h.captured) == 0 }, time.Second, time.Millisecond)
433+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 25

Tags 10

↑↓ moveenter openesc close