golang / groupcache

groupcache is a caching and cache-filling library, intended as a replacement for memcached in many cases.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

concurrency problem in singleflight

jtuki opened this issue · comments

commented

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.

commented

Found similar issue:
#38

The problem does not exist. Because c.wg.Wait() just skip through when the wait group is zero.