Skip to content

Drop in replacement for errgroup that converts panics to errors

License

Notifications You must be signed in to change notification settings

StevenACoffman/errgroup

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Actions Status Go Report Card GoDoc license

StevenACoffman/errgroup

StevenACoffman/errgroup is a drop-in alternative to Go's wonderful sync/errgroup but it converts goroutine panics to errors.

While net/http installs a panic handler with each request-serving goroutine, goroutines do not and cannot inherit panic handlers from parent goroutines, so a panic() in one of the child goroutines will kill the whole program.

So whenever you use an sync.errgroup, with some discipline, you can always remember to add a deferred recover() to every goroutine. This library just avoids that boilerplate and does that for you.

You can see it in use

package main

import (
	"fmt"

	"github.com/StevenACoffman/errgroup"
)

func main() {
	g := new(errgroup.Group)
	var urls = []string{
		"http://www.golang.org/",
		"http://www.google.com/",
		"http://www.somestupidname.com/",
	}
	for i := range urls {
		// Launch a goroutine to fetch the URL.
		i := i // https://golang.org/doc/faq#closures_and_goroutines
		g.Go(func() error {

			// deliberate index out of bounds triggered
			fmt.Println("Fetching:", i, urls[i+1])

			return nil
		})
	}
	// Wait for all HTTP fetches to complete.
	err := g.Wait()
	if err == nil {
		fmt.Println("Successfully fetched all URLs.")
	} else {
		fmt.Println(err)
	}
}

This work was done by my co-worker Ben Kraft, and, with his permission, I lightly modified it to lift it out of our repository for Go community discussion.

Counterpoint

There is an interesting discussion which has an alternative view that, with few exceptions, panics should crash your program.

Prior Art

With only a cursory search, I found a few existing open source examples.

Kratos Go framework for microservices has a similar errgroup solution.

PanicGroup by Sergey Alexandrovich

In the article Errors in Go: From denial to acceptance, (which advocates panic based flow control 😱), they have a PanicGroup that's roughly equivalent:

type PanicGroup struct {
  wg      sync.WaitGroup
  errOnce sync.Once
  err     error
}

func (g *PanicGroup) Wait() error {
  g.wg.Wait()
  return g.err
}

func (g *PanicGroup) Go(f func()) {
  g.wg.Add(1)

  go func() {
    defer g.wg.Done()
    defer func(){
      if r := recover(); r != nil {
        if err, ok := r.(error); ok {
          // We need only the first error, sync.Once is useful here.
          g.errOnce.Do(func() {
            g.err = err
          })
        } else {
          panic(r)
        }
      }
    }()

    f()
  }()
}

About

Drop in replacement for errgroup that converts panics to errors

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages