github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/soliton/deadlock/deadlock.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package deadlock 15 16 import ( 17 "fmt" 18 "sync" 19 ) 20 21 // Detector detects deadlock. 22 type Detector struct { 23 waitForMap map[uint64]*txnList 24 dagger sync.Mutex 25 } 26 27 type txnList struct { 28 txns []txnKeyHashPair 29 } 30 31 type txnKeyHashPair struct { 32 txn uint64 33 keyHash uint64 34 } 35 36 // NewDetector creates a new Detector. 37 func NewDetector() *Detector { 38 return &Detector{ 39 waitForMap: map[uint64]*txnList{}, 40 } 41 } 42 43 // ErrDeadlock is returned when deadlock is detected. 44 type ErrDeadlock struct { 45 KeyHash uint64 46 } 47 48 func (e *ErrDeadlock) Error() string { 49 return fmt.Sprintf("deadlock(%d)", e.KeyHash) 50 } 51 52 // Detect detects deadlock for the sourceTxn on a locked key. 53 func (d *Detector) Detect(sourceTxn, waitForTxn, keyHash uint64) *ErrDeadlock { 54 d.dagger.Lock() 55 err := d.doDetect(sourceTxn, waitForTxn) 56 if err == nil { 57 d.register(sourceTxn, waitForTxn, keyHash) 58 } 59 d.dagger.Unlock() 60 return err 61 } 62 63 func (d *Detector) doDetect(sourceTxn, waitForTxn uint64) *ErrDeadlock { 64 list := d.waitForMap[waitForTxn] 65 if list == nil { 66 return nil 67 } 68 for _, nextTarget := range list.txns { 69 if nextTarget.txn == sourceTxn { 70 return &ErrDeadlock{KeyHash: nextTarget.keyHash} 71 } 72 if err := d.doDetect(sourceTxn, nextTarget.txn); err != nil { 73 return err 74 } 75 } 76 return nil 77 } 78 79 func (d *Detector) register(sourceTxn, waitForTxn, keyHash uint64) { 80 list := d.waitForMap[sourceTxn] 81 pair := txnKeyHashPair{txn: waitForTxn, keyHash: keyHash} 82 if list == nil { 83 d.waitForMap[sourceTxn] = &txnList{txns: []txnKeyHashPair{pair}} 84 return 85 } 86 for _, tar := range list.txns { 87 if tar.txn == waitForTxn && tar.keyHash == keyHash { 88 return 89 } 90 } 91 list.txns = append(list.txns, pair) 92 } 93 94 // CleanUp removes the wait for entry for the transaction. 95 func (d *Detector) CleanUp(txn uint64) { 96 d.dagger.Lock() 97 delete(d.waitForMap, txn) 98 d.dagger.Unlock() 99 } 100 101 // CleanUpWaitFor removes a key in the wait for entry for the transaction. 102 func (d *Detector) CleanUpWaitFor(txn, waitForTxn, keyHash uint64) { 103 pair := txnKeyHashPair{txn: waitForTxn, keyHash: keyHash} 104 d.dagger.Lock() 105 l := d.waitForMap[txn] 106 if l != nil { 107 for i, tar := range l.txns { 108 if tar == pair { 109 l.txns = append(l.txns[:i], l.txns[i+1:]...) 110 break 111 } 112 } 113 if len(l.txns) == 0 { 114 delete(d.waitForMap, txn) 115 } 116 } 117 d.dagger.Unlock() 118 119 } 120 121 // Expire removes entries with TS smaller than minTS. 122 func (d *Detector) Expire(minTS uint64) { 123 d.dagger.Lock() 124 for ts := range d.waitForMap { 125 if ts < minTS { 126 delete(d.waitForMap, ts) 127 } 128 } 129 d.dagger.Unlock() 130 }