From cf4690191ed923ac03156915ef4c19571d40cdda Mon Sep 17 00:00:00 2001 From: Jack Phallen Date: Sun, 11 Jan 2026 10:39:27 -0500 Subject: [PATCH] Fix semaphore acquisition order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acquire semaphore before spawning goroutine in calculateDirSizeFast to prevent goroutine explosion. Previously, all subdirectory goroutines were spawned immediately and blocked on the semaphore—now the spawning itself is throttled. --- cmd/analyze/scanner.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/cmd/analyze/scanner.go b/cmd/analyze/scanner.go index 0d7ddca..a417ee5 100644 --- a/cmd/analyze/scanner.go +++ b/cmd/analyze/scanner.go @@ -126,10 +126,10 @@ func scanPathConcurrent(root string, filesScanned, dirsScanned, bytesScanned *in // ~/Library is scanned separately; reuse cache when possible. if isHomeDir && child.Name() == "Library" { + sem <- struct{}{} wg.Add(1) go func(name, path string) { defer wg.Done() - sem <- struct{}{} defer func() { <-sem }() var size int64 @@ -156,10 +156,10 @@ func scanPathConcurrent(root string, filesScanned, dirsScanned, bytesScanned *in // Folded dirs: fast size without expanding. if shouldFoldDirWithPath(child.Name(), fullPath) { + sem <- struct{}{} wg.Add(1) go func(name, path string) { defer wg.Done() - sem <- struct{}{} defer func() { <-sem }() size, err := getDirectorySizeFromDu(path) @@ -180,10 +180,10 @@ func scanPathConcurrent(root string, filesScanned, dirsScanned, bytesScanned *in continue } + sem <- struct{}{} wg.Add(1) go func(name, path string) { defer wg.Done() - sem <- struct{}{} defer func() { <-sem }() size := calculateDirSizeConcurrent(path, largeFileChan, filesScanned, dirsScanned, bytesScanned, currentPath) @@ -311,11 +311,11 @@ func calculateDirSizeFast(root string, filesScanned, dirsScanned, bytesScanned * for _, entry := range entries { if entry.IsDir() { - wg.Add(1) subDir := filepath.Join(dirPath, entry.Name()) + sem <- struct{}{} + wg.Add(1) go func(p string) { defer wg.Done() - sem <- struct{}{} defer func() { <-sem }() walk(p) }(subDir) @@ -446,9 +446,11 @@ func calculateDirSizeConcurrent(root string, largeFileChan chan<- fileEntry, fil if child.IsDir() { if shouldFoldDirWithPath(child.Name(), fullPath) { + sem <- struct{}{} wg.Add(1) go func(path string) { defer wg.Done() + defer func() { <-sem }() size, err := getDirectorySizeFromDu(path) if err == nil && size > 0 { atomic.AddInt64(&total, size) @@ -459,10 +461,10 @@ func calculateDirSizeConcurrent(root string, largeFileChan chan<- fileEntry, fil continue } + sem <- struct{}{} wg.Add(1) go func(path string) { defer wg.Done() - sem <- struct{}{} defer func() { <-sem }() size := calculateDirSizeConcurrent(path, largeFileChan, filesScanned, dirsScanned, bytesScanned, currentPath)