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  }