github.com/cilium/statedb@v0.3.2/internal/sortable_mutex.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package internal
     5  
     6  import (
     7  	"sort"
     8  	"sync"
     9  	"sync/atomic"
    10  	"time"
    11  )
    12  
    13  // sortableMutexSeq is a global sequence counter for the creation of new
    14  // SortableMutex's with unique sequence numbers.
    15  var sortableMutexSeq atomic.Uint64
    16  
    17  // sortableMutex implements SortableMutex. Not exported as the only way to
    18  // initialize it is via NewSortableMutex().
    19  type sortableMutex struct {
    20  	sync.Mutex
    21  	seq             uint64
    22  	acquireDuration time.Duration
    23  }
    24  
    25  func (s *sortableMutex) Lock() {
    26  	start := time.Now()
    27  	s.Mutex.Lock()
    28  	s.acquireDuration = time.Since(start)
    29  }
    30  
    31  func (s *sortableMutex) Seq() uint64 { return s.seq }
    32  
    33  func (s *sortableMutex) AcquireDuration() time.Duration { return s.acquireDuration }
    34  
    35  // SortableMutex provides a Mutex that can be globally sorted with other
    36  // sortable mutexes. This allows deadlock-safe locking of a set of mutexes
    37  // as it guarantees consistent lock ordering.
    38  type SortableMutex interface {
    39  	sync.Locker
    40  	Seq() uint64
    41  	AcquireDuration() time.Duration // The amount of time it took to acquire the lock
    42  }
    43  
    44  // SortableMutexes is a set of mutexes that can be locked in a safe order.
    45  // Once Lock() is called it must not be mutated!
    46  type SortableMutexes []SortableMutex
    47  
    48  // Len implements sort.Interface.
    49  func (s SortableMutexes) Len() int {
    50  	return len(s)
    51  }
    52  
    53  // Less implements sort.Interface.
    54  func (s SortableMutexes) Less(i int, j int) bool {
    55  	return s[i].Seq() < s[j].Seq()
    56  }
    57  
    58  // Swap implements sort.Interface.
    59  func (s SortableMutexes) Swap(i int, j int) {
    60  	s[i], s[j] = s[j], s[i]
    61  }
    62  
    63  // Lock sorts the mutexes, and then locks them in order. If any lock cannot be acquired,
    64  // this will block while holding the locks with a lower sequence number.
    65  func (s SortableMutexes) Lock() {
    66  	sort.Sort(s)
    67  	for _, mu := range s {
    68  		mu.Lock()
    69  	}
    70  }
    71  
    72  // Unlock locks the sorted set of mutexes locked by prior call to Lock().
    73  func (s SortableMutexes) Unlock() {
    74  	for _, mu := range s {
    75  		mu.Unlock()
    76  	}
    77  }
    78  
    79  var _ sort.Interface = SortableMutexes{}
    80  
    81  func NewSortableMutex() SortableMutex {
    82  	seq := sortableMutexSeq.Add(1)
    83  	return &sortableMutex{
    84  		seq: seq,
    85  	}
    86  }