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