I'm trying to use channels to list directory trees recursively.
Currently I get a list of few files and then it gets stuck on one directory. Directory is sent to a worker but it doesn't process it.
How the directory should be sent inside the worker (if file.IsDir()
) so that it gets properly processed and also notify the file lister that there are no new files to be processed after recursion is done?
Here's my current attempt:
package main
import (
"fmt"
"os"
"path/filepath"
"errors"
"log"
)
// Job for worker
type workerJob struct {
Root string
}
// Result of a worker
type workerResult struct {
Filename string
}
func worker(jobs chan workerJob, results chan<- workerResult, done chan bool) {
for j := range jobs {
log.Printf(`Directory: %#v`, j.Root)
dir, err := os.Open(j.Root)
if err != nil {
if os.IsPermission(err) {
// Skip if there's no permission
continue
}
continue
}
fInfo, err := dir.Readdir(-1)
dir.Close()
if err != nil {
if os.IsPermission(err) {
// Skip if there's no permission
continue
}
continue
}
for _, file := range fInfo {
fpath := filepath.Join(dir.Name(), file.Name())
if file.Mode().IsRegular() {
// is file
fs := uint64(file.Size())
if fs == 0 {
// Skip zero sized
continue
}
r := workerResult{
Filename: fpath,
}
log.Printf(`sent result: %#v`, r.Filename)
results <- r
} else if file.IsDir() {
// Send directory to be processed by the worker
nj := workerJob{
Root: fpath,
}
log.Printf(`sent new dir job: %#v`, nj.Root)
jobs <- nj
}
}
done <- true
}
}
func main() {
dir := `/tmp`
workerCount := 1
jobs := make(chan workerJob, workerCount)
results := make(chan workerResult)
readDone := make(chan bool)
// start N workers
for i := 0; i < workerCount; i++ {
go worker(jobs, results, readDone)
}
jobs <- workerJob{
Root: dir,
}
readloop:
for {
select {
case res := <-results:
log.Printf(`result=%#v`, res.Filename)
case _ = <-readDone:
log.Printf(`got stop`)
break readloop
}
}
}
This results in:
2018/07/12 14:37:29 Directory: "/tmp"
2018/07/12 14:37:29 sent result: "/tmp/.bashrc"
2018/07/12 14:37:29 result="/tmp/.bashrc"
2018/07/12 14:37:29 sent result: "/tmp/.bash_profile"
2018/07/12 14:37:29 result="/tmp/.bash_profile"
2018/07/12 14:37:29 sent result: "/tmp/.bash_logout"
2018/07/12 14:37:29 result="/tmp/.bash_logout"
2018/07/12 14:37:29 sent result: "/tmp/.xinitrc"
2018/07/12 14:37:29 result="/tmp/.xinitrc"
2018/07/12 14:37:29 sent new dir job: "/tmp/.config"
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select]:
main.main()
+0x281
goroutine 5 [chan send]:
main.worker(0xc42005a060, 0xc420078060, 0xc4200780c0)
+0x4e7
created by main.main
+0x109
Process finished with exit code 2
How the deadlock can be fixed?