github.com/haraldrudell/parl@v0.4.176/go-result-struct.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package parl 7 8 import ( 9 "math" 10 "sync/atomic" 11 12 "github.com/haraldrudell/parl/perrors" 13 ) 14 15 // when when GoResult.g is has multiple values 16 type goResultStruct struct { 17 goResultChan 18 remaining atomic.Uint64 19 isError atomic.Bool 20 } 21 22 // NewGoResult2 also has isError 23 func newGoResultStruct(ch goResultChan) (goResult *goResultStruct) { 24 g := goResultStruct{goResultChan: ch} 25 g.remaining.Store(uint64(cap(ch))) 26 return &g 27 } 28 29 // ReceiveError is a deferrable function receiving error values from goroutines 30 // - n is number of goroutines to wait for, default 1 31 // - errp may be nil 32 // - ReceiveError makes a goroutine: 33 // - — awaitable and 34 // - — able to return a fatal error 35 // - — other needs of a goroutine is to initiate and detect cancel and 36 // submit non-fatal errors 37 // - GoRoutine should have enough capacity for all its goroutines 38 // - — this prevents goroutines from blocking in channel send 39 // - ReceiveError only panics from structural coding problems 40 // - deferrable thread-safe 41 func (g *goResultStruct) ReceiveError(errp *error, n ...int) (err error) { 42 var remainingErrors int 43 if len(n) > 0 { 44 remainingErrors = n[0] 45 } else { 46 remainingErrors = int(g.remaining.Load()) 47 } 48 49 // await goroutine results 50 for ; remainingErrors > 0; remainingErrors-- { 51 52 // blocks here 53 // - wait for a result from a goroutine 54 var e = <-g.goResultChan 55 if g.remaining.Add(math.MaxUint64) == 0 { 56 break // end of configured errors 57 } else if e == nil { 58 continue // good return: ignore 59 } 60 61 // goroutine exited with error 62 if !g.isError.Load() { 63 g.isError.Store(true) 64 } 65 // ensure e has stack 66 e = perrors.Stack(e) 67 // build error list 68 err = perrors.AppendError(err, e) 69 } 70 71 // final action: update errp if present 72 if err != nil && errp != nil { 73 *errp = perrors.AppendError(*errp, err) 74 } 75 76 return 77 } 78 79 func (g *goResultStruct) SetIsError() { 80 if g.isError.Load() { 81 return 82 } 83 g.isError.Store(true) 84 } 85 86 // IsError returns if any goroutine has returned an error 87 func (g *goResultStruct) IsError() (isError bool) { return g.isError.Load() } 88 89 // Remaining returns the number of goroutines that have yet to exit 90 func (g *goResultStruct) Remaining() (remaining int) { return int(g.remaining.Load()) }