63 lines
1.5 KiB
Go
63 lines
1.5 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"math/rand"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Goal: Read multiple files concurrently. All files must be read successfully.
|
|
// If one goroutine fails, cancel all the other goroutines.
|
|
//
|
|
// The cancelled goroutines can inspect the error from canceling the context with ctx.Err()..
|
|
|
|
func ReadFilesConcurrently(paths []string) ([][]byte, error) {
|
|
var contents [][]byte
|
|
|
|
ctx, cancel := context.WithCancelCause(context.Background())
|
|
defer cancel(nil)
|
|
|
|
resCh := make(chan []byte)
|
|
wg := sync.WaitGroup{}
|
|
|
|
for _, path := range paths {
|
|
wg.Add(1)
|
|
go func(ctx context.Context, cancel context.CancelCauseFunc, p string, resCh chan<- []byte, wg *sync.WaitGroup) {
|
|
time.Sleep(time.Duration(rand.Intn(10)) * time.Microsecond) // simulate workload
|
|
select {
|
|
case <-ctx.Done():
|
|
log.Printf("ReadFilesConcurrently (goroutine): Context canceled for path %s: %v", p, ctx.Err())
|
|
wg.Done()
|
|
return
|
|
default:
|
|
// If an error occurs here, cancel all the other goroutines
|
|
content, err := ReadFile(p)
|
|
if err != nil {
|
|
cancel(fmt.Errorf("error reading %s: %w", p, err))
|
|
log.Printf("ReadFilesConcurrently (goroutine): Context canceled for path %s: %v", p, err)
|
|
wg.Done()
|
|
return
|
|
}
|
|
resCh <- content
|
|
}
|
|
}(ctx, cancel, path, resCh, &wg)
|
|
}
|
|
|
|
go func() {
|
|
wg.Wait()
|
|
close(resCh)
|
|
}()
|
|
|
|
for c := range resCh {
|
|
contents = append(contents, c)
|
|
}
|
|
|
|
if e := ctx.Err(); e != nil {
|
|
return nil, fmt.Errorf("ReadFilesConcurrently: %w", e)
|
|
}
|
|
return contents, nil
|
|
}
|