detached 570c3c0

fix(web): free a build slot before waking its waiters and filter paths once

commit
570c3c01936f4e32209888090e68f1ae210f5bd1
author
brice · · 2026-10-12 05:22 UTC
parent
ff596dd
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +3 -5
M internal/adapters/ermitegit/convert.go +2 -4
@@ -408,15 +408,13 @@ func (w *pathWalker) walk(tree git.ObjectID) error {
408408 }
409409
410410 // add lists the file name of the current directory, and says whether the walk
411-// goes on. A path that is not addressable is not written to a chunk.
411+// goes on. PathList.Add filters the paths that are not addressable; their
412+// bytes stay in the chunk, which is rare enough not to matter.
412413 func (w *pathWalker) add(name []byte) bool {
413414 mark := len(w.prefix)
414415 w.prefix = append(w.prefix, name...)
415416 path := w.prefix
416417 w.prefix = w.prefix[:mark]
417- if !domain.IsAddressablePathBytes(path) {
418- return true
419- }
420418 if w.chunk.Cap()-w.chunk.Len() < len(path) {
421419 w.chunk = strings.Builder{}
422420 w.chunk.Grow(max(pathChunk, len(path)))
@@ -408,15 +408,13 @@ func (w *pathWalker) walk(tree git.ObjectID) error {
408 }408 }
409 409
410 // add lists the file name of the current directory, and says whether the walk410 // add lists the file name of the current directory, and says whether the walk
411-// goes on. A path that is not addressable is not written to a chunk.411+// goes on. PathList.Add filters the paths that are not addressable; their
412+// bytes stay in the chunk, which is rare enough not to matter.
412 func (w *pathWalker) add(name []byte) bool {413 func (w *pathWalker) add(name []byte) bool {
413 mark := len(w.prefix)414 mark := len(w.prefix)
414 w.prefix = append(w.prefix, name...)415 w.prefix = append(w.prefix, name...)
415 path := w.prefix416 path := w.prefix
416 w.prefix = w.prefix[:mark]417 w.prefix = w.prefix[:mark]
417- if !domain.IsAddressablePathBytes(path) {
418- return true
419- }
420 if w.chunk.Cap()-w.chunk.Len() < len(path) {418 if w.chunk.Cap()-w.chunk.Len() < len(path) {
421 w.chunk = strings.Builder{}419 w.chunk = strings.Builder{}
422 w.chunk.Grow(max(pathChunk, len(path)))420 w.chunk.Grow(max(pathChunk, len(path)))
M internal/adapters/web/pathcache.go +1 -1
@@ -173,7 +173,6 @@ func (c *pathCache) joinOrStart(ctx context.Context, key, id string, build func(
173173 // nobody above this goroutine could recover it and it would end the process.
174174 func (c *pathCache) run(ctx context.Context, key, id string, f *pathBuild, build func() ([]byte, error)) {
175175 defer func() {
176- defer func() { <-c.slots }()
177176 if r := recover(); r != nil {
178177 f.body, f.err = nil, errBuildPanicked
179178 zerowrap.Ctx(ctx).Error().Interface("panic", r).Str("stack", string(debug.Stack())).Msg("path list build panicked")
@@ -191,5 +190,6 @@ func (c *pathCache) finish(key, id string, f *pathBuild) {
191190 c.putLocked(id, f.body)
192191 }
193192 c.mu.Unlock()
193+ <-c.slots // free before the waiters wake: one of them may start the next build
194194 close(f.done)
195195 }
@@ -173,7 +173,6 @@ func (c *pathCache) joinOrStart(ctx context.Context, key, id string, build func(
173 // nobody above this goroutine could recover it and it would end the process.173 // nobody above this goroutine could recover it and it would end the process.
174 func (c *pathCache) run(ctx context.Context, key, id string, f *pathBuild, build func() ([]byte, error)) {174 func (c *pathCache) run(ctx context.Context, key, id string, f *pathBuild, build func() ([]byte, error)) {
175 defer func() {175 defer func() {
176- defer func() { <-c.slots }()
177 if r := recover(); r != nil {176 if r := recover(); r != nil {
178 f.body, f.err = nil, errBuildPanicked177 f.body, f.err = nil, errBuildPanicked
179 zerowrap.Ctx(ctx).Error().Interface("panic", r).Str("stack", string(debug.Stack())).Msg("path list build panicked")178 zerowrap.Ctx(ctx).Error().Interface("panic", r).Str("stack", string(debug.Stack())).Msg("path list build panicked")
@@ -191,5 +190,6 @@ func (c *pathCache) finish(key, id string, f *pathBuild) {
191 c.putLocked(id, f.body)190 c.putLocked(id, f.body)
192 }191 }
193 c.mu.Unlock()192 c.mu.Unlock()
193+ <-c.slots // free before the waiters wake: one of them may start the next build
194 close(f.done)194 close(f.done)
195 }195 }

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 2

Tags 44

↑↓ moveenter openesc close