Lo tengo medio hecho... esto funciona:
spoilerpackage main
import (
"context"
"crypto/md5"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"golang.org/x/sync/errgroup"
)
func walkFiles(ctx context.Context, root string, paths chan<- string) {
filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.Mode().IsRegular() {
return nil
}
select {
case paths <- path:
case <-ctx.Done():
return ctx.Err()
}
return nil
})
}
type result struct {
path string
sum [md5.Size]byte
err error
}
func digester(ctx context.Context, paths <-chan string, c chan<- result) error {
for path := range paths {
data, err := ioutil.ReadFile(path)
select {
case c <- result{path, md5.Sum(data), err}:
case <-ctx.Done():
return ctx.Err()
}
}
return nil
}
func MD5All(root string) (map[string][md5.Size]byte, error) {
g, ctx := errgroup.WithContext(context.Background())
paths := make(chan string)
g.Go(func() error {
walkFiles(ctx, root, paths)
return nil
})
// c := make(chan result)
// for i := 0; i < 1; i++ {
// g.Go(func() error {
// return digester(ctx, paths, c)
// })
// }
go func() {
g.Wait()
close(paths)
// close(c)
}()
for p := range paths {
fmt.Println(p)
}
m := make(map[string][md5.Size]byte)
// for r := range c {
// if r.err != nil {
// return nil, r.err
// }
// m[r.path] = r.sum
// }
return m, nil
}
func main() {
m, err := MD5All(os.Args[1])
if err != nil {
fmt.Println(err)
return
}
var paths []string
for path := range m {
paths = append(paths, path)
}
sort.Strings(paths)
for _, path := range paths {
fmt.Printf("%x %s\n", m[path], path)
}
}
Ahora estoy tratando de hacer el result que me salta un deadlock... XD all gorutines asleep
pero vamos la idea que yo tengo es esta.
tengo una gorutina en un errogrup que lee paths
tengo N workers en este group que van haciendo digest y publicando en results (c chan result)
si hay cualquier error uso el ctx para cortar...
hago un g.Wait() y cierro los canales...
luego proceso el resultado
edit2: esto es lo mejor que tengo, no entiendo el deadlock aun
package main
import (
"context"
"crypto/md5"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"golang.org/x/sync/errgroup"
)
func walkFiles(ctx context.Context, root string, paths chan<- string) error {
return filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.Mode().IsRegular() {
return nil
}
select {
case paths <- path:
case <-ctx.Done():
return ctx.Err()
}
return nil
})
}
type result struct {
path string
sum [md5.Size]byte
err error
}
func digester(ctx context.Context, paths <-chan string, c chan<- result) error {
for {
select {
case path, open := <-paths:
if !open {
return nil
}
data, err := ioutil.ReadFile(path)
select {
case c <- result{path, md5.Sum(data), err}:
case <-ctx.Done():
return ctx.Err()
}
case <-ctx.Done():
return ctx.Err()
}
}
}
func MD5All(root string) (map[string][md5.Size]byte, error) {
g, ctx := errgroup.WithContext(context.Background())
paths := make(chan string)
g.Go(func() error {
return walkFiles(ctx, root, paths)
})
c := make(chan result)
numDigesters := 1
for i := 0; i < numDigesters; i++ {
g.Go(func() error {
return digester(ctx, paths, c)
})
}
go func() {
g.Wait()
close(paths)
close(c)
}()
m := make(map[string][md5.Size]byte)
for {
select {
case r, open := <-c:
if !open {
return m, nil
}
if r.err != nil {
return nil, r.err
}
m[r.path] = r.sum
}
}
}
func main() {
m, err := MD5All(os.Args[1])
if err != nil {
fmt.Println(err)
return
}
var paths []string
for path := range m {
paths = append(paths, path)
}
sort.Strings(paths)
for _, path := range paths {
fmt.Printf("%x %s\n", m[path], path)
}
}
he cambiado los range a for select y en teoria asi no bloqueo no? y cuando se cierra XD
aver si alguien que sabe go me resuelve la duda de como se hace esto. es el primer ejercicio con errgroups y asi que hago.
edit3:
If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the "select" statement blocks until at least one of the communications can proceed.
blocks ayy