Skip to content

Instantly share code, notes, and snippets.

@dg3feiko
Created August 12, 2017 05:23
Show Gist options
  • Save dg3feiko/232b9c5352a3334db5eca3821972423c to your computer and use it in GitHub Desktop.
Save dg3feiko/232b9c5352a3334db5eca3821972423c to your computer and use it in GitHub Desktop.

Revisions

  1. dg3feiko created this gist Aug 12, 2017.
    111 changes: 111 additions & 0 deletions singleflight.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,111 @@
    // Copyright 2013 The Go Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.

    // Package singleflight provides a duplicate function call suppression
    // mechanism.
    package singleflight

    import "sync"

    // call is an in-flight or completed singleflight.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

    // These fields are read and written with the singleflight
    // mutex held before the WaitGroup is done, and are read but
    // not written after the WaitGroup is done.
    dups int
    chans []chan<- Result
    }

    // 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[string]*call // lazily initialized
    }

    // Result holds the results of Do, so they can be passed
    // on a channel.
    type Result struct {
    Val interface{}
    Err error
    Shared bool
    }

    // 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.
    // The return value shared indicates whether v was given to multiple callers.
    func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) {
    g.mu.Lock()
    if g.m == nil {
    g.m = make(map[string]*call)
    }
    if c, ok := g.m[key]; ok {
    c.dups++
    g.mu.Unlock()
    c.wg.Wait()
    return c.val, c.err, true
    }
    c := new(call)
    c.wg.Add(1)
    g.m[key] = c
    g.mu.Unlock()

    g.doCall(c, key, fn)
    return c.val, c.err, c.dups > 0
    }

    // DoChan is like Do but returns a channel that will receive the
    // results when they are ready.
    func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result {
    ch := make(chan Result, 1)
    g.mu.Lock()
    if g.m == nil {
    g.m = make(map[string]*call)
    }
    if c, ok := g.m[key]; ok {
    c.dups++
    c.chans = append(c.chans, ch)
    g.mu.Unlock()
    return ch
    }
    c := &call{chans: []chan<- Result{ch}}
    c.wg.Add(1)
    g.m[key] = c
    g.mu.Unlock()

    go g.doCall(c, key, fn)

    return ch
    }

    // doCall handles the single call for a key.
    func (g *Group) doCall(c *call, key string, fn func() (interface{}, error)) {
    c.val, c.err = fn()
    c.wg.Done()

    g.mu.Lock()
    delete(g.m, key)
    for _, ch := range c.chans {
    ch <- Result{c.val, c.err, c.dups > 0}
    }
    g.mu.Unlock()
    }

    // Forget tells the singleflight to forget about a key. Future calls
    // to Do for this key will call the function rather than waiting for
    // an earlier call to complete.
    func (g *Group) Forget(key string) {
    g.mu.Lock()
    delete(g.m, key)
    g.mu.Unlock()
    }