github.com/xxf098/lite-proxy@v0.15.1-0.20230422081941-12c69f323218/common/picker/picker.go (about)

     1  package picker
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"time"
     7  )
     8  
     9  // Picker provides synchronization, and Context cancelation
    10  // for groups of goroutines working on subtasks of a common task.
    11  // Inspired by errGroup
    12  type Picker struct {
    13  	ctx    context.Context
    14  	cancel func()
    15  
    16  	wg sync.WaitGroup
    17  
    18  	once    sync.Once
    19  	errOnce sync.Once
    20  	result  interface{}
    21  	err     error
    22  }
    23  
    24  func newPicker(ctx context.Context, cancel func()) *Picker {
    25  	return &Picker{
    26  		ctx:    ctx,
    27  		cancel: cancel,
    28  	}
    29  }
    30  
    31  // WithContext returns a new Picker and an associated Context derived from ctx.
    32  // and cancel when first element return.
    33  func WithContext(ctx context.Context) (*Picker, context.Context) {
    34  	ctx, cancel := context.WithCancel(ctx)
    35  	return newPicker(ctx, cancel), ctx
    36  }
    37  
    38  // WithTimeout returns a new Picker and an associated Context derived from ctx with timeout.
    39  func WithTimeout(ctx context.Context, timeout time.Duration) (*Picker, context.Context) {
    40  	ctx, cancel := context.WithTimeout(ctx, timeout)
    41  	return newPicker(ctx, cancel), ctx
    42  }
    43  
    44  // Wait blocks until all function calls from the Go method have returned,
    45  // then returns the first nil error result (if any) from them.
    46  func (p *Picker) Wait() interface{} {
    47  	p.wg.Wait()
    48  	if p.cancel != nil {
    49  		p.cancel()
    50  	}
    51  	return p.result
    52  }
    53  
    54  // Error return the first error (if all success return nil)
    55  func (p *Picker) Error() error {
    56  	return p.err
    57  }
    58  
    59  // Go calls the given function in a new goroutine.
    60  // The first call to return a nil error cancels the group; its result will be returned by Wait.
    61  func (p *Picker) Go(f func() (interface{}, error)) {
    62  	p.wg.Add(1)
    63  
    64  	go func() {
    65  		defer p.wg.Done()
    66  
    67  		if ret, err := f(); err == nil {
    68  			p.once.Do(func() {
    69  				p.result = ret
    70  				if p.cancel != nil {
    71  					p.cancel()
    72  				}
    73  			})
    74  		} else {
    75  			p.errOnce.Do(func() {
    76  				p.err = err
    77  			})
    78  		}
    79  	}()
    80  }