Skip to content

Commit 00abc00

Browse files
committed
✨ up: fsutil/finder - update some worker logic, optimize concurrent processing
1 parent 3be227e commit 00abc00

5 files changed

Lines changed: 76 additions & 28 deletions

File tree

fsutil/find.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,19 @@ import (
1111
"github.com/gookit/goutil/strutil"
1212
)
1313

14-
// FilePathInDirs get full file path in dirs.
14+
// FilePathInDirs get full file path in dirs. return empty string if not found.
1515
//
1616
// Params:
1717
// - file: can be relative path, file name, full path.
1818
// - dirs: dir paths
19-
func FilePathInDirs(file string, dirs ...string) string {
20-
file = comfunc.ExpandHome(file)
21-
if FileExists(file) {
22-
return file
19+
func FilePathInDirs(fPath string, dirs ...string) string {
20+
fPath = comfunc.ExpandHome(fPath)
21+
if FileExists(fPath) {
22+
return fPath
2323
}
2424

2525
for _, dirPath := range dirs {
26-
fPath := JoinSubPaths(dirPath, file)
26+
fPath := JoinSubPaths(dirPath, fPath)
2727
if FileExists(fPath) {
2828
return fPath
2929
}

fsutil/find_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ import (
1212
"github.com/gookit/goutil/testutil/fakeobj"
1313
)
1414

15+
func TestFilePathInDirs(t *testing.T) {
16+
result := fsutil.FilePathInDirs("not_existing_file.txt")
17+
assert.Empty(t, result)
18+
result = fsutil.FilePathInDirs("not_existing_file.txt", "testdata")
19+
assert.Empty(t, result)
20+
21+
result = fsutil.FilePathInDirs("find.go")
22+
assert.NotEmpty(t, result)
23+
}
24+
1525
func TestMatchFirst(t *testing.T) {
1626
assert.Eq(t, "testdata", fsutil.MatchFirst([]string{"testdata"}, fsutil.IsDir, ""))
1727

@@ -21,6 +31,8 @@ func TestMatchFirst(t *testing.T) {
2131

2232
ps := fsutil.MatchPaths([]string{"testdata", "testdata/test.jpg"}, fsutil.IsDir)
2333
assert.Eq(t, []string{"testdata"}, ps)
34+
35+
assert.Eq(t, "default_dir", fsutil.MatchFirst([]string{"not_exist_dir"}, fsutil.IsDir, "default_dir"))
2436
}
2537

2638
func TestSearchNameUp(t *testing.T) {

fsutil/finder/config.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ type Config struct {
5656

5757
// ScanDirs scan dir paths for find.
5858
ScanDirs []string `json:"scan_dirs"`
59-
// FindFlags for find result. default is FlagFile
59+
// FindFlags type for find result. default is FlagFile
6060
FindFlags FindFlag `json:"find_flags"`
6161
// MaxDepth for find result. default is 0 - not limit
6262
MaxDepth int `json:"max_depth"`
@@ -383,6 +383,12 @@ func (f *Finder) WithStrFlag(s string) *Finder {
383383
return f
384384
}
385385

386+
// TypeFile only find file.
387+
func (f *Finder) TypeFile() *Finder { return f.WithFlags(FlagFile) }
388+
389+
// TypeDir only find dir.
390+
func (f *Finder) TypeDir() *Finder { return f.WithFlags(FlagDir) }
391+
386392
// OnlyFindDir only find dir.
387393
func (f *Finder) OnlyFindDir() *Finder { return f.WithFlags(FlagDir) }
388394

fsutil/finder/finder.go

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func (f *Finder) EachFile(fn func(file *os.File)) {
119119
if err == nil {
120120
fn(file)
121121
} else {
122-
f.err = err
122+
f.setError(err)
123123
}
124124
})
125125
}
@@ -166,8 +166,8 @@ func (f *Finder) prepare() {
166166
f.debugf("PREPARE done. type-flag: %s, concurrency: %d", f.c.FindFlags, coNum)
167167
f.debugf("config: %+v", f.c)
168168
// 创建队列
169-
f.ch = make(chan Elem, coNum*8)
170-
f.dirQueue = make(chan scanDir, coNum*8)
169+
f.ch = make(chan Elem, coNum*8*3)
170+
f.dirQueue = make(chan scanDir, coNum*8*2)
171171
}
172172

173173
// Do finding
@@ -190,14 +190,14 @@ func (f *Finder) find() <-chan Elem {
190190

191191
f.prepare()
192192

193+
// 添加初始任务
194+
f.addRootDirs()
195+
193196
// 启动工作goroutine
194197
for i := 0; i < f.c.Concurrency; i++ {
195198
go f.worker(i)
196199
}
197200

198-
// 添加初始任务
199-
f.addRootDirs()
200-
201201
// 等待所有任务完成并关闭通道
202202
go func() {
203203
f.debugf("waiting all task complete ...")
@@ -217,13 +217,26 @@ func (f *Finder) find() <-chan Elem {
217217

218218
// worker 处理目录的工作goroutine
219219
func (f *Finder) worker(index int) {
220+
f.debugf("worker#%d STARTING ...", index)
220221
for sd := range f.dirQueue {
221-
func() {
222-
defer f.wg.Done()
223-
f.debugf("worker#%d into dir: %s (depth: %d)", index, sd.path, sd.depth)
224-
f.findDir(sd.path, sd.depth)
225-
}()
222+
f.safeFindDir(index, sd.path, sd.depth)
226223
}
224+
f.debugf("worker#%d DONE.", index)
225+
}
226+
227+
func (f *Finder) safeFindDir(index int, dirPath string, depth int) {
228+
f.debugf("worker#%d into dir: %s (depth: %d)", index, dirPath, depth)
229+
230+
// recover error and always call wg.Done()
231+
defer func() {
232+
if err := recover(); err != nil {
233+
f.debugf("worker#%d panic in dir: %s, ERROR: %v", index, dirPath, err)
234+
f.setError(fmt.Errorf("worker#%d findDir panic, dir: %s, ERROR: %v", index, dirPath, err))
235+
}
236+
f.wg.Done()
237+
}()
238+
239+
f.findDir(dirPath, depth)
227240
}
228241

229242
func (f *Finder) addRootDirs() {
@@ -241,6 +254,7 @@ func (f *Finder) addRootDirs() {
241254

242255
// add task
243256
f.debugf("add root-dir: %s", dirPath)
257+
f.wg.Add(1)
244258
f.dirQueue <- scanDir{path: dirPath}
245259
}
246260
}
@@ -308,7 +322,11 @@ func (f *Finder) findDir(dirPath string, depth int) {
308322
// find in sub dir. 添加子目录任务
309323
if cfg.MaxDepth == 0 || depth < cfg.MaxDepth {
310324
f.debugf("add sub-dir: %s (depth: %d)", fullPath, depth)
311-
f.dirQueue <- scanDir{path: fullPath, depth: depth}
325+
f.wg.Add(1)
326+
// fix: 创建一个 goroutine 添加子目录任务,不然会造成阻塞
327+
go func(p string, d int) {
328+
f.dirQueue <- scanDir{path: p, depth: d}
329+
}(fullPath, depth)
312330
}
313331
continue
314332
}

fsutil/finder/finder_test.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func TestFinder_find_file(t *testing.T) {
2626
f := finder.EmptyFinder().
2727
WithDebug().
2828
ScanDir(".").
29+
// ScanDir("C:\\Users\\inhere\\workspace\\godev\\gookit\\goutil").
2930
NoDotFile().
3031
NoDotDir().
3132
ExcludeName("*_test.go").
@@ -41,6 +42,7 @@ func TestFinder_find_file(t *testing.T) {
4142

4243
func TestFinder_find_file_withCache(t *testing.T) {
4344
f := finder.EmptyFinder().
45+
TypeFile().
4446
WithDebug().
4547
ScanDir("./testdata").
4648
NoDotFile().
@@ -102,26 +104,36 @@ func TestFinder_find_file_withCache(t *testing.T) {
102104
}
103105

104106
func TestFinder_OnlyFindDir(t *testing.T) {
107+
// ff := finder.NewFinder("./../").
105108
ff := finder.NewFinder("./../../").
109+
// ff := finder.NewFinder("C:\\Users\\inhere\\workspace\\godev\\gookit").
110+
// ff := finder.EmptyFinder().ScanDir("./../").
111+
WithConcurrency(2).
106112
OnlyFindDir().
107113
UseAbsPath().
108114
WithoutDotDir().
109-
WithDirName("testdata")
115+
WithDirName("testdata").
116+
WithDebug()
117+
118+
fmt.Println(ff.FindPaths())
119+
// return
110120

111-
ff.EachPath(func(filePath string) {
112-
fmt.Println(filePath)
121+
t.Run("EachPath", func(t *testing.T) {
122+
ff.EachPath(func(filePath string) {
123+
fmt.Println(filePath)
124+
})
125+
assert.Gt(t, ff.Num(), 0)
126+
assert.Eq(t, 0, ff.CacheNum())
113127
})
114-
assert.Gt(t, ff.Num(), 0)
115-
assert.Eq(t, 0, ff.CacheNum())
116128

117-
t.Run("each elem", func(t *testing.T) {
118-
ff.Each(func(elem finder.Elem) {
119-
fmt.Println(elem)
129+
t.Run("Each", func(t *testing.T) {
130+
ff.Each(func(el finder.Elem) {
131+
fmt.Println(el)
120132
})
121133
})
122134

123135
ff.ResetResult()
124-
assert.Eq(t, 0, ff.Num())
136+
assert.Eq(t, uint(0), ff.Num())
125137
assert.Eq(t, 0, ff.CacheNum())
126138

127139
t.Run("max depth", func(t *testing.T) {

0 commit comments

Comments
 (0)