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  }