github.com/matrixorigin/matrixone@v1.2.0/pkg/txn/service/service_event.go (about) 1 // Copyright 2021 - 2022 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 service 16 17 import ( 18 "context" 19 "runtime" 20 "sync" 21 22 "github.com/matrixorigin/matrixone/pkg/common/moerr" 23 "github.com/matrixorigin/matrixone/pkg/pb/txn" 24 ) 25 26 var ( 27 waiterPool = sync.Pool{ 28 New: func() any { 29 w := &waiter{c: make(chan txn.TxnStatus, 1)} 30 w.setFinalizer() 31 return w 32 }, 33 } 34 35 notifierPool = sync.Pool{ 36 New: func() any { 37 n := ¬ifier{} 38 n.waiters = make([][]*waiter, len(txn.TxnStatus_name)) 39 return n 40 }, 41 } 42 ) 43 44 func acquireWaiter() *waiter { 45 w := waiterPool.Get().(*waiter) 46 w.ref() 47 w.reuse++ 48 return w 49 } 50 51 type waiter struct { 52 c chan txn.TxnStatus 53 reuse uint64 54 55 mu struct { 56 sync.RWMutex 57 closed bool 58 // A waiter can only be notified once, regardless of how many transactions it watches. 59 notified bool 60 // The wait will held by notifier and the place to use waiter. The waiter can only be recycled to 61 // sync.Pool if it is no longer referenced by any. 62 ref int 63 } 64 } 65 66 func (w *waiter) ref() { 67 w.mu.Lock() 68 defer w.mu.Unlock() 69 70 w.mu.ref++ 71 } 72 73 func (w *waiter) unref() { 74 w.mu.Lock() 75 defer w.mu.Unlock() 76 77 w.unrefLocked() 78 w.maybeReleaseLocked() 79 } 80 81 func (w *waiter) unrefLocked() { 82 w.mu.ref-- 83 if w.mu.ref < 0 { 84 panic("invalid ref value") 85 } 86 } 87 88 func (w *waiter) notify(status txn.TxnStatus) bool { 89 w.mu.Lock() 90 defer w.mu.Unlock() 91 92 if w.mu.notified { 93 panic("already notified") 94 } 95 96 w.mu.notified = true 97 if w.mu.closed { 98 return false 99 } 100 select { 101 case w.c <- status: 102 default: 103 panic("BUG") 104 } 105 return true 106 } 107 108 func (w *waiter) close() { 109 w.mu.Lock() 110 defer w.mu.Unlock() 111 112 w.mu.closed = true 113 w.unrefLocked() 114 w.maybeReleaseLocked() 115 } 116 117 func (w *waiter) maybeReleaseLocked() { 118 if w.mu.ref == 0 { 119 w.mu.closed = false 120 w.mu.notified = false 121 select { 122 case <-w.c: 123 default: 124 } 125 waiterPool.Put(w) 126 } 127 } 128 129 // wait only 3 results: 130 // 1. want status, nil 131 // 2. any, context.Done error 132 // 3. final status(committed or aborted), nil 133 func (w *waiter) wait(ctx context.Context) (txn.TxnStatus, error) { 134 select { 135 case <-ctx.Done(): 136 return txn.TxnStatus_Aborted, moerr.ConvertGoError(ctx, ctx.Err()) 137 case status := <-w.c: 138 return status, nil 139 } 140 } 141 142 func (w *waiter) setFinalizer() { 143 runtime.SetFinalizer(w, func(w *waiter) { 144 close(w.c) 145 }) 146 } 147 148 func acquireNotifier() *notifier { 149 return notifierPool.Get().(*notifier) 150 } 151 152 // notifier a transaction corresponds to a notifier, for other transactions that need to be notified that 153 // the current transaction has reached the specified status. 154 type notifier struct { 155 sync.Mutex 156 waiters [][]*waiter 157 } 158 159 func (s *notifier) close(status txn.TxnStatus) { 160 s.Lock() 161 defer s.Unlock() 162 163 for i, waiters := range s.waiters { 164 for j := range waiters { 165 waiters[j].notify(status) 166 waiters[j].unref() 167 waiters[j] = nil 168 } 169 s.waiters[i] = waiters[:0] 170 } 171 notifierPool.Put(s) 172 } 173 174 func (s *notifier) notify(status txn.TxnStatus) int { 175 s.Lock() 176 defer s.Unlock() 177 178 if !s.hasWaitersLocked(status) { 179 return 0 180 } 181 182 waiters := s.waiters[status] 183 n := 0 184 for idx, w := range waiters { 185 if w.notify(status) { 186 n++ 187 } 188 w.unref() 189 waiters[idx] = nil 190 } 191 s.waiters[status] = waiters[:0] 192 return n 193 } 194 195 func (s *notifier) addWaiter(w *waiter, status txn.TxnStatus) { 196 s.Lock() 197 defer s.Unlock() 198 199 w.ref() 200 s.waiters[status] = append(s.waiters[status], w) 201 } 202 203 func (s *notifier) hasWaitersLocked(status txn.TxnStatus) bool { 204 return len(s.waiters[status]) > 0 205 }