github.com/sandwich-go/boost@v1.3.29/singleflight/single.go (about)

     1  // Package singleflight provides a duplicate function call suppression
     2  // mechanism.
     3  package singleflight
     4  
     5  import (
     6  	"github.com/sandwich-go/boost/xerror"
     7  	"github.com/sandwich-go/boost/xpanic"
     8  	"sync"
     9  )
    10  
    11  // call is an in-flight or completed Do call
    12  type call struct {
    13  	wg  sync.WaitGroup
    14  	val interface{}
    15  	err error
    16  }
    17  
    18  // New create a group
    19  func New() *Group { return &Group{} }
    20  
    21  // Group represents a class of work and forms a namespace in which
    22  // units of work can be executed with duplicate suppression.
    23  type Group struct {
    24  	mu sync.Mutex       // protects m
    25  	m  map[string]*call // lazily initialized
    26  }
    27  
    28  // Do executes and returns the results of the given function, making
    29  // sure that only one execution is in-flight for a given key at a
    30  // time. If a duplicate comes in, the duplicate caller waits for the
    31  // original to complete and receives the same results.
    32  func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
    33  	g.mu.Lock()
    34  	if g.m == nil {
    35  		g.m = make(map[string]*call)
    36  	}
    37  	if c, ok := g.m[key]; ok {
    38  		g.mu.Unlock()
    39  		c.wg.Wait()
    40  		return c.val, c.err
    41  	}
    42  	c := new(call)
    43  	c.wg.Add(1)
    44  	g.m[key] = c
    45  	g.mu.Unlock()
    46  
    47  	xpanic.Do(func() {
    48  		c.val, c.err = fn()
    49  	}, func(p *xpanic.Panic) {
    50  		c.err = xerror.NewText("panic with: %v", p)
    51  	})
    52  
    53  	c.wg.Done()
    54  
    55  	g.mu.Lock()
    56  	delete(g.m, key)
    57  	g.mu.Unlock()
    58  
    59  	return c.val, c.err
    60  }