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