1
votes

I am trying to make a code to scan from a folder link all my files and make a "top 10" by his size with also a regexp based on his content and his name. file. By it content, I make channels with goroutines but I dont understand why each time my goroutines are locked. Here is my code:

package main

import (
    "flag"
    "fmt"
    "io/ioutil"
    "regexp"
    "runtime"
    "sort"
    "sync"
    "time"
)

var rName = ".php"
var rContent = "php"
var maxSize, minSize int64
var files_ten []File

func main() {
    start := time.Now()

    channelOne := make(chan File)
    channelTwo := make(chan File)

    var wg sync.WaitGroup
    var path string
    flag.StringVar(&path, "path", "", "Path to folder")
    flag.Parse()
    fmt.Println("Path=", path)

    for i := 0; i < runtime.NumCPU(); i++ {
        go check(channelOne, channelTwo, &wg)
    }

    go top10(channelTwo, &wg)

    wg.Wait()

    getFolder(path, channelOne, &wg)

    fmt.Println("top 10", files_ten)
    t := time.Now()
    current := t.Sub(start)
    fmt.Println(current)

}

type File struct {
    Size int64
    Name string
    Path string
}

func (this File) GetSize() int64 {
    return this.Size
}

func getFolder(path string, channelOne chan File, wg *sync.WaitGroup) {
    folder, err := ioutil.ReadDir(path)

    if err != nil {
        fmt.Println("Error:", err)
        return
    }

    for _, data := range folder {
        if data.IsDir() {
            var newFolder string = path + data.Name() + "/"
            getFolder(newFolder, channelOne, wg)
        } else {
            wg.Add(1)
            channelOne <- File{Size: data.Size(), Name: data.Name(), Path: path}
        }
    }
}

func check(channelOne chan File, channelTwo chan File, wg *sync.WaitGroup) {
    for {
        file := <-channelOne
        rName := regexp.MustCompile(rName)

        maxSize = 10000
        minSize = 0

        if rName.MatchString(file.Name) {
            if file.Size <= maxSize && file.Size >= minSize {
                f, err := ioutil.ReadFile(file.Path + "/" + file.Name)

                if err != nil {
                    fmt.Println("Error:", err)
                    return
                }
                rContent := regexp.MustCompile(rContent)
                if rContent.MatchString(string(f)) {
                    channelTwo <- file
                } else {
                    wg.Done()
                }
            } else {
                wg.Done()
            }
        } else {
            wg.Done()
        }
    }
}

func sortFilesFromBiggestToLowerSize(arrayFile []File) []File {
    sort.Slice(arrayFile, func(i, j int) bool {
        return arrayFile[i].Size > arrayFile[j].Size
    })
    return arrayFile
}

func top10(channelTwo chan File, wg *sync.WaitGroup) []File {
    for {
        f := <-channelTwo

        if len(files_ten) == 10 {
            if f.Size > files_ten[0].Size || f.Size >
                files_ten[len(files_ten)-1].Size {
                files_ten = files_ten[:len(files_ten)-1]
                files_ten = append(files_ten, f)
                return sortFilesFromBiggestToLowerSize(files_ten)
            }
        } else {
            sortFilesFromBiggestToLowerSize(files_ten)
            return append(files_ten, f)
        }
        wg.Done()
        return files_ten
    }
}

Here is the error each time I compile it :

go run filebysize.go --path=C:/wamp64/www/symfony/init/cours1/
Path= C:/wamp64/www/symfony/init/cours1/
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.getFolder(0xc04210a3c0, 0x3d, 0xc04204c0c0, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:72 +0x28a
main.getFolder(0xc04210a200, 0x32, 0xc04204c0c0, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:69 +0x151
main.getFolder(0xc04200e6c0, 0x26, 0xc04204c0c0, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:69 +0x151
main.getFolder(0xc042051f57, 0x22, 0xc04204c0c0, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:69 +0x151
main.main()
    C:/Users/Sahra/Documents/go/display/filebysize.go:37 +0x2e0

goroutine 19 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 20 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 21 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 22 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 23 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 24 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 25 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d

goroutine 26 [chan send]:
main.check(0xc04204c0c0, 0xc04204c120, 0xc04204e210)
    C:/Users/Sahra/Documents/go/display/filebysize.go:95 +0x2ec
created by main.main
    C:/Users/Sahra/Documents/go/display/filebysize.go:32 +0x26d
exit status 2
1
This is a lot of code to understand, but some things look off: the return value from top10 is ignored, it looks like waitgroup is used to count something other than executing goroutines.Cerise Limón
You are right, the problem was that in the function top 10, i dont need to return anything, include at the if else statement, thanks a lot !!!!!!!!!!!!!!!!!!!!akali leona

1 Answers

1
votes

You're trying to send on channelOne, but nothing reads from it until after wg.Done, hence the deadlock: the routines trying to send to it must wait until something is available to receive, which never occurs.

Also, your WaitGroup use is off; you should call Add before starting each goroutine you want to wait on, then call Done at the end of the goroutine. A single goroutine should not call Add or Done in a loop, and a goroutine should not call Done if there is no associated Add call.

It looks like you have multiple for loops that will never exit; they have no conditions and no breaks.

You can also loop over channels much more simply. You can replace constructs like:

for {
    file := <-channelOne

with the simpler:

for file := range channelOne {

This has the added advantage that when the channel you're ranging over is closed, the loop will exit, allowing you to use closing the channel as a signal that consumers can stop.