-
Notifications
You must be signed in to change notification settings - Fork 0
/
group.go
69 lines (62 loc) · 1.64 KB
/
group.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package syncerr
import (
"errors"
"fmt"
"sync"
)
// Group is a concurrent error collector. Unlike errgroup.Group
// it is context-independent and returns all errors. It's zero value is valid.
type Group struct {
sync.Mutex
wg sync.WaitGroup
once sync.Once
errs chan []error
}
// Go runs fn in a separate goroutine with panic recovery and collection of the return error.
func (e *Group) Go(fn func() error) {
e.once.Do(func() {
e.errs = make(chan []error, 1)
e.errs <- nil // when we extract the errors with Join, nils are ignored
})
e.wg.Add(1)
go func() {
var err error
defer e.wg.Done()
defer e.put(&err)
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("Group: panic recovered: %v", r)
}
}()
err = fn()
}()
}
func (e *Group) put(errp *error) {
if *errp != nil {
e.errs <- append(<-e.errs, *errp)
}
}
// Wait blocks as long as the underlying wait group blocks.
func (e *Group) Wait() {
e.wg.Wait()
}
// Done wraps the underlying wait group so that it can be used in select statements.
func (e *Group) Done() <-chan struct{} {
c := make(chan struct{})
go func() {
e.wg.Wait()
close(c)
}()
return c
}
// Error returns the result of calling errors.Join with all errors returned by the functions supplied to Group.Go calls.
// It should be called after Wait() or Done() to ensure all errors are returned.
// If a program doesn't care about all errors, errgroup.Group should be preferred.
// The returned error implements interface{ Unwrap() []error }.
func (e *Group) Error() error {
// this case happens when Go was never called
if len(e.errs) == 0 {
return nil
}
return errors.Join(<-e.errs...)
}