github.com/quay/claircore@v1.5.28/updater/locallocker.go (about) 1 package updater 2 3 import ( 4 "context" 5 "sync" 6 ) 7 8 // LocalLocker provides locks backed by local concurrency primitives. 9 type localLocker struct { 10 sync.Mutex 11 wait *sync.Cond 12 m map[string]struct{} 13 } 14 15 var _ Locker = (*localLocker)(nil) 16 17 // NewLocalLocker initializes a localLocker. 18 func newLocalLocker() *localLocker { 19 l := &localLocker{ 20 m: make(map[string]struct{}), 21 } 22 l.wait = sync.NewCond(&l.Mutex) 23 return l 24 } 25 26 // BUG(hank) The localLocker implementation does not respect the parent 27 // context cancellation when waiting for a lock. 28 29 // Lock implements Locker. 30 func (s *localLocker) Lock(ctx context.Context, key string) (context.Context, context.CancelFunc) { 31 s.Mutex.Lock() 32 defer s.Mutex.Unlock() 33 for _, exists := s.m[key]; exists; _, exists = s.m[key] { 34 s.wait.Wait() 35 } 36 s.m[key] = struct{}{} 37 c, f := context.WithCancel(ctx) 38 return c, s.cancelfunc(key, f) 39 } 40 41 // TryLock implements Locker. 42 func (s *localLocker) TryLock(ctx context.Context, key string) (context.Context, context.CancelFunc) { 43 c, f := context.WithCancel(ctx) 44 s.Mutex.Lock() 45 defer s.Mutex.Unlock() 46 if _, exists := s.m[key]; exists { 47 f() 48 return c, f 49 } 50 s.m[key] = struct{}{} 51 return c, s.cancelfunc(key, f) 52 } 53 54 // Cancelfunc returns a CancelFunc that calls "next" and then unlocks. 55 func (s *localLocker) cancelfunc(key string, next context.CancelFunc) context.CancelFunc { 56 return func() { 57 next() 58 s.Mutex.Lock() 59 defer s.Mutex.Unlock() 60 delete(s.m, key) 61 s.wait.Broadcast() 62 } 63 }