Skip to content

Instantly share code, notes, and snippets.

@shanab
Last active November 27, 2018 12:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shanab/137966675b5977f16afee664df2c5589 to your computer and use it in GitHub Desktop.
Save shanab/137966675b5977f16afee664df2c5589 to your computer and use it in GitHub Desktop.
A modified version of singleflight API.
package cache
// call is an in-flight or completed Do call
type call struct {
wg sync.WaitGroup
// These fields are written once before the WaitGroup is done
// and are only read after the WaitGroup is done.
val interface{}
err error
}
// Group represents a class of work and forms a namespace in which
// units of work can be executed with duplicate suppression.
type Group struct {
mu sync.Mutex // protects m
m map[interface{}]*call // lazily initialized
}
/ Do executes and returns the results of the given function, making
// sure that only one execution is in-flight for a given key at a
// time. If a duplicate comes in, the duplicate caller waits for the
// original to complete and receives the same results.
func (g *Group) Do(cacher Cacher, key interface{}, fn func() (interface{}, error)) (interface{}, error) {
g.mu.Lock()
// OPTIMIZATION: Check if the key has been populated already while waiting to obtain the lock
v, err := cacher.get(key)
if err == nil {
g.mu.Unlock()
return v, nil
}
// Lazily initialize m
if g.m == nil {
g.m = make(map[interface{}]*call)
}
// Check if a call is already in-flight for the given key.
// If yes, release the lock and wait for the wait group.
if c, ok := g.m[key]; ok {
g.mu.Unlock()
c.wg.Wait()
return c.val, c.err
}
// No calls are in-flight. Make a new call, increment the wait group and perform the call.
c := new(call)
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()
v, err = g.call(c, key, fn)
return v, err
}
// call handles a single call for a key
func (g *Group) call(c *call, key interface{}, fn func() (interface{}, error)) (interface{}, error) {
c.val, c.err = fn()
c.wg.Done()
g.mu.Lock()
delete(g.m, key)
g.mu.Unlock()
return c.val, c.err
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment