@@ -2,6 +2,8 @@ package pool
22
33import (
44 "context"
5+
6+ "github.com/sourcegraph/conc/panics"
57)
68
79// ContextPool is a pool that runs tasks that take a context.
@@ -23,14 +25,31 @@ type ContextPool struct {
2325// are busy, a call to Go() will block until the task can be started.
2426func (p * ContextPool ) Go (f func (ctx context.Context ) error ) {
2527 p .errorPool .Go (func () error {
26- err := f (p .ctx )
27- if err != nil && p .cancelOnError {
28+ // If we aren't cancelling on error, just return the result of f.
29+ if ! p .cancelOnError {
30+ return f (p .ctx )
31+ }
32+
33+ // If we are cancelling on error, then we also want to cancel on panic.
34+ // To do this, we need to recover from any panic f raises.
35+ var err error
36+ recovered := panics .Try (func () { err = f (p .ctx ) })
37+ if err != nil || recovered != nil {
2838 // Leaky abstraction warning: We add the error directly because
2939 // otherwise, canceling could cause another goroutine to exit and
3040 // return an error before this error was added, which breaks the
3141 // expectations of WithFirstError().
32- p .errorPool .addErr (err )
42+ if err != nil {
43+ p .errorPool .addErr (err )
44+ }
45+
3346 p .cancel ()
47+
48+ // Now that context is cancelled, if we caught a panic we can
49+ // propagate it.
50+ if recovered != nil {
51+ panic (recovered )
52+ }
3453 return nil
3554 }
3655 return err
@@ -56,7 +75,7 @@ func (p *ContextPool) WithFirstError() *ContextPool {
5675}
5776
5877// WithCancelOnError configures the pool to cancel its context as soon as
59- // any task returns an error. By default, the pool's context is not
78+ // any task returns an error or panics . By default, the pool's context is not
6079// canceled until the parent context is canceled.
6180//
6281// In this case, all errors returned from the pool after the first will
0 commit comments