Last active
November 27, 2018 12:14
-
-
Save shanab/137966675b5977f16afee664df2c5589 to your computer and use it in GitHub Desktop.
A modified version of singleflight API.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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