'Range on Unbuffered chan with go routines throws all goroutines are asleep - deadlock [duplicate]

I have written a sample program to do file search, it does the search but fails at the end with

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]: main.main() ~/Learning/golang/GoFileSearch/file_search.go:52 +0x2a7

I have checked many examples of Waitgroups and that portion seems alright to me, I have tried passing it to the function as a reference but that too yields the same results.

What am I missing?

package main

import (
    "bufio"
    "flag"
    "fmt"
    "io/fs"
    "log"
    "os"
    "strings"
    "sync"
)

func main() {
    searchTerm := flag.String("s", "", "Search string")
    dirname := flag.String("dirname", ".", "Directory for search")
    flag.Parse()

    if len(*searchTerm) == 0 {
        log.Fatal("enter search term")
    }

    dirInfo,err := os.Stat(*dirname)
    if err != nil {
        panic(err)
    }

    if !dirInfo.IsDir() {
        panic("please enter a directory for dirname")
    }

    files, err := os.ReadDir(*dirname)
    ch := make(chan string)
    wg:= &sync.WaitGroup{}
    defer close(ch)

    for _, file := range files {
        if file.IsDir() {
            continue
        }
        wg.Add(1)

        fmt.Println("Searching in "+*dirname + "/" + file.Name())
        //search for the keyword in the file
        go func() {
            defer wg.Done()
            search(*searchTerm, *dirname, file, ch)
        }()
    }


    for name := range ch {
        fmt.Println("Search term found in ->"+name)
    }
    wg.Wait()
    fmt.Println("Done")
}


func search(searchTerm string, 
    dirname string, 
    fileEntry fs.DirEntry, 
    resultChan chan string) {

    filePath := dirname + "/" + fileEntry.Name()
    file, err := os.Open(filePath)
    if err != nil {
        log.Println(err)
        return
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    // optionally, resize scanner's capacity for lines over 64K, see next example
    for scanner.Scan() {
        if strings.Contains(scanner.Text(), searchTerm) {
            fmt.Println("Found")
            resultChan <- fileEntry.Name()
            return
        }
    }
}

I would appreciate any help with this



Solution 1:[1]

Thanks to the link shared by Cerise Limón , the problem is with the channel not closing by the defer close(ch) call

I tried the workaround as suggested here

Now my code looks like this and works fine

package main

import (
    "bufio"
    "flag"
    "fmt"
    "io/fs"
    "log"
    "os"
    "strings"
    "sync"
)

func main() {
    searchTerm := flag.String("s", "", "Search string")
    dirname := flag.String("dirname", ".", "Directory for search")
    flag.Parse()

    if len(*searchTerm) == 0 {
        log.Fatal("enter search term")
    }

    dirInfo,err := os.Stat(*dirname)
    if err != nil {
        panic(err)
    }

    if !dirInfo.IsDir() {
        panic("please enter a directory for dirname")
    }

    files, err := os.ReadDir(*dirname)
    ch := make(chan string)
    wg:= &sync.WaitGroup{}
    //defer close(ch)

    for _, file := range files {
        if file.IsDir() {
            continue
        }
        wg.Add(1)

        fmt.Println("Searching in "+*dirname + "/" + file.Name())
        //search for the keyword in the file
        go func() {
            defer wg.Done()
            search(*searchTerm, *dirname, file, ch)
        }()
    }


    // Close channel after goroutines complete.
    go func() {
        wg.Wait()
        close(ch)
    }()

    for name := range ch {
        fmt.Println("Search term found in ->"+name)
    }

    fmt.Println("Done")
}


func search(searchTerm string,
    dirname string,
    fileEntry fs.DirEntry,
    resultChan chan string) {

    filePath := dirname + "/" + fileEntry.Name()
    file, err := os.Open(filePath)
    if err != nil {
        log.Println(err)
        return
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    // optionally, resize scanner's capacity for lines over 64K, see next example
    for scanner.Scan() {
        if strings.Contains(scanner.Text(), searchTerm) {
            fmt.Println("Found")
            resultChan <- fileEntry.Name()
            return
        }
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Orion bigfish