github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/lockset/table.go (about) 1 package lockset 2 3 import ( 4 "fmt" 5 "runtime" 6 "strings" 7 "sync" 8 ) 9 10 // TODO: 11 // * per goroutine table 12 // * across goroutine locks 13 // * replacement for sync package 14 // * reduce overhead 15 // * garbage collection of locks 16 17 type Table struct { 18 mu sync.Mutex 19 20 root Lock 21 holding []*Lock 22 callers []CallFrame 23 } 24 25 type CallFrame struct { 26 entries [4]uintptr 27 count int 28 } 29 30 func (cf *CallFrame) String() string { 31 frames := runtime.CallersFrames(cf.entries[:cf.count]) 32 var builder strings.Builder 33 for { 34 frame, more := frames.Next() 35 fmt.Fprintf(&builder, " %s (%s:%d)\n", frame.Function, frame.File, frame.Line) 36 if !more { 37 break 38 } 39 } 40 return builder.String() 41 } 42 43 type Lock struct { 44 followers Set 45 } 46 47 func NewTable() *Table { 48 table := &Table{} 49 table.holding = append(table.holding, &table.root) 50 table.callers = append(table.callers, CallFrame{}) 51 return table 52 } 53 54 type Inversion struct { 55 Held *Lock 56 HeldCall CallFrame 57 58 Lock *Lock 59 LockCall CallFrame 60 } 61 62 func (inv *Inversion) String() string { 63 return fmt.Sprintf("previously locked:\n%s\nlocking:\n%s", &inv.HeldCall, &inv.LockCall) 64 } 65 66 func (table *Table) Locked(lock *Lock) *Inversion { 67 table.mu.Lock() 68 defer table.mu.Unlock() 69 70 var frame CallFrame 71 frame.count = runtime.Callers(2, frame.entries[:]) 72 73 var inversion *Inversion 74 75 for i, held := range table.holding { 76 if lock.followers.Contains(held) { 77 inversion = &Inversion{ 78 Held: held, 79 HeldCall: table.callers[i], 80 81 Lock: lock, 82 LockCall: frame, 83 } 84 break 85 } 86 held.followers.Include(lock) 87 } 88 89 table.holding = append(table.holding, lock) 90 table.callers = append(table.callers, frame) 91 92 return inversion 93 } 94 95 func (table *Table) Unlocking(lock *Lock) bool { 96 table.mu.Lock() 97 defer table.mu.Unlock() 98 99 for i := len(table.holding) - 1; i >= 0; i-- { 100 if table.holding[i] == lock { 101 table.holding = append(table.holding[:i], table.holding[i+1:]...) 102 table.callers = append(table.callers[:i], table.callers[i+1:]...) 103 return true 104 } 105 } 106 107 return false 108 } 109 110 type Set struct{ list []*Lock } 111 112 func (set *Set) Include(lock *Lock) { 113 if !set.Contains(lock) { 114 set.list = append(set.list, lock) 115 } 116 } 117 118 func (set *Set) Contains(lock *Lock) bool { 119 for _, k := range set.list { 120 if k == lock { 121 return true 122 } 123 } 124 return false 125 } 126 127 func (set *Set) Remove(lock *Lock) bool { 128 for i, k := range set.list { 129 if k == lock { 130 set.list = append(set.list[:i], set.list[i+1:]...) 131 return true 132 } 133 } 134 return false 135 }