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  }