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 }