github.com/kelleygo/clashcore@v1.0.2/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 }