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  }