github.com/metacubex/mihomo@v1.18.5/common/singledo/singledo.go (about)

     1  package singledo
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  )
     7  
     8  type call[T any] struct {
     9  	wg  sync.WaitGroup
    10  	val T
    11  	err error
    12  }
    13  
    14  type Single[T any] struct {
    15  	mux    sync.Mutex
    16  	last   time.Time
    17  	wait   time.Duration
    18  	call   *call[T]
    19  	result *Result[T]
    20  }
    21  
    22  type Result[T any] struct {
    23  	Val T
    24  	Err error
    25  }
    26  
    27  // Do single.Do likes sync.singleFlight
    28  func (s *Single[T]) Do(fn func() (T, error)) (v T, err error, shared bool) {
    29  	s.mux.Lock()
    30  	now := time.Now()
    31  	if now.Before(s.last.Add(s.wait)) {
    32  		s.mux.Unlock()
    33  		return s.result.Val, s.result.Err, true
    34  	}
    35  
    36  	if callM := s.call; callM != nil {
    37  		s.mux.Unlock()
    38  		callM.wg.Wait()
    39  		return callM.val, callM.err, true
    40  	}
    41  
    42  	callM := &call[T]{}
    43  	callM.wg.Add(1)
    44  	s.call = callM
    45  	s.mux.Unlock()
    46  	callM.val, callM.err = fn()
    47  	callM.wg.Done()
    48  
    49  	s.mux.Lock()
    50  	s.call = nil
    51  	s.result = &Result[T]{callM.val, callM.err}
    52  	s.last = now
    53  	s.mux.Unlock()
    54  	return callM.val, callM.err, false
    55  }
    56  
    57  func (s *Single[T]) Reset() {
    58  	s.last = time.Time{}
    59  }
    60  
    61  func NewSingle[T any](wait time.Duration) *Single[T] {
    62  	return &Single[T]{wait: wait}
    63  }