1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 |
- package syncx
- import "sync"
- type (
- // LockedCalls makes sure the calls with the same key to be called sequentially.
- // For example, A called F, before it's done, B called F, then B's call would not blocked,
- // after A's call finished, B's call got executed.
- // The calls with the same key are independent, not sharing the returned values.
- // A ------->calls F with key and executes<------->returns
- // B ------------------>calls F with key<--------->executes<---->returns
- LockedCalls interface {
- Do(key string, fn func() (interface{}, error)) (interface{}, error)
- }
- lockedGroup struct {
- mu sync.Mutex
- m map[string]*sync.WaitGroup
- }
- )
- func NewLockedCalls() LockedCalls {
- return &lockedGroup{
- m: make(map[string]*sync.WaitGroup),
- }
- }
- func (lg *lockedGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
- begin:
- lg.mu.Lock()
- if wg, ok := lg.m[key]; ok {
- lg.mu.Unlock()
- wg.Wait()
- goto begin
- }
- return lg.makeCall(key, fn)
- }
- func (lg *lockedGroup) makeCall(key string, fn func() (interface{}, error)) (interface{}, error) {
- var wg sync.WaitGroup
- wg.Add(1)
- lg.m[key] = &wg
- lg.mu.Unlock()
- defer func() {
- // delete key first, done later. can't reverse the order, because if reverse,
- // another Do call might wg.Wait() without get notified with wg.Done()
- lg.mu.Lock()
- delete(lg.m, key)
- lg.mu.Unlock()
- wg.Done()
- }()
- return fn()
- }
|