// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package errgroup provides synchronization, error propagation, and Context // cancelation for groups of goroutines working on subtasks of a common task.
package errgroup import ( ) type token struct{} // A Group is a collection of goroutines working on subtasks that are part of // the same overall task. // // A zero Group is valid, has no limit on the number of active goroutines, // and does not cancel on error. type Group struct { cancel func(error) wg sync.WaitGroup sem chan token errOnce sync.Once err error } func ( *Group) () { if .sem != nil { <-.sem } .wg.Done() } // WithContext returns a new Group and an associated Context derived from ctx. // // The derived Context is canceled the first time a function passed to Go // returns a non-nil error or the first time Wait returns, whichever occurs // first. func ( context.Context) (*Group, context.Context) { , := withCancelCause() return &Group{cancel: }, } // Wait blocks until all function calls from the Go method have returned, then // returns the first non-nil error (if any) from them. func ( *Group) () error { .wg.Wait() if .cancel != nil { .cancel(.err) } return .err } // Go calls the given function in a new goroutine. // It blocks until the new goroutine can be added without the number of // active goroutines in the group exceeding the configured limit. // // The first call to return a non-nil error cancels the group's context, if the // group was created by calling WithContext. The error will be returned by Wait. func ( *Group) ( func() error) { if .sem != nil { .sem <- token{} } .wg.Add(1) go func() { defer .done() if := (); != nil { .errOnce.Do(func() { .err = if .cancel != nil { .cancel(.err) } }) } }() } // TryGo calls the given function in a new goroutine only if the number of // active goroutines in the group is currently below the configured limit. // // The return value reports whether the goroutine was started. func ( *Group) ( func() error) bool { if .sem != nil { select { case .sem <- token{}: // Note: this allows barging iff channels in general allow barging. default: return false } } .wg.Add(1) go func() { defer .done() if := (); != nil { .errOnce.Do(func() { .err = if .cancel != nil { .cancel(.err) } }) } }() return true } // SetLimit limits the number of active goroutines in this group to at most n. // A negative value indicates no limit. // // Any subsequent call to the Go method will block until it can add an active // goroutine without exceeding the configured limit. // // The limit must not be modified while any goroutines in the group are active. func ( *Group) ( int) { if < 0 { .sem = nil return } if len(.sem) != 0 { panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(.sem))) } .sem = make(chan token, ) }