github.com/KinWaiYuen/client-go/v2@v2.5.4/internal/mockstore/deadlock/deadlock.go (about) 1 // Copyright 2021 TiKV Authors 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // NOTE: The code in this file is based on code from the 16 // TiDB project, licensed under the Apache License v 2.0 17 // 18 // https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/mockstore/deadlock/deadlock.go 19 // 20 21 // Copyright 2019 PingCAP, Inc. 22 // 23 // Licensed under the Apache License, Version 2.0 (the "License"); 24 // you may not use this file except in compliance with the License. 25 // You may obtain a copy of the License at 26 // 27 // http://www.apache.org/licenses/LICENSE-2.0 28 // 29 // Unless required by applicable law or agreed to in writing, software 30 // distributed under the License is distributed on an "AS IS" BASIS, 31 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 // See the License for the specific language governing permissions and 33 // limitations under the License. 34 35 package deadlock 36 37 import ( 38 "fmt" 39 "sync" 40 ) 41 42 // Detector detects deadlock. 43 type Detector struct { 44 waitForMap map[uint64]*txnList 45 lock sync.Mutex 46 } 47 48 type txnList struct { 49 txns []txnKeyHashPair 50 } 51 52 type txnKeyHashPair struct { 53 txn uint64 54 keyHash uint64 55 } 56 57 // NewDetector creates a new Detector. 58 func NewDetector() *Detector { 59 return &Detector{ 60 waitForMap: map[uint64]*txnList{}, 61 } 62 } 63 64 // ErrDeadlock is returned when deadlock is detected. 65 type ErrDeadlock struct { 66 KeyHash uint64 67 } 68 69 func (e *ErrDeadlock) Error() string { 70 return fmt.Sprintf("deadlock(%d)", e.KeyHash) 71 } 72 73 // Detect detects deadlock for the sourceTxn on a locked key. 74 func (d *Detector) Detect(sourceTxn, waitForTxn, keyHash uint64) *ErrDeadlock { 75 d.lock.Lock() 76 err := d.doDetect(sourceTxn, waitForTxn) 77 if err == nil { 78 d.register(sourceTxn, waitForTxn, keyHash) 79 } 80 d.lock.Unlock() 81 return err 82 } 83 84 func (d *Detector) doDetect(sourceTxn, waitForTxn uint64) *ErrDeadlock { 85 list := d.waitForMap[waitForTxn] 86 if list == nil { 87 return nil 88 } 89 for _, nextTarget := range list.txns { 90 if nextTarget.txn == sourceTxn { 91 return &ErrDeadlock{KeyHash: nextTarget.keyHash} 92 } 93 if err := d.doDetect(sourceTxn, nextTarget.txn); err != nil { 94 return err 95 } 96 } 97 return nil 98 } 99 100 func (d *Detector) register(sourceTxn, waitForTxn, keyHash uint64) { 101 list := d.waitForMap[sourceTxn] 102 pair := txnKeyHashPair{txn: waitForTxn, keyHash: keyHash} 103 if list == nil { 104 d.waitForMap[sourceTxn] = &txnList{txns: []txnKeyHashPair{pair}} 105 return 106 } 107 for _, tar := range list.txns { 108 if tar.txn == waitForTxn && tar.keyHash == keyHash { 109 return 110 } 111 } 112 list.txns = append(list.txns, pair) 113 } 114 115 // CleanUp removes the wait for entry for the transaction. 116 func (d *Detector) CleanUp(txn uint64) { 117 d.lock.Lock() 118 delete(d.waitForMap, txn) 119 d.lock.Unlock() 120 } 121 122 // CleanUpWaitFor removes a key in the wait for entry for the transaction. 123 func (d *Detector) CleanUpWaitFor(txn, waitForTxn, keyHash uint64) { 124 pair := txnKeyHashPair{txn: waitForTxn, keyHash: keyHash} 125 d.lock.Lock() 126 l := d.waitForMap[txn] 127 if l != nil { 128 for i, tar := range l.txns { 129 if tar == pair { 130 l.txns = append(l.txns[:i], l.txns[i+1:]...) 131 break 132 } 133 } 134 if len(l.txns) == 0 { 135 delete(d.waitForMap, txn) 136 } 137 } 138 d.lock.Unlock() 139 140 } 141 142 // Expire removes entries with TS smaller than minTS. 143 func (d *Detector) Expire(minTS uint64) { 144 d.lock.Lock() 145 for ts := range d.waitForMap { 146 if ts < minTS { 147 delete(d.waitForMap, ts) 148 } 149 } 150 d.lock.Unlock() 151 }