github.com/imannamdari/v2ray-core/v5@v5.0.5/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/imannamdari/v2ray-core/v5/common"
    11  	"github.com/imannamdari/v2ray-core/v5/common/buf"
    12  	"github.com/imannamdari/v2ray-core/v5/common/signal"
    13  	"github.com/imannamdari/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  }