github.com/matrixorigin/matrixone@v1.2.0/pkg/common/morpc/future.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 morpc 16 17 import ( 18 "runtime" 19 "sync" 20 "sync/atomic" 21 "time" 22 ) 23 24 func newFuture(releaseFunc func(f *Future)) *Future { 25 f := &Future{ 26 c: make(chan Message, 1), 27 errC: make(chan error, 1), 28 writtenC: make(chan error, 1), 29 releaseFunc: releaseFunc, 30 } 31 f.setFinalizer() 32 return f 33 } 34 35 // Future is used to obtain response data synchronously. 36 type Future struct { 37 id uint64 38 send RPCMessage 39 c chan Message 40 errC chan error 41 // used to check error for sending message 42 writtenC chan error 43 waiting atomic.Bool 44 releaseFunc func(*Future) 45 oneWay bool 46 mu struct { 47 sync.Mutex 48 notified bool 49 closed bool 50 ref int 51 cb func() 52 } 53 } 54 55 func (f *Future) init(send RPCMessage) { 56 if _, ok := send.Ctx.Deadline(); !ok && !send.oneWay && !send.internal { 57 panic("context deadline not set") 58 } 59 f.waiting.Store(false) 60 f.send = send 61 f.send.createAt = time.Now() 62 f.id = send.Message.GetID() 63 f.oneWay = send.oneWay 64 f.mu.Lock() 65 f.mu.closed = false 66 f.mu.notified = false 67 f.mu.Unlock() 68 } 69 70 // Get get the response data synchronously, blocking until `context.Done` or the response is received. 71 // This method cannot be called more than once. After calling `Get`, `Close` must be called to close 72 // `Future`. 73 func (f *Future) Get() (Message, error) { 74 // we have to wait until the message is written, otherwise it will result in the message still 75 // waiting in the send queue after the Get returns, causing concurrent reading and writing on the 76 // request. 77 if err := f.waitSendCompleted(); err != nil { 78 return nil, err 79 } 80 select { 81 case <-f.send.Ctx.Done(): 82 return nil, f.send.Ctx.Err() 83 case resp := <-f.c: 84 return resp, nil 85 case err := <-f.errC: 86 return nil, err 87 } 88 } 89 90 // Close closes the future. 91 func (f *Future) Close() { 92 f.mu.Lock() 93 defer f.mu.Unlock() 94 95 f.mu.closed = true 96 if f.mu.cb != nil { 97 f.mu.cb() 98 } 99 f.maybeReleaseLocked() 100 } 101 102 func (f *Future) waitSendCompleted() error { 103 if f.oneWay { 104 panic("one way cannot call waitSendCompleted") 105 } 106 return <-f.writtenC 107 } 108 109 func (f *Future) messageSent(err error) { 110 if !f.oneWay && f.waiting.CompareAndSwap(false, true) { 111 f.writtenC <- err 112 f.unRef() 113 } 114 } 115 116 func (f *Future) maybeReleaseLocked() { 117 if f.mu.closed && f.mu.ref == 0 && f.releaseFunc != nil { 118 f.clear() 119 f.releaseFunc(f) 120 } 121 } 122 123 func (f *Future) clear() { 124 for { 125 select { 126 case <-f.c: 127 case <-f.errC: 128 case <-f.writtenC: 129 default: 130 return 131 } 132 } 133 } 134 135 func (f *Future) getSendMessageID() uint64 { 136 return f.id 137 } 138 139 func (f *Future) done(response Message, cb func()) { 140 f.mu.Lock() 141 defer f.mu.Unlock() 142 143 if f.mu.notified { 144 return 145 } 146 147 if !f.mu.closed && !f.timeout() { 148 if response.GetID() != f.getSendMessageID() { 149 return 150 } 151 f.mu.cb = cb 152 f.c <- response 153 } else if cb != nil { 154 cb() 155 } 156 f.mu.notified = true 157 } 158 159 func (f *Future) error(id uint64, err error, cb func()) { 160 f.mu.Lock() 161 defer f.mu.Unlock() 162 163 if f.mu.notified { 164 return 165 } 166 167 if !f.mu.closed && !f.timeout() { 168 if id != f.getSendMessageID() { 169 return 170 } 171 f.mu.cb = cb 172 f.errC <- err 173 } else if cb != nil { 174 cb() 175 } 176 f.mu.notified = true 177 } 178 179 func (f *Future) ref() { 180 f.mu.Lock() 181 defer f.mu.Unlock() 182 183 f.mu.ref++ 184 } 185 186 func (f *Future) unRef() { 187 f.mu.Lock() 188 defer f.mu.Unlock() 189 190 f.mu.ref-- 191 if f.mu.ref < 0 { 192 panic("BUG") 193 } 194 f.maybeReleaseLocked() 195 } 196 197 func (f *Future) reset() { 198 select { 199 case <-f.c: 200 default: 201 } 202 f.send = RPCMessage{} 203 f.mu.cb = nil 204 f.id = 0 205 } 206 207 func (f *Future) timeout() bool { 208 select { 209 case <-f.send.Ctx.Done(): 210 return true 211 default: 212 return false 213 } 214 } 215 216 func (f *Future) setFinalizer() { 217 // when we need to reuse, we need to keep chan from being closed to avoid 218 // repeated creation. When Future is released by sync.Pool and is GC'd, we 219 // need to close chan to avoid resource leaks. 220 runtime.SetFinalizer(f, func(f *Future) { 221 close(f.c) 222 close(f.errC) 223 close(f.writtenC) 224 }) 225 }