github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/syncutil/singleflight/singleflight.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 // Copyright 2013 The Go Authors. All rights reserved. 12 // Use of this source code is governed by a BSD-style 13 // license that can be found in licenses/BSD-golang.txt. 14 15 // This code originated in Go's internal/singleflight package. 16 17 // Package singleflight provides a duplicate function call suppression 18 // mechanism. 19 package singleflight 20 21 import ( 22 "sync" 23 24 "github.com/cockroachdb/cockroach/pkg/util/syncutil" 25 ) 26 27 // call is an in-flight or completed singleflight.Do call 28 type call struct { 29 wg sync.WaitGroup 30 31 // These fields are written once before the WaitGroup is done 32 // and are only read after the WaitGroup is done. 33 val interface{} 34 err error 35 36 // These fields are read and written with the singleflight 37 // mutex held before the WaitGroup is done, and are read but 38 // not written after the WaitGroup is done. 39 dups int 40 chans []chan<- Result 41 } 42 43 // Group represents a class of work and forms a namespace in 44 // which units of work can be executed with duplicate suppression. 45 type Group struct { 46 mu syncutil.Mutex // protects m 47 m map[string]*call // lazily initialized 48 } 49 50 // Result holds the results of Do, so they can be passed 51 // on a channel. 52 type Result struct { 53 Val interface{} 54 Err error 55 Shared bool 56 } 57 58 // Do executes and returns the results of the given function, making 59 // sure that only one execution is in-flight for a given key at a 60 // time. If a duplicate comes in, the duplicate caller waits for the 61 // original to complete and receives the same results. 62 // The return value shared indicates whether v was given to multiple callers. 63 func (g *Group) Do( 64 key string, fn func() (interface{}, error), 65 ) (v interface{}, shared bool, err error) { 66 g.mu.Lock() 67 if g.m == nil { 68 g.m = make(map[string]*call) 69 } 70 if c, ok := g.m[key]; ok { 71 c.dups++ 72 g.mu.Unlock() 73 c.wg.Wait() 74 return c.val, true, c.err 75 } 76 c := new(call) 77 c.wg.Add(1) 78 g.m[key] = c 79 g.mu.Unlock() 80 81 g.doCall(c, key, fn) 82 return c.val, c.dups > 0, c.err 83 } 84 85 // DoChan is like Do but returns a channel that will receive the results when 86 // they are ready. The method also returns a boolean specifying whether the 87 // caller's fn function will be called or not. This return value lets callers 88 // identify a unique "leader" for a flight. 89 // 90 // NOTE: DoChan makes it possible to initiate or join a flight while holding a 91 // lock without holding it for the duration of the flight. A common usage 92 // pattern is: 93 // 1. Check some datastructure to see if it contains the value you're looking 94 // for. 95 // 2. If it doesn't, initiate or join a flight to produce it. 96 // 97 // Step one is expected to be done while holding a lock. Modifying the 98 // datastructure in the callback is expected to need to take the same lock. Once 99 // a caller proceeds to step two, it likely wants to keep the lock until 100 // DoChan() returned a channel, in order to ensure that a flight is only started 101 // before any modifications to the datastructure occurred (relative to the state 102 // observed in step one). Were the lock to be released before calling DoChan(), 103 // a previous flight might modify the datastructure before our flight began. 104 func (g *Group) DoChan(key string, fn func() (interface{}, error)) (<-chan Result, bool) { 105 ch := make(chan Result, 1) 106 g.mu.Lock() 107 if g.m == nil { 108 g.m = make(map[string]*call) 109 } 110 if c, ok := g.m[key]; ok { 111 c.dups++ 112 c.chans = append(c.chans, ch) 113 g.mu.Unlock() 114 return ch, false 115 } 116 c := &call{chans: []chan<- Result{ch}} 117 c.wg.Add(1) 118 g.m[key] = c 119 g.mu.Unlock() 120 121 go g.doCall(c, key, fn) 122 123 return ch, true 124 } 125 126 // doCall handles the single call for a key. 127 func (g *Group) doCall(c *call, key string, fn func() (interface{}, error)) { 128 c.val, c.err = fn() 129 c.wg.Done() 130 131 g.mu.Lock() 132 delete(g.m, key) 133 for _, ch := range c.chans { 134 ch <- Result{c.val, c.err, c.dups > 0} 135 } 136 g.mu.Unlock() 137 } 138 139 var _ = (*Group).Forget 140 141 // Forget tells the singleflight to forget about a key. Future calls 142 // to Do for this key will call the function rather than waiting for 143 // an earlier call to complete. 144 func (g *Group) Forget(key string) { 145 g.mu.Lock() 146 delete(g.m, key) 147 g.mu.Unlock() 148 } 149 150 // NumCalls returns the number of in-flight calls for a given key. 151 func (g *Group) NumCalls(key string) int { 152 g.mu.Lock() 153 defer g.mu.Unlock() 154 if c, ok := g.m[key]; ok { 155 return c.dups + 1 156 } 157 return 0 158 }