1 // Copyright 2019 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.
5 // Package memoize supports memoizing the return values of functions with
6 // idempotent results that are expensive to compute.
8 // To use this package, build a store and use it to acquire handles with the
21 "golang.org/x/tools/internal/xcontext"
25 panicOnDestroyed = flag.Bool("memoize_panic_on_destroyed", false,
26 "Panic when a destroyed generation is read rather than returning an error. "+
27 "Panicking may make it easier to debug lifetime errors, especially when "+
28 "used with GOTRACEBACK=crash to see all running goroutines.")
31 // Store binds keys to functions, returning handles that can be used to access
32 // the functions results.
35 // handles is the set of values stored.
36 handles map[interface{}]*Handle
38 // generations is the set of generations live in this store.
39 generations map[*Generation]struct{}
42 // Generation creates a new Generation associated with s. Destroy must be
43 // called on the returned Generation once it is no longer in use. name is
44 // for debugging purposes only.
45 func (s *Store) Generation(name string) *Generation {
49 s.handles = map[interface{}]*Handle{}
50 s.generations = map[*Generation]struct{}{}
52 g := &Generation{store: s, name: name}
53 s.generations[g] = struct{}{}
57 // A Generation is a logical point in time of the cache life-cycle. Cache
58 // entries associated with a Generation will not be removed until the
59 // Generation is destroyed.
60 type Generation struct {
61 // destroyed is 1 after the generation is destroyed. Atomic.
65 // wg tracks the reference count of this generation.
69 // Destroy waits for all operations referencing g to complete, then removes
70 // all references to g from cache entries. Cache entries that no longer
71 // reference any non-destroyed generation are removed. Destroy must be called
72 // exactly once for each generation.
73 func (g *Generation) Destroy() {
75 atomic.StoreUint32(&g.destroyed, 1)
77 defer g.store.mu.Unlock()
78 for k, e := range g.store.handles {
80 if _, ok := e.generations[g]; ok {
81 delete(e.generations, g) // delete even if it's dead, in case of dangling references to the entry.
82 if len(e.generations) == 0 {
83 delete(g.store.handles, k)
84 e.state = stateDestroyed
89 delete(g.store.generations, g)
92 // Acquire creates a new reference to g, and returns a func to release that
94 func (g *Generation) Acquire(ctx context.Context) func() {
95 destroyed := atomic.LoadUint32(&g.destroyed)
100 panic("acquire on destroyed generation " + g.name)
106 // Arg is a marker interface that can be embedded to indicate a type is
107 // intended for use as a Function argument.
108 type Arg interface{ memoizeArg() }
110 // Function is the type for functions that can be memoized.
111 // The result must be a pointer.
112 type Function func(ctx context.Context, arg Arg) interface{}
123 // Handle is returned from a store when a key is bound to a function.
124 // It is then used to access the results of that function.
126 // A Handle starts out in idle state, waiting for something to demand its
127 // evaluation. It then transitions into running state. While it's running,
128 // waiters tracks the number of Get calls waiting for a result, and the done
129 // channel is used to notify waiters of the next state transition. Once the
130 // evaluation finishes, value is set, state changes to completed, and done
131 // is closed, unblocking waiters. Alternatively, as Get calls are cancelled,
132 // they decrement waiters. If it drops to zero, the inner context is cancelled,
133 // computation is abandoned, and state resets to idle to start the process over
139 // generations is the set of generations in which this handle is valid.
140 generations map[*Generation]struct{}
143 // done is set in running state, and closed when exiting it.
145 // cancel is set in running state. It cancels computation.
146 cancel context.CancelFunc
147 // waiters is the number of Gets outstanding.
149 // the function that will be used to populate the value
151 // value is set in completed state.
155 // Bind returns a handle for the given key and function.
157 // Each call to bind will return the same handle if it is already bound.
158 // Bind will always return a valid handle, creating one if needed.
159 // Each key can only have one handle at any given time.
160 // The value will be held at least until the associated generation is destroyed.
161 // Bind does not cause the value to be generated.
162 func (g *Generation) Bind(key interface{}, function Function) *Handle {
163 // panic early if the function is nil
164 // it would panic later anyway, but in a way that was much harder to debug
166 panic("the function passed to bind must not be nil")
168 if atomic.LoadUint32(&g.destroyed) != 0 {
169 panic("operation on destroyed generation " + g.name)
172 defer g.store.mu.Unlock()
173 h, ok := g.store.handles[key]
178 generations: map[*Generation]struct{}{g: {}},
180 g.store.handles[key] = h
185 if _, ok := h.generations[g]; !ok {
186 h.generations[g] = struct{}{}
191 // Stats returns the number of each type of value in the store.
192 func (s *Store) Stats() map[reflect.Type]int {
196 result := map[reflect.Type]int{}
197 for k := range s.handles {
198 result[reflect.TypeOf(k)]++
203 // DebugOnlyIterate iterates through all live cache entries and calls f on them.
204 // It should only be used for debugging purposes.
205 func (s *Store) DebugOnlyIterate(f func(k, v interface{})) {
209 for k, e := range s.handles {
212 if e.state == stateCompleted {
223 func (g *Generation) Inherit(h *Handle) {
224 if atomic.LoadUint32(&g.destroyed) != 0 {
225 panic("inherit on destroyed generation " + g.name)
230 if h.state == stateDestroyed {
231 panic(fmt.Sprintf("inheriting destroyed handle %#v (type %T) into generation %v", h.key, h.key, g.name))
233 h.generations[g] = struct{}{}
236 // Cached returns the value associated with a handle.
238 // It will never cause the value to be generated.
239 // It will return the cached value, if present.
240 func (h *Handle) Cached(g *Generation) interface{} {
243 if _, ok := h.generations[g]; !ok {
246 if h.state == stateCompleted {
252 // Get returns the value associated with a handle.
254 // If the value is not yet ready, the underlying function will be invoked.
255 // If ctx is cancelled, Get returns nil.
256 func (h *Handle) Get(ctx context.Context, g *Generation, arg Arg) (interface{}, error) {
257 release := g.Acquire(ctx)
260 if ctx.Err() != nil {
261 return nil, ctx.Err()
264 if _, ok := h.generations[g]; !ok {
267 err := fmt.Errorf("reading key %#v: generation %v is not known", h.key, g.name)
268 if *panicOnDestroyed && ctx.Err() != nil {
275 return h.run(ctx, g, arg)
283 err := fmt.Errorf("Get on destroyed entry %#v (type %T) in generation %v", h.key, h.key, g.name)
284 if *panicOnDestroyed {
289 panic("unknown state")
293 // run starts h.function and returns the result. h.mu must be locked.
294 func (h *Handle) run(ctx context.Context, g *Generation, arg Arg) (interface{}, error) {
295 childCtx, cancel := context.WithCancel(xcontext.Detach(ctx))
297 h.state = stateRunning
298 h.done = make(chan struct{})
299 function := h.function // Read under the lock
301 // Make sure that the generation isn't destroyed while we're running in it.
302 release := g.Acquire(ctx)
305 // Just in case the function does something expensive without checking
306 // the context, double-check we're still alive.
307 if childCtx.Err() != nil {
310 v := function(childCtx, arg)
311 if childCtx.Err() != nil {
317 // It's theoretically possible that the handle has been cancelled out
318 // of the run that started us, and then started running again since we
319 // checked childCtx above. Even so, that should be harmless, since each
320 // run should produce the same results.
321 if h.state != stateRunning {
326 h.state = stateCompleted
333 // wait waits for the value to be computed, or ctx to be cancelled. h.mu must be locked.
334 func (h *Handle) wait(ctx context.Context) (interface{}, error) {
343 if h.state == stateCompleted {
351 if h.waiters == 0 && h.state == stateRunning {
358 return nil, ctx.Err()