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 }