github.com/amane3/goreleaser@v0.182.0/internal/semerrgroup/sem.go (about) 1 // Package semerrgroup wraps a error group with a semaphore with configurable 2 // size, so you can control the number of tasks being executed simultaneously. 3 package semerrgroup 4 5 import ( 6 "sync" 7 8 "github.com/amane3/goreleaser/internal/pipe" 9 "golang.org/x/sync/errgroup" 10 ) 11 12 // Group is the Semphore ErrorGroup itself. 13 type Group interface { 14 Go(func() error) 15 Wait() error 16 } 17 18 // New returns a new Group of a given size. 19 func New(size int) Group { 20 if size == 1 { 21 return &serialGroup{} 22 } 23 return ¶llelGroup{ 24 ch: make(chan bool, size), 25 g: errgroup.Group{}, 26 } 27 } 28 29 var _ Group = ¶llelGroup{} 30 31 type parallelGroup struct { 32 ch chan bool 33 g errgroup.Group 34 } 35 36 // Go execs one function respecting the group and semaphore. 37 func (s *parallelGroup) Go(fn func() error) { 38 s.g.Go(func() error { 39 s.ch <- true 40 defer func() { 41 <-s.ch 42 }() 43 return fn() 44 }) 45 } 46 47 // Wait waits for the group to complete and return an error if any. 48 func (s *parallelGroup) Wait() error { 49 return s.g.Wait() 50 } 51 52 var _ Group = &serialGroup{} 53 54 type serialGroup struct { 55 err error 56 errOnce sync.Once 57 } 58 59 // Go execs runs `fn` and saves the result if no error has been encountered. 60 func (s *serialGroup) Go(fn func() error) { 61 if s.err != nil { 62 return 63 } 64 if err := fn(); err != nil { 65 s.errOnce.Do(func() { 66 s.err = err 67 }) 68 } 69 } 70 71 // Wait waits for Go to complete and returns the first error encountered. 72 func (s *serialGroup) Wait() error { 73 return s.err 74 } 75 76 var _ Group = &skipAwareGroup{} 77 78 // NewSkipAware returns a new Group of a given size and aware of pipe skips. 79 func NewSkipAware(g Group) Group { 80 return &skipAwareGroup{g: g} 81 } 82 83 type skipAwareGroup struct { 84 g Group 85 skipErr error 86 skipOnce sync.Once 87 } 88 89 // Go execs runs `fn` and saves the result if no error has been encountered. 90 func (s *skipAwareGroup) Go(fn func() error) { 91 s.g.Go(func() error { 92 var err = fn() 93 // if the err is a skip, set it for later, but return nil for now so the 94 // the group proceeds. 95 if pipe.IsSkip(err) { 96 s.skipOnce.Do(func() { 97 s.skipErr = err 98 }) 99 return nil 100 } 101 return err 102 }) 103 } 104 105 // Wait waits for Go to complete and returns the first error encountered. 106 func (s *skipAwareGroup) Wait() error { 107 // if we got a "real error", return it, otherwise return skipErr or nil. 108 if err := s.g.Wait(); err != nil { 109 return err 110 } 111 return s.skipErr 112 }