github.com/Asutorufa/yuhaiin@v0.3.6-0.20240502055049-7984da7023a0/pkg/utils/singleflight/singleflight.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package singleflight provides a duplicate function call suppression 6 // mechanism. 7 package singleflight // import "golang.org/x/sync/singleflight" 8 9 import ( 10 "bytes" 11 "errors" 12 "fmt" 13 "runtime" 14 "runtime/debug" 15 "sync" 16 ) 17 18 // errGoexit indicates the runtime.Goexit was called in 19 // the user given function. 20 var errGoexit = errors.New("runtime.Goexit was called") 21 22 // A panicError is an arbitrary value recovered from a panic 23 // with the stack trace during the execution of given function. 24 type panicError struct { 25 value any 26 stack []byte 27 } 28 29 // Error implements error interface. 30 func (p *panicError) Error() string { 31 return fmt.Sprintf("%v\n\n%s", p.value, p.stack) 32 } 33 34 func (p *panicError) Unwrap() error { 35 err, ok := p.value.(error) 36 if !ok { 37 return nil 38 } 39 40 return err 41 } 42 43 func newPanicError(v any) error { 44 stack := debug.Stack() 45 46 // The first line of the stack trace is of the form "goroutine N [status]:" 47 // but by the time the panic reaches Do the goroutine may no longer exist 48 // and its status will have changed. Trim out the misleading line. 49 if line := bytes.IndexByte(stack[:], '\n'); line >= 0 { 50 stack = stack[line+1:] 51 } 52 return &panicError{value: v, stack: stack} 53 } 54 55 // call is an in-flight or completed singleflight.Do call 56 type call[T any] struct { 57 wg sync.WaitGroup 58 59 // These fields are written once before the WaitGroup is done 60 // and are only read after the WaitGroup is done. 61 val T 62 err error 63 64 // These fields are read and written with the singleflight 65 // mutex held before the WaitGroup is done, and are read but 66 // not written after the WaitGroup is done. 67 dups int 68 chans []chan<- Result[T] 69 } 70 71 // Group represents a class of work and forms a namespace in 72 // which units of work can be executed with duplicate suppression. 73 type Group[K comparable, V any] struct { 74 mu sync.Mutex // protects m 75 m map[K]*call[V] // lazily initialized 76 } 77 78 // Result holds the results of Do, so they can be passed 79 // on a channel. 80 type Result[T any] struct { 81 Val T 82 Err error 83 Shared bool 84 } 85 86 // Do executes and returns the results of the given function, making 87 // sure that only one execution is in-flight for a given key at a 88 // time. If a duplicate comes in, the duplicate caller waits for the 89 // original to complete and receives the same results. 90 // The return value shared indicates whether v was given to multiple callers. 91 func (g *Group[K, V]) Do(key K, fn func() (V, error)) (v V, err error, shared bool) { 92 g.mu.Lock() 93 if g.m == nil { 94 g.m = make(map[K]*call[V]) 95 } 96 if c, ok := g.m[key]; ok { 97 c.dups++ 98 g.mu.Unlock() 99 c.wg.Wait() 100 101 if e, ok := c.err.(*panicError); ok { 102 panic(e) 103 } else if c.err == errGoexit { 104 runtime.Goexit() 105 } 106 return c.val, c.err, true 107 } 108 c := new(call[V]) 109 c.wg.Add(1) 110 g.m[key] = c 111 g.mu.Unlock() 112 113 g.doCall(c, key, fn) 114 return c.val, c.err, c.dups > 0 115 } 116 117 // DoChan is like Do but returns a channel that will receive the 118 // results when they are ready. 119 // 120 // The returned channel will not be closed. 121 func (g *Group[K, V]) DoChan(key K, fn func() (V, error)) <-chan Result[V] { 122 ch := make(chan Result[V], 1) 123 g.mu.Lock() 124 if g.m == nil { 125 g.m = make(map[K]*call[V]) 126 } 127 if c, ok := g.m[key]; ok { 128 c.dups++ 129 c.chans = append(c.chans, ch) 130 g.mu.Unlock() 131 return ch 132 } 133 c := &call[V]{chans: []chan<- Result[V]{ch}} 134 c.wg.Add(1) 135 g.m[key] = c 136 g.mu.Unlock() 137 138 go g.doCall(c, key, fn) 139 140 return ch 141 } 142 143 // doCall handles the single call for a key. 144 func (g *Group[K, V]) doCall(c *call[V], key K, fn func() (V, error)) { 145 normalReturn := false 146 recovered := false 147 148 // use double-defer to distinguish panic from runtime.Goexit, 149 // more details see https://golang.org/cl/134395 150 defer func() { 151 // the given function invoked runtime.Goexit 152 if !normalReturn && !recovered { 153 c.err = errGoexit 154 } 155 156 g.mu.Lock() 157 defer g.mu.Unlock() 158 c.wg.Done() 159 if g.m[key] == c { 160 delete(g.m, key) 161 } 162 163 if e, ok := c.err.(*panicError); ok { 164 // In order to prevent the waiting channels from being blocked forever, 165 // needs to ensure that this panic cannot be recovered. 166 if len(c.chans) > 0 { 167 go panic(e) 168 select {} // Keep this goroutine around so that it will appear in the crash dump. 169 } else { 170 panic(e) 171 } 172 } else if c.err == errGoexit { 173 // Already in the process of goexit, no need to call again 174 } else { 175 // Normal return 176 for _, ch := range c.chans { 177 ch <- Result[V]{c.val, c.err, c.dups > 0} 178 } 179 } 180 }() 181 182 func() { 183 defer func() { 184 if !normalReturn { 185 // Ideally, we would wait to take a stack trace until we've determined 186 // whether this is a panic or a runtime.Goexit. 187 // 188 // Unfortunately, the only way we can distinguish the two is to see 189 // whether the recover stopped the goroutine from terminating, and by 190 // the time we know that, the part of the stack trace relevant to the 191 // panic has been discarded. 192 if r := recover(); r != nil { 193 c.err = newPanicError(r) 194 } 195 } 196 }() 197 198 c.val, c.err = fn() 199 normalReturn = true 200 }() 201 202 if !normalReturn { 203 recovered = true 204 } 205 } 206 207 // Forget tells the singleflight to forget about a key. Future calls 208 // to Do for this key will call the function rather than waiting for 209 // an earlier call to complete. 210 func (g *Group[K, V]) Forget(key K) { 211 g.mu.Lock() 212 delete(g.m, key) 213 g.mu.Unlock() 214 }