// 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 // 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 any 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 any 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 ( *Group) ( string, func() (any, error)) ( any, error, bool) { .mu.Lock() if .m == nil { .m = make(map[string]*call) } if , := .m[]; { .dups++ .mu.Unlock() .wg.Wait() return .val, .err, true } := new(call) .wg.Add(1) .m[] = .mu.Unlock() .doCall(, , ) return .val, .err, .dups > 0 } // DoChan is like Do but returns a channel that will receive the // results when they are ready. func ( *Group) ( string, func() (any, error)) <-chan Result { := make(chan Result, 1) .mu.Lock() if .m == nil { .m = make(map[string]*call) } if , := .m[]; { .dups++ .chans = append(.chans, ) .mu.Unlock() return } := &call{chans: []chan<- Result{}} .wg.Add(1) .m[] = .mu.Unlock() go .doCall(, , ) return } // doCall handles the single call for a key. func ( *Group) ( *call, string, func() (any, error)) { .val, .err = () .mu.Lock() .wg.Done() if .m[] == { delete(.m, ) } for , := range .chans { <- Result{.val, .err, .dups > 0} } .mu.Unlock() } // ForgetUnshared tells the singleflight to forget about a key if it is not // shared with any other goroutines. Future calls to Do for a forgotten key // will call the function rather than waiting for an earlier call to complete. // Returns whether the key was forgotten or unknown--that is, whether no // other goroutines are waiting for the result. func ( *Group) ( string) bool { .mu.Lock() defer .mu.Unlock() , := .m[] if ! { return true } if .dups == 0 { delete(.m, ) return true } return false }