github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/transport/pipe/impl.go (about) 1 package pipe 2 3 import ( 4 "errors" 5 "io" 6 "runtime" 7 "sync" 8 "time" 9 10 "github.com/v2fly/v2ray-core/v5/common" 11 "github.com/v2fly/v2ray-core/v5/common/buf" 12 "github.com/v2fly/v2ray-core/v5/common/signal" 13 "github.com/v2fly/v2ray-core/v5/common/signal/done" 14 ) 15 16 type state byte 17 18 const ( 19 open state = iota 20 closed 21 errord 22 ) 23 24 type pipeOption struct { 25 limit int32 // maximum buffer size in bytes 26 discardOverflow bool 27 } 28 29 func (o *pipeOption) isFull(curSize int32) bool { 30 return o.limit >= 0 && curSize > o.limit 31 } 32 33 type pipe struct { 34 sync.Mutex 35 data buf.MultiBuffer 36 readSignal *signal.Notifier 37 writeSignal *signal.Notifier 38 done *done.Instance 39 option pipeOption 40 state state 41 } 42 43 var ( 44 errBufferFull = errors.New("buffer full") 45 errSlowDown = errors.New("slow down") 46 ) 47 48 func (p *pipe) getState(forRead bool) error { 49 switch p.state { 50 case open: 51 if !forRead && p.option.isFull(p.data.Len()) { 52 return errBufferFull 53 } 54 return nil 55 case closed: 56 if !forRead { 57 return io.ErrClosedPipe 58 } 59 if !p.data.IsEmpty() { 60 return nil 61 } 62 return io.EOF 63 case errord: 64 return io.ErrClosedPipe 65 default: 66 panic("impossible case") 67 } 68 } 69 70 func (p *pipe) readMultiBufferInternal() (buf.MultiBuffer, error) { 71 p.Lock() 72 defer p.Unlock() 73 74 if err := p.getState(true); err != nil { 75 return nil, err 76 } 77 78 data := p.data 79 p.data = nil 80 return data, nil 81 } 82 83 func (p *pipe) ReadMultiBuffer() (buf.MultiBuffer, error) { 84 for { 85 data, err := p.readMultiBufferInternal() 86 if data != nil || err != nil { 87 p.writeSignal.Signal() 88 return data, err 89 } 90 91 select { 92 case <-p.readSignal.Wait(): 93 case <-p.done.Wait(): 94 } 95 } 96 } 97 98 func (p *pipe) ReadMultiBufferTimeout(d time.Duration) (buf.MultiBuffer, error) { 99 timer := time.NewTimer(d) 100 defer timer.Stop() 101 102 for { 103 data, err := p.readMultiBufferInternal() 104 if data != nil || err != nil { 105 p.writeSignal.Signal() 106 return data, err 107 } 108 109 select { 110 case <-p.readSignal.Wait(): 111 case <-p.done.Wait(): 112 case <-timer.C: 113 return nil, buf.ErrReadTimeout 114 } 115 } 116 } 117 118 func (p *pipe) writeMultiBufferInternal(mb buf.MultiBuffer) error { 119 p.Lock() 120 defer p.Unlock() 121 122 if err := p.getState(false); err != nil { 123 return err 124 } 125 126 if p.data == nil { 127 p.data = mb 128 return nil 129 } 130 131 p.data, _ = buf.MergeMulti(p.data, mb) 132 return errSlowDown 133 } 134 135 func (p *pipe) WriteMultiBuffer(mb buf.MultiBuffer) error { 136 if mb.IsEmpty() { 137 return nil 138 } 139 140 for { 141 err := p.writeMultiBufferInternal(mb) 142 if err == nil { 143 p.readSignal.Signal() 144 return nil 145 } 146 147 if err == errSlowDown { 148 p.readSignal.Signal() 149 150 // Yield current goroutine. Hopefully the reading counterpart can pick up the payload. 151 runtime.Gosched() 152 return nil 153 } 154 155 if err == errBufferFull && p.option.discardOverflow { 156 buf.ReleaseMulti(mb) 157 return nil 158 } 159 160 if err != errBufferFull { 161 buf.ReleaseMulti(mb) 162 p.readSignal.Signal() 163 return err 164 } 165 166 select { 167 case <-p.writeSignal.Wait(): 168 case <-p.done.Wait(): 169 return io.ErrClosedPipe 170 } 171 } 172 } 173 174 func (p *pipe) Close() error { 175 p.Lock() 176 defer p.Unlock() 177 178 if p.state == closed || p.state == errord { 179 return nil 180 } 181 182 p.state = closed 183 common.Must(p.done.Close()) 184 return nil 185 } 186 187 // Interrupt implements common.Interruptible. 188 func (p *pipe) Interrupt() { 189 p.Lock() 190 defer p.Unlock() 191 192 if p.state == closed || p.state == errord { 193 return 194 } 195 196 p.state = errord 197 198 if !p.data.IsEmpty() { 199 buf.ReleaseMulti(p.data) 200 p.data = nil 201 } 202 203 common.Must(p.done.Close()) 204 }