github.com/Cloud-Foundations/Dominator@v0.3.4/lib/concurrent/impl.go (about) 1 package concurrent 2 3 import ( 4 "runtime" 5 ) 6 7 func newState(numConcurrent uint, p putter) *State { 8 state := &State{errorChannel: make(chan error), putter: p} 9 if numConcurrent > 0 { 10 state.semaphore = make(chan struct{}, numConcurrent) 11 } else { 12 state.semaphore = make(chan struct{}, runtime.NumCPU()) 13 } 14 return state 15 } 16 17 func (*nilPutter) put() { 18 } 19 20 func (state *State) goRun(doFunc func() error) error { 21 if state.entered { 22 panic("GoRun is not re-entrant safe") 23 } 24 if state.error != nil { 25 return state.error 26 } 27 if state.reaped { 28 panic("state has been reaped") 29 } 30 state.entered = true 31 defer func() { state.entered = false }() 32 for { 33 select { 34 case err := <-state.errorChannel: 35 state.pending-- 36 if err != nil { 37 state.error = err 38 state.reap() 39 return err 40 } 41 case state.semaphore <- struct{}{}: 42 state.pending++ 43 go func() { 44 state.errorChannel <- doFunc() 45 <-state.semaphore 46 state.putter.put() 47 }() 48 return nil 49 } 50 } 51 } 52 53 func (state *State) reap() error { 54 if state.reaped { 55 return state.error 56 } 57 state.reaped = true 58 close(state.semaphore) 59 err := state.error 60 for ; state.pending > 0; state.pending-- { 61 if e := <-state.errorChannel; err == nil && e != nil { 62 err = e 63 } 64 } 65 close(state.errorChannel) 66 return err 67 }