Skip to content

Instantly share code, notes, and snippets.

@sebnyberg
Created January 17, 2022 15:17
Show Gist options
  • Save sebnyberg/09c2865b3e1636b65d5d918c88ec1f0d to your computer and use it in GitHub Desktop.
Save sebnyberg/09c2865b3e1636b65d5d918c88ec1f0d to your computer and use it in GitHub Desktop.
concurrent_errors.go
package main
import (
"context"
"fmt"
"math/rand"
"strings"
"sync"
"time"
"golang.org/x/sync/errgroup"
)
type ManyErrors struct {
errors []error
mtx sync.Mutex
}
func (e *ManyErrors) Error() string {
errStrs := make([]string, len(e.errors))
for i := range e.errors {
errStrs[i] = e.errors[i].Error()
}
return strings.Join(errStrs, ", ")
}
func (e *ManyErrors) AddError(err error) {
if err != nil {
e.mtx.Lock()
e.errors = append(e.errors, err)
defer e.mtx.Unlock()
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// var wg sync.WaitGroup
g, gctx := errgroup.WithContext(ctx)
var errors ManyErrors
poolc := make(chan struct{}, 10)
for i := 0; i < 30; i++ {
select {
case poolc <- struct{}{}:
case <-gctx.Done():
fmt.Println("context canceled, stopping work loop")
goto breakLoop
}
x := i
g.Go(func() error {
defer func() {
<-poolc
}()
if err := maybeFailAfterSomeTime(ctx, x); err != nil {
errors.AddError(err)
return err
}
return nil
})
}
breakLoop:
if err := g.Wait(); err != nil {
fmt.Println(errors.Error())
}
}
func maybeFailAfterSomeTime(ctx context.Context, i int) error {
fmt.Printf("work %v started...\n", i)
ms := time.Millisecond * time.Duration(rand.Intn(1000))
select {
case <-time.After(ms):
if rand.Intn(100) > 50 {
return fmt.Errorf("work %v failed", i)
}
fmt.Printf("work %v done\n", i)
return nil
case <-ctx.Done():
fmt.Printf("work %v canceled...\n", i)
return ctx.Err()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment