summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--analyze/dir.go79
-rw-r--r--analyze/dir_test.go8
-rw-r--r--internal/testanalyze/analyze.go17
-rw-r--r--internal/testapp/app.go5
-rw-r--r--stdout/stdout.go12
-rw-r--r--tui/tui.go34
-rw-r--r--tui/tui_test.go5
7 files changed, 97 insertions, 63 deletions
diff --git a/analyze/dir.go b/analyze/dir.go
index fbbc1c7..3ec8b0f 100644
--- a/analyze/dir.go
+++ b/analyze/dir.go
@@ -10,11 +10,10 @@ import (
// CurrentProgress struct
type CurrentProgress struct {
- Mutex *sync.Mutex
+ mutex *sync.Mutex
CurrentItemName string
ItemCount int
TotalSize int64
- Done bool
}
var concurrencyLimit chan struct{} = make(chan struct{}, 3*runtime.GOMAXPROCS(0))
@@ -25,40 +24,49 @@ type ShouldDirBeIgnored func(path string) bool
// Analyzer is type for dir analyzing function
type Analyzer interface {
AnalyzeDir(path string, ignore ShouldDirBeIgnored) *Dir
- GetProgress() *CurrentProgress
+ GetProgressChan() chan CurrentProgress
+ GetDoneChan() chan struct{}
ResetProgress()
}
// ParallelAnalyzer implements Analyzer
type ParallelAnalyzer struct {
- progress *CurrentProgress
- wait sync.WaitGroup
- ignoreDir ShouldDirBeIgnored
+ progress *CurrentProgress
+ progressChan chan CurrentProgress
+ doneChan chan struct{}
+ wait sync.WaitGroup
+ ignoreDir ShouldDirBeIgnored
}
// CreateAnalyzer returns Analyzer
func CreateAnalyzer() Analyzer {
return &ParallelAnalyzer{
progress: &CurrentProgress{
- Mutex: &sync.Mutex{},
- Done: false,
+ mutex: &sync.Mutex{},
ItemCount: 0,
TotalSize: int64(0),
},
+ progressChan: make(chan CurrentProgress, 10),
+ doneChan: make(chan struct{}, 1),
}
}
-// GetProgress returns progress
-func (a *ParallelAnalyzer) GetProgress() *CurrentProgress {
- return a.progress
+// GetProgressChan returns channel for getting progress
+func (a *ParallelAnalyzer) GetProgressChan() chan CurrentProgress {
+ return a.progressChan
+}
+
+// GetDoneChan returns channel for checking when analysis is done
+func (a *ParallelAnalyzer) GetDoneChan() chan struct{} {
+ return a.doneChan
}
// ResetProgress returns progress
func (a *ParallelAnalyzer) ResetProgress() {
- a.progress.Done = false
a.progress.ItemCount = 0
a.progress.TotalSize = int64(0)
- a.progress.Mutex = &sync.Mutex{}
+ a.progress.CurrentItemName = ""
+ a.progress.mutex = &sync.Mutex{}
}
// AnalyzeDir analyzes given path
@@ -71,20 +79,19 @@ func (a *ParallelAnalyzer) AnalyzeDir(path string, ignore ShouldDirBeIgnored) *D
links := make(AlreadyCountedHardlinks, 10)
dir.UpdateStats(links)
- a.progress.Mutex.Lock()
- a.progress.Done = true
- a.progress.Mutex.Unlock()
+ a.doneChan <- struct{}{}
return dir
}
func (a *ParallelAnalyzer) processDir(path string) *Dir {
var (
- file *File
- err error
- mutex sync.Mutex
- totalSize int64
- info os.FileInfo
+ file *File
+ err error
+ totalSize int64
+ info os.FileInfo
+ subDirChan chan *Dir = make(chan *Dir)
+ dirCount int = 0
)
files, err := os.ReadDir(path)
@@ -107,19 +114,15 @@ func (a *ParallelAnalyzer) processDir(path string) *Dir {
if a.ignoreDir(entryPath) {
continue
}
+ dirCount += 1
- a.wait.Add(1)
go func() {
concurrencyLimit <- struct{}{}
subdir := a.processDir(entryPath)
subdir.Parent = dir
- mutex.Lock()
- dir.Files.Append(subdir)
- mutex.Unlock()
-
+ subDirChan <- subdir
<-concurrencyLimit
- a.wait.Done()
}()
} else {
info, err = f.Info()
@@ -137,22 +140,36 @@ func (a *ParallelAnalyzer) processDir(path string) *Dir {
totalSize += info.Size()
- mutex.Lock()
dir.Files.Append(file)
- mutex.Unlock()
}
}
+ a.wait.Add(1)
+ go func() {
+ var sub *Dir
+
+ for i := 0; i < dirCount; i++ {
+ sub = <-subDirChan
+ dir.Files.Append(sub)
+ }
+
+ a.wait.Done()
+ }()
+
a.updateProgress(path, len(files), totalSize)
return dir
}
func (a *ParallelAnalyzer) updateProgress(path string, itemCount int, totalSize int64) {
- a.progress.Mutex.Lock()
+ a.progress.mutex.Lock()
a.progress.CurrentItemName = path
a.progress.ItemCount += itemCount
a.progress.TotalSize += totalSize
- a.progress.Mutex.Unlock()
+ select {
+ case a.progressChan <- *a.progress:
+ default:
+ }
+ a.progress.mutex.Unlock()
}
func getDirFlag(err error, items int) rune {
diff --git a/analyze/dir_test.go b/analyze/dir_test.go
index e576793..93a0834 100644
--- a/analyze/dir_test.go
+++ b/analyze/dir_test.go
@@ -16,9 +16,13 @@ func TestAnalyzeDir(t *testing.T) {
analyzer := CreateAnalyzer()
dir := analyzer.AnalyzeDir("test_dir", func(_ string) bool { return false })
- assert.True(t, analyzer.GetProgress().Done)
+ c := analyzer.GetProgressChan()
+ progress := <-c
+ assert.Equal(t, "test_dir", progress.CurrentItemName)
analyzer.ResetProgress()
- assert.False(t, analyzer.GetProgress().Done)
+
+ done := analyzer.GetDoneChan()
+ <-done
// test dir info
assert.Equal(t, "test_dir", dir.Name)
diff --git a/internal/testanalyze/analyze.go b/internal/testanalyze/analyze.go
index caadf6b..508ff43 100644
--- a/internal/testanalyze/analyze.go
+++ b/internal/testanalyze/analyze.go
@@ -2,7 +2,6 @@ package testanalyze
import (
"errors"
- "sync"
"github.com/dundee/gdu/v4/analyze"
)
@@ -59,12 +58,16 @@ func (a *MockedAnalyzer) AnalyzeDir(path string, ignore analyze.ShouldDirBeIgnor
return dir
}
-// GetProgress returns always Done
-func (a *MockedAnalyzer) GetProgress() *analyze.CurrentProgress {
- return &analyze.CurrentProgress{
- Done: true,
- Mutex: &sync.Mutex{},
- }
+// GetProgressChan returns always Done
+func (a *MockedAnalyzer) GetProgressChan() chan analyze.CurrentProgress {
+ return make(chan analyze.CurrentProgress)
+}
+
+// GetDoneChan returns always Done
+func (a *MockedAnalyzer) GetDoneChan() chan struct{} {
+ c := make(chan struct{}, 1)
+ defer func() { c <- struct{}{} }()
+ return c
}
// ResetProgress does nothing
diff --git a/internal/testapp/app.go b/internal/testapp/app.go
index 04e026c..9cc2309 100644
--- a/internal/testapp/app.go
+++ b/internal/testapp/app.go
@@ -2,6 +2,7 @@ package testapp
import (
"errors"
+ "sync"
"github.com/dundee/gdu/v4/common"
"github.com/gdamore/tcell/v2"
@@ -25,6 +26,7 @@ type MockedApp struct {
FailRun bool
UpdateDraws []func()
BeforeDraws []func(screen tcell.Screen) bool
+ mutex *sync.Mutex
}
// CreateMockedApp returns app with simulation screen for tests
@@ -33,6 +35,7 @@ func CreateMockedApp(failRun bool) common.TermApplication {
FailRun: failRun,
UpdateDraws: make([]func(), 0, 1),
BeforeDraws: make([]func(screen tcell.Screen) bool, 0, 1),
+ mutex: &sync.Mutex{},
}
return app
}
@@ -66,7 +69,9 @@ func (app *MockedApp) SetInputCapture(capture func(event *tcell.EventKey) *tcell
// QueueUpdateDraw does nothing
func (app *MockedApp) QueueUpdateDraw(f func()) *tview.Application {
+ app.mutex.Lock()
app.UpdateDraws = append(app.UpdateDraws, f)
+ app.mutex.Unlock()
return nil
}
diff --git a/stdout/stdout.go b/stdout/stdout.go
index 647e243..8d728fe 100644
--- a/stdout/stdout.go
+++ b/stdout/stdout.go
@@ -202,15 +202,18 @@ func (ui *UI) updateProgress() {
progressRunes := []rune(`⠇⠏⠋⠙⠹⠸⠼⠴⠦⠧`)
- progress := ui.analyzer.GetProgress()
+ progressChan := ui.analyzer.GetProgressChan()
+ doneChan := ui.analyzer.GetDoneChan()
+
+ var progress analyze.CurrentProgress
i := 0
for {
- progress.Mutex.Lock()
-
fmt.Fprint(ui.output, emptyRow)
- if progress.Done {
+ select {
+ case progress = <-progressChan:
+ case <-doneChan:
fmt.Fprint(ui.output, "\r")
return
}
@@ -221,7 +224,6 @@ func (ui *UI) updateProgress() {
ui.red.Sprint(progress.ItemCount)+
" size: "+
ui.formatSize(progress.TotalSize))
- progress.Mutex.Unlock()
time.Sleep(100 * time.Millisecond)
i++
diff --git a/tui/tui.go b/tui/tui.go
index 9a63a10..7ddc0f2 100644
--- a/tui/tui.go
+++ b/tui/tui.go
@@ -344,26 +344,30 @@ func (ui *UI) updateProgress() {
color = "[red:black:b]"
}
- progress := ui.analyzer.GetProgress()
+ progressChan := ui.analyzer.GetProgressChan()
+ doneChan := ui.analyzer.GetDoneChan()
- for {
- progress.Mutex.Lock()
+ var progress analyze.CurrentProgress
- if progress.Done {
+ for {
+ select {
+ case progress = <-progressChan:
+ case <-doneChan:
return
}
- ui.app.QueueUpdateDraw(func() {
- ui.progress.SetText("Total items: " +
- color +
- fmt.Sprint(progress.ItemCount) +
- "[white:black:-] size: " +
- color +
- ui.formatSize(progress.TotalSize, false, false) +
- "[white:black:-]\nCurrent item: [white:black:b]" +
- progress.CurrentItemName)
- })
- progress.Mutex.Unlock()
+ func(itemCount int, totalSize int64, currentItem string) {
+ ui.app.QueueUpdateDraw(func() {
+ ui.progress.SetText("Total items: " +
+ color +
+ fmt.Sprint(itemCount) +
+ "[white:black:-] size: " +
+ color +
+ ui.formatSize(totalSize, false, false) +
+ "[white:black:-]\nCurrent item: [white:black:b]" +
+ currentItem)
+ })
+ }(progress.ItemCount, progress.TotalSize, progress.CurrentItemName)
time.Sleep(100 * time.Millisecond)
}
diff --git a/tui/tui_test.go b/tui/tui_test.go
index 171a682..1d15c3d 100644
--- a/tui/tui_test.go
+++ b/tui/tui_test.go
@@ -61,9 +61,8 @@ func TestUpdateProgress(t *testing.T) {
defer simScreen.Fini()
ui := CreateUI(app, false, false)
- progress := ui.analyzer.GetProgress()
- progress.Done = true
- progress.CurrentItemName = "xxx"
+ done := ui.analyzer.GetDoneChan()
+ done <- struct{}{}
ui.updateProgress()
assert.True(t, true)
}