github.com/april1989/origin-go-tools@v0.0.32/internal/memoize/memoize.go (about)

     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.
     4  
     5  // Package memoize supports memoizing the return values of functions with
     6  // idempotent results that are expensive to compute.
     7  //
     8  // To use this package, build a store and use it to acquire handles with the
     9  // Bind method.
    10  //
    11  package memoize
    12  
    13  import (
    14  	"context"
    15  	"flag"
    16  	"fmt"
    17  	"reflect"
    18  	"sync"
    19  	"sync/atomic"
    20  
    21  	"github.com/april1989/origin-go-tools/internal/xcontext"
    22  )
    23  
    24  var (
    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.")
    29  )
    30  
    31  // Store binds keys to functions, returning handles that can be used to access
    32  // the functions results.
    33  type Store struct {
    34  	mu sync.Mutex
    35  	// handles is the set of values stored.
    36  	handles map[interface{}]*Handle
    37  
    38  	// generations is the set of generations live in this store.
    39  	generations map[*Generation]struct{}
    40  }
    41  
    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 {
    46  	s.mu.Lock()
    47  	defer s.mu.Unlock()
    48  	if s.handles == nil {
    49  		s.handles = map[interface{}]*Handle{}
    50  		s.generations = map[*Generation]struct{}{}
    51  	}
    52  	g := &Generation{store: s, name: name}
    53  	s.generations[g] = struct{}{}
    54  	return g
    55  }
    56  
    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.
    62  	destroyed uint32
    63  	store     *Store
    64  	name      string
    65  	// wg tracks the reference count of this generation.
    66  	wg sync.WaitGroup
    67  }
    68  
    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() {
    74  	g.wg.Wait()
    75  	atomic.StoreUint32(&g.destroyed, 1)
    76  	g.store.mu.Lock()
    77  	defer g.store.mu.Unlock()
    78  	for k, e := range g.store.handles {
    79  		e.mu.Lock()
    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
    85  			}
    86  		}
    87  		e.mu.Unlock()
    88  	}
    89  	delete(g.store.generations, g)
    90  }
    91  
    92  // Acquire creates a new reference to g, and returns a func to release that
    93  // reference.
    94  func (g *Generation) Acquire(ctx context.Context) func() {
    95  	destroyed := atomic.LoadUint32(&g.destroyed)
    96  	if ctx.Err() != nil {
    97  		return func() {}
    98  	}
    99  	if destroyed != 0 {
   100  		panic("acquire on destroyed generation " + g.name)
   101  	}
   102  	g.wg.Add(1)
   103  	return g.wg.Done
   104  }
   105  
   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() }
   109  
   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{}
   113  
   114  type state int
   115  
   116  const (
   117  	stateIdle = iota
   118  	stateRunning
   119  	stateCompleted
   120  	stateDestroyed
   121  )
   122  
   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.
   125  //
   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
   134  // again.
   135  type Handle struct {
   136  	key interface{}
   137  	mu  sync.Mutex
   138  
   139  	// generations is the set of generations in which this handle is valid.
   140  	generations map[*Generation]struct{}
   141  
   142  	state state
   143  	// done is set in running state, and closed when exiting it.
   144  	done chan struct{}
   145  	// cancel is set in running state. It cancels computation.
   146  	cancel context.CancelFunc
   147  	// waiters is the number of Gets outstanding.
   148  	waiters uint
   149  	// the function that will be used to populate the value
   150  	function Function
   151  	// value is set in completed state.
   152  	value interface{}
   153  }
   154  
   155  // Bind returns a handle for the given key and function.
   156  //
   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
   165  	if function == nil {
   166  		panic("the function passed to bind must not be nil")
   167  	}
   168  	if atomic.LoadUint32(&g.destroyed) != 0 {
   169  		panic("operation on destroyed generation " + g.name)
   170  	}
   171  	g.store.mu.Lock()
   172  	defer g.store.mu.Unlock()
   173  	h, ok := g.store.handles[key]
   174  	if !ok {
   175  		h := &Handle{
   176  			key:         key,
   177  			function:    function,
   178  			generations: map[*Generation]struct{}{g: {}},
   179  		}
   180  		g.store.handles[key] = h
   181  		return h
   182  	}
   183  	h.mu.Lock()
   184  	defer h.mu.Unlock()
   185  	if _, ok := h.generations[g]; !ok {
   186  		h.generations[g] = struct{}{}
   187  	}
   188  	return h
   189  }
   190  
   191  // Stats returns the number of each type of value in the store.
   192  func (s *Store) Stats() map[reflect.Type]int {
   193  	s.mu.Lock()
   194  	defer s.mu.Unlock()
   195  
   196  	result := map[reflect.Type]int{}
   197  	for k := range s.handles {
   198  		result[reflect.TypeOf(k)]++
   199  	}
   200  	return result
   201  }
   202  
   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{})) {
   206  	s.mu.Lock()
   207  	defer s.mu.Unlock()
   208  
   209  	for k, e := range s.handles {
   210  		var v interface{}
   211  		e.mu.Lock()
   212  		if e.state == stateCompleted {
   213  			v = e.value
   214  		}
   215  		e.mu.Unlock()
   216  		if v == nil {
   217  			continue
   218  		}
   219  		f(k, v)
   220  	}
   221  }
   222  
   223  func (g *Generation) Inherit(h *Handle) {
   224  	if atomic.LoadUint32(&g.destroyed) != 0 {
   225  		panic("inherit on destroyed generation " + g.name)
   226  	}
   227  
   228  	h.mu.Lock()
   229  	defer h.mu.Unlock()
   230  	if h.state == stateDestroyed {
   231  		panic(fmt.Sprintf("inheriting destroyed handle %#v into generation %v", h.key, g.name))
   232  	}
   233  	h.generations[g] = struct{}{}
   234  }
   235  
   236  // Cached returns the value associated with a handle.
   237  //
   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{} {
   241  	h.mu.Lock()
   242  	defer h.mu.Unlock()
   243  	if _, ok := h.generations[g]; !ok {
   244  		return nil
   245  	}
   246  	if h.state == stateCompleted {
   247  		return h.value
   248  	}
   249  	return nil
   250  }
   251  
   252  // Get returns the value associated with a handle.
   253  //
   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)
   258  	defer release()
   259  
   260  	if ctx.Err() != nil {
   261  		return nil, ctx.Err()
   262  	}
   263  	h.mu.Lock()
   264  	if _, ok := h.generations[g]; !ok {
   265  		h.mu.Unlock()
   266  
   267  		err := fmt.Errorf("reading key %#v: generation %v is not known", h.key, g.name)
   268  		if *panicOnDestroyed && ctx.Err() != nil {
   269  			panic(err)
   270  		}
   271  		return nil, err
   272  	}
   273  	switch h.state {
   274  	case stateIdle:
   275  		return h.run(ctx, g, arg)
   276  	case stateRunning:
   277  		return h.wait(ctx)
   278  	case stateCompleted:
   279  		defer h.mu.Unlock()
   280  		return h.value, nil
   281  	case stateDestroyed:
   282  		h.mu.Unlock()
   283  		err := fmt.Errorf("Get on destroyed entry %#v in generation %v", h.key, g.name)
   284  		if *panicOnDestroyed {
   285  			panic(err)
   286  		}
   287  		return nil, err
   288  	default:
   289  		panic("unknown state")
   290  	}
   291  }
   292  
   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))
   296  	h.cancel = cancel
   297  	h.state = stateRunning
   298  	h.done = make(chan struct{})
   299  	function := h.function // Read under the lock
   300  
   301  	// Make sure that the generation isn't destroyed while we're running in it.
   302  	release := g.Acquire(ctx)
   303  	go func() {
   304  		defer release()
   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 {
   308  			return
   309  		}
   310  		v := function(childCtx, arg)
   311  		if childCtx.Err() != nil {
   312  			return
   313  		}
   314  
   315  		h.mu.Lock()
   316  		defer h.mu.Unlock()
   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 {
   322  			return
   323  		}
   324  		h.value = v
   325  		h.function = nil
   326  		h.state = stateCompleted
   327  		close(h.done)
   328  	}()
   329  
   330  	return h.wait(ctx)
   331  }
   332  
   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) {
   335  	h.waiters++
   336  	done := h.done
   337  	h.mu.Unlock()
   338  
   339  	select {
   340  	case <-done:
   341  		h.mu.Lock()
   342  		defer h.mu.Unlock()
   343  		if h.state == stateCompleted {
   344  			return h.value, nil
   345  		}
   346  		return nil, nil
   347  	case <-ctx.Done():
   348  		h.mu.Lock()
   349  		defer h.mu.Unlock()
   350  		h.waiters--
   351  		if h.waiters == 0 && h.state == stateRunning {
   352  			h.cancel()
   353  			close(h.done)
   354  			h.state = stateIdle
   355  			h.done = nil
   356  			h.cancel = nil
   357  		}
   358  		return nil, ctx.Err()
   359  	}
   360  }