blob: 8997841f62ffa5ce7f00dbd8ab3527dca2825a1c [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// atonce is essentially like the singleflight package from groupcache, but global
// and with a simpler interface.
package atonce
import (
"sync"
)
type job struct {
sync.WaitGroup
err error
}
type jobkey struct {
group string
key string
}
var (
mu sync.Mutex
jobs = make(map[jobkey]*job)
)
// Do will perform f concurrently at most once for all overlapping calls with
// the same group & key. All concurrent callers receive the same result. Callers
// should be careful not to let external actors take full control over groups &
// keys, in order to avoid externally influenced, but invalid collisions.
// Typically a "group" is a string unique across the program representing a
// particular task that may occur concurrently, and key is a value unique to
// that particular job, e.g. "updateMetadata", "example.org"
func Do(group, key string, f func() error) error {
mu.Lock()
jk := jobkey{group, key}
if j, found := jobs[jk]; found {
mu.Unlock()
j.Wait()
return j.err
}
var j job
j.Add(1)
jobs[jk] = &j
mu.Unlock()
j.err = f()
j.Done()
mu.Lock()
delete(jobs, jk)
mu.Unlock()
return j.err
}