'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 |