github.com/cloudwego/kitex@v0.9.0/pkg/remote/trans/nphttp2/grpc/flowcontrol.go (about) 1 /* 2 * 3 * Copyright 2014 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 * This file may have been modified by CloudWeGo authors. All CloudWeGo 18 * Modifications are Copyright 2021 CloudWeGo Authors. 19 */ 20 21 package grpc 22 23 import ( 24 "fmt" 25 "math" 26 "sync" 27 "sync/atomic" 28 ) 29 30 // writeQuota is a soft limit on the amount of data a stream can 31 // schedule before some of it is written out. 32 type writeQuota struct { 33 quota int32 34 // get waits on read from when quota goes less than or equal to zero. 35 // replenish writes on it when quota goes positive again. 36 ch chan struct{} 37 // done is triggered in error case. 38 done <-chan struct{} 39 // replenish is called by loopyWriter to give quota back to. 40 // It is implemented as a field so that it can be updated 41 // by tests. 42 replenish func(n int) 43 } 44 45 func newWriteQuota(sz int32, done <-chan struct{}) *writeQuota { 46 w := &writeQuota{ 47 quota: sz, 48 ch: make(chan struct{}, 1), 49 done: done, 50 } 51 w.replenish = w.realReplenish 52 return w 53 } 54 55 func (w *writeQuota) get(sz int32) error { 56 for { 57 if atomic.LoadInt32(&w.quota) > 0 { 58 atomic.AddInt32(&w.quota, -sz) 59 return nil 60 } 61 select { 62 case <-w.ch: 63 continue 64 case <-w.done: 65 return errStreamDone 66 } 67 } 68 } 69 70 func (w *writeQuota) realReplenish(n int) { 71 sz := int32(n) 72 a := atomic.AddInt32(&w.quota, sz) 73 b := a - sz 74 if b <= 0 && a > 0 { 75 select { 76 case w.ch <- struct{}{}: 77 default: 78 } 79 } 80 } 81 82 type trInFlow struct { 83 limit uint32 84 unacked uint32 85 effectiveWindowSize uint32 86 } 87 88 func (f *trInFlow) newLimit(n uint32) uint32 { 89 d := n - f.limit 90 f.limit = n 91 f.updateEffectiveWindowSize() 92 return d 93 } 94 95 func (f *trInFlow) onData(n uint32) uint32 { 96 f.unacked += n 97 if f.unacked >= f.limit/4 { 98 w := f.unacked 99 f.unacked = 0 100 f.updateEffectiveWindowSize() 101 return w 102 } 103 f.updateEffectiveWindowSize() 104 return 0 105 } 106 107 func (f *trInFlow) reset() uint32 { 108 w := f.unacked 109 f.unacked = 0 110 f.updateEffectiveWindowSize() 111 return w 112 } 113 114 func (f *trInFlow) updateEffectiveWindowSize() { 115 atomic.StoreUint32(&f.effectiveWindowSize, f.limit-f.unacked) 116 } 117 118 // TODO(mmukhi): Simplify this code. 119 // inFlow deals with inbound flow control 120 type inFlow struct { 121 mu sync.Mutex 122 // The inbound flow control limit for pending data. 123 limit uint32 124 // pendingData is the overall data which have been received but not been 125 // consumed by applications. 126 pendingData uint32 127 // The amount of data the application has consumed but grpc has not sent 128 // window update for them. Used to reduce window update frequency. 129 pendingUpdate uint32 130 // delta is the extra window update given by receiver when an application 131 // is reading data bigger in size than the inFlow limit. 132 delta uint32 133 } 134 135 // newLimit updates the inflow window to a new value n. 136 // It assumes that n is always greater than the old limit. 137 func (f *inFlow) newLimit(n uint32) { 138 f.mu.Lock() 139 f.limit = n 140 f.mu.Unlock() 141 } 142 143 func (f *inFlow) maybeAdjust(n uint32) uint32 { 144 if n > uint32(math.MaxInt32) { 145 n = uint32(math.MaxInt32) 146 } 147 f.mu.Lock() 148 defer f.mu.Unlock() 149 // estSenderQuota is the receiver's view of the maximum number of bytes the sender 150 // can send without a window update. 151 estSenderQuota := int32(f.limit - (f.pendingData + f.pendingUpdate)) 152 // estUntransmittedData is the maximum number of bytes the sends might not have put 153 // on the wire yet. A value of 0 or less means that we have already received all or 154 // more bytes than the application is requesting to read. 155 estUntransmittedData := int32(n - f.pendingData) // Casting into int32 since it could be negative. 156 // This implies that unless we send a window update, the sender won't be able to send all the bytes 157 // for this message. Therefore we must send an update over the limit since there's an active read 158 // request from the application. 159 if estUntransmittedData > estSenderQuota { 160 // Sender's window shouldn't go more than 2^31 - 1 as specified in the HTTP spec. 161 if f.limit+n > maxWindowSize { 162 f.delta = maxWindowSize - f.limit 163 } else { 164 // Send a window update for the whole message and not just the difference between 165 // estUntransmittedData and estSenderQuota. This will be helpful in case the message 166 // is padded; We will fallback on the current available window(at least a 1/4th of the limit). 167 f.delta = n 168 } 169 return f.delta 170 } 171 return 0 172 } 173 174 // onData is invoked when some data frame is received. It updates pendingData. 175 func (f *inFlow) onData(n uint32) error { 176 f.mu.Lock() 177 f.pendingData += n 178 if f.pendingData+f.pendingUpdate > f.limit+f.delta { 179 limit := f.limit 180 rcvd := f.pendingData + f.pendingUpdate 181 f.mu.Unlock() 182 return fmt.Errorf("received %d-bytes data exceeding the limit %d bytes", rcvd, limit) 183 } 184 f.mu.Unlock() 185 return nil 186 } 187 188 // onRead is invoked when the application reads the data. It returns the window size 189 // to be sent to the peer. 190 func (f *inFlow) onRead(n uint32) uint32 { 191 f.mu.Lock() 192 if f.pendingData == 0 { 193 f.mu.Unlock() 194 return 0 195 } 196 f.pendingData -= n 197 if n > f.delta { 198 n -= f.delta 199 f.delta = 0 200 } else { 201 f.delta -= n 202 n = 0 203 } 204 f.pendingUpdate += n 205 if f.pendingUpdate >= f.limit/4 { 206 wu := f.pendingUpdate 207 f.pendingUpdate = 0 208 f.mu.Unlock() 209 return wu 210 } 211 f.mu.Unlock() 212 return 0 213 }