Guest User

Untitled

a guest
Apr 20th, 2018
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 2.41 KB | None | 0 0
  1. package dnscache
  2.  
  3. import (
  4. "context"
  5. "sync"
  6. )
  7.  
  8. // waiter represents an outstanding computation that will fill a cache entry
  9. type waiter struct {
  10. val interface{}
  11. err error
  12.  
  13. wg sync.WaitGroup
  14. }
  15.  
  16. func (w *waiter) wait(ctx context.Context) (interface{}, error) {
  17. done := make(chan struct{})
  18. go func() {
  19. w.wg.Wait()
  20. close(done)
  21. }()
  22.  
  23. select {
  24. case <-ctx.Done():
  25. return nil, ctx.Err()
  26. case <-done:
  27. return w.val, w.err
  28. }
  29. }
  30.  
  31. func (w *waiter) finish(val interface{}, err error) {
  32. w.val = val
  33. w.err = err
  34. w.wg.Done()
  35. }
  36.  
  37. // FillFunc is a function that computes the value of a cache entry given a
  38. // string key
  39. type FillFunc func(context.Context, string) (interface{}, error)
  40.  
  41. // FillCache is a cache whose entries are calculated and filled on-demand
  42. type FillCache struct {
  43. fillFunc FillFunc
  44.  
  45. cache map[string]interface{}
  46. inflight map[string]*waiter
  47.  
  48. mu sync.Mutex
  49. }
  50.  
  51. // NewFillCache creates a FillCache whose entries will be computed by the given
  52. // FillFunc
  53. func NewFillCache(fillFunc FillFunc) *FillCache {
  54. return &FillCache{
  55. fillFunc: fillFunc,
  56. cache: make(map[string]interface{}),
  57. inflight: make(map[string]*waiter),
  58. }
  59. }
  60.  
  61. // Get returns the cache value for the given key, computing it as necessary
  62. func (c *FillCache) Get(ctx context.Context, key string) (interface{}, error) {
  63. c.mu.Lock()
  64. if val, found := c.cache[key]; found {
  65. c.mu.Unlock()
  66. return val, nil
  67. }
  68.  
  69. if w, waiting := c.inflight[key]; waiting {
  70. c.mu.Unlock()
  71. return w.wait(ctx)
  72. }
  73.  
  74. w := &waiter{}
  75. w.wg.Add(1)
  76. c.inflight[key] = w
  77. c.mu.Unlock()
  78.  
  79. val, err := c.fillFunc(ctx, key)
  80.  
  81. c.mu.Lock()
  82. defer c.mu.Unlock()
  83.  
  84. w.finish(val, err)
  85. delete(c.inflight, key)
  86.  
  87. if err == nil {
  88. c.cache[key] = val
  89. }
  90. return val, err
  91. }
  92.  
  93. // Update recomputes the value for the given key, unless another goroutine is
  94. // already computing the value, and returns a bool indicating whether the value
  95. // was updated
  96. func (c *FillCache) Update(ctx context.Context, key string) (bool, error) {
  97. c.mu.Lock()
  98.  
  99. // Another goroutine is updating this entry, so we don't need to
  100. if _, waiting := c.inflight[key]; waiting {
  101. c.mu.Unlock()
  102. return false, nil
  103. }
  104.  
  105. // Otherwise, we'll update this entry ourselves
  106. w := &waiter{}
  107. w.wg.Add(1)
  108. c.inflight[key] = w
  109. c.mu.Unlock()
  110.  
  111. val, err := c.fillFunc(ctx, key)
  112.  
  113. c.mu.Lock()
  114. defer c.mu.Unlock()
  115.  
  116. w.finish(val, err)
  117. delete(c.inflight, key)
  118.  
  119. if w.err == nil {
  120. c.cache[key] = w.val
  121. return true, nil
  122. }
  123. return false, w.err
  124. }
Add Comment
Please, Sign In to add comment