concurrency problem in singleflight
jtuki opened this issue · comments
I found the code beflow in singleflight.
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
g.mu.Lock()
if g.m == nil {
g.m = make(map[string]*call)
}
if c, ok := g.m[key]; ok {
g.mu.Unlock()
c.wg.Wait()
return c.val, c.err
}
c := new(call)
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()
c.val, c.err = fn()
c.wg.Done()
g.mu.Lock()
delete(g.m, key)
g.mu.Unlock()
return c.val, c.err
}
In most circumstances, the code should perform well and meet concurrency challenges.
However, is there any possibility that c.wg.Wait()
happens before delete(g.m, key)
(so that the key exists, rather deleted) and after c.wg.Done()
? If so, there might be some concurrency problems. To avoid the problem, the c.wg.Done()
should also be protected by the mu
lock.