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  }