github.com/matrixorigin/matrixone@v0.7.0/pkg/lockservice/txn.go (about)

     1  // Copyright 2023 Matrix Origin
     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  package lockservice
    16  
    17  import (
    18  	"bytes"
    19  	"sync"
    20  )
    21  
    22  var (
    23  	txnPool = sync.Pool{New: func() any {
    24  		return &activeTxn{holdLocks: make(map[uint64]*cowSlice)}
    25  	}}
    26  )
    27  
    28  // activeTxn one goroutine write, multi goroutine read
    29  type activeTxn struct {
    30  	sync.RWMutex
    31  	txnID         []byte
    32  	txnKey        string
    33  	fsp           *fixedSlicePool
    34  	blockedWaiter *waiter
    35  	holdLocks     map[uint64]*cowSlice
    36  }
    37  
    38  func newActiveTxn(
    39  	txnID []byte,
    40  	txnKey string,
    41  	fsp *fixedSlicePool) *activeTxn {
    42  	txn := txnPool.Get().(*activeTxn)
    43  	txn.Lock()
    44  	defer txn.Unlock()
    45  	txn.txnID = txnID
    46  	txn.txnKey = txnKey
    47  	txn.fsp = fsp
    48  	return txn
    49  }
    50  
    51  func (txn *activeTxn) lockAdded(
    52  	table uint64,
    53  	locks [][]byte) {
    54  	txn.Lock()
    55  	defer txn.Unlock()
    56  
    57  	v, ok := txn.holdLocks[table]
    58  	if ok {
    59  		v.append(locks)
    60  		return
    61  	}
    62  	txn.holdLocks[table] = newCowSlice(txn.fsp, locks)
    63  }
    64  
    65  func (txn *activeTxn) close(lockTableFunc func(uint64) *lockTable) {
    66  	txn.Lock()
    67  	defer txn.Unlock()
    68  
    69  	for table, cs := range txn.holdLocks {
    70  		l := lockTableFunc(table)
    71  		l.unlock(cs)
    72  		cs.close()
    73  		delete(txn.holdLocks, table)
    74  	}
    75  	txn.txnID = nil
    76  	txn.txnKey = ""
    77  	txn.blockedWaiter = nil
    78  	txnPool.Put(txn)
    79  }
    80  
    81  func (txn *activeTxn) abort(txnID []byte) {
    82  	txn.RLock()
    83  	defer txn.RUnlock()
    84  
    85  	// txn already closed
    86  	if !bytes.Equal(txn.txnID, txnID) {
    87  		return
    88  	}
    89  
    90  	// if a deadlock occurs, then the transaction must have a waiter that is
    91  	// currently waiting and has been blocked inside the Lock.
    92  	if txn.blockedWaiter == nil {
    93  		panic("BUG: abort a non-waiting txn")
    94  	}
    95  
    96  	if !txn.blockedWaiter.notify(ErrDeadlockDetectorClosed) {
    97  		panic("BUG: can not notify deadlock failed")
    98  	}
    99  }
   100  
   101  func (txn *activeTxn) fetchWhoWaitingMe(
   102  	txnID []byte,
   103  	waiters *waiters,
   104  	lockTableFunc func(uint64) *lockTable) bool {
   105  	txn.RLock()
   106  	defer txn.RUnlock()
   107  
   108  	// txn already closed
   109  	if !bytes.Equal(txn.txnID, txnID) {
   110  		return true
   111  	}
   112  	for table, cs := range txn.holdLocks {
   113  		l := lockTableFunc(table)
   114  		locks := cs.slice()
   115  		hasDeadLock := false
   116  		locks.iter(func(lockKey []byte) bool {
   117  			if lock, ok := l.getLock(lockKey); ok {
   118  				lock.waiter.waiters.iter(func(id []byte) bool {
   119  					if !waiters.add(id) {
   120  						hasDeadLock = true
   121  						return false
   122  					}
   123  					return true
   124  				})
   125  				if hasDeadLock {
   126  					return false
   127  				}
   128  			}
   129  			return true
   130  		})
   131  		locks.unref()
   132  		if hasDeadLock {
   133  			return false
   134  		}
   135  	}
   136  	return true
   137  }
   138  
   139  func (txn *activeTxn) setBlocked(w *waiter) {
   140  	txn.Lock()
   141  	defer txn.Unlock()
   142  	txn.blockedWaiter = w
   143  }