github.com/sagernet/sing@v0.4.0-beta.19.0.20240518125136-f67a0988a636/common/pipe/pipe.go (about)

     1  // Copyright 2010 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package pipe
     6  
     7  import (
     8  	"io"
     9  	"net"
    10  	"os"
    11  	"sync"
    12  	"time"
    13  
    14  	N "github.com/sagernet/sing/common/network"
    15  )
    16  
    17  // pipeDeadline is an abstraction for handling timeouts.
    18  type pipeDeadline struct {
    19  	mu     sync.Mutex // Guards timer and cancel
    20  	timer  *time.Timer
    21  	cancel chan struct{} // Must be non-nil
    22  }
    23  
    24  func makePipeDeadline() pipeDeadline {
    25  	return pipeDeadline{cancel: make(chan struct{})}
    26  }
    27  
    28  // set sets the point in time when the deadline will time out.
    29  // A timeout event is signaled by closing the channel returned by waiter.
    30  // Once a timeout has occurred, the deadline can be refreshed by specifying a
    31  // t value in the future.
    32  //
    33  // A zero value for t prevents timeout.
    34  func (d *pipeDeadline) set(t time.Time) {
    35  	d.mu.Lock()
    36  	defer d.mu.Unlock()
    37  
    38  	if d.timer != nil && !d.timer.Stop() {
    39  		<-d.cancel // Wait for the timer callback to finish and close cancel
    40  	}
    41  	d.timer = nil
    42  
    43  	// Time is zero, then there is no deadline.
    44  	closed := isClosedChan(d.cancel)
    45  	if t.IsZero() {
    46  		if closed {
    47  			d.cancel = make(chan struct{})
    48  		}
    49  		return
    50  	}
    51  
    52  	// Time in the future, setup a timer to cancel in the future.
    53  	if dur := time.Until(t); dur > 0 {
    54  		if closed {
    55  			d.cancel = make(chan struct{})
    56  		}
    57  		d.timer = time.AfterFunc(dur, func() {
    58  			close(d.cancel)
    59  		})
    60  		return
    61  	}
    62  
    63  	// Time in the past, so close immediately.
    64  	if !closed {
    65  		close(d.cancel)
    66  	}
    67  }
    68  
    69  // wait returns a channel that is closed when the deadline is exceeded.
    70  func (d *pipeDeadline) wait() chan struct{} {
    71  	d.mu.Lock()
    72  	defer d.mu.Unlock()
    73  	return d.cancel
    74  }
    75  
    76  func isClosedChan(c <-chan struct{}) bool {
    77  	select {
    78  	case <-c:
    79  		return true
    80  	default:
    81  		return false
    82  	}
    83  }
    84  
    85  type pipeAddr struct{}
    86  
    87  func (pipeAddr) Network() string { return "pipe" }
    88  func (pipeAddr) String() string  { return "pipe" }
    89  
    90  type pipe struct {
    91  	wrMu sync.Mutex // Serialize Write operations
    92  
    93  	// Used by local Read to interact with remote Write.
    94  	// Successful receive on rdRx is always followed by send on rdTx.
    95  	rdRx <-chan []byte
    96  	rdTx chan<- int
    97  
    98  	// Used by local Write to interact with remote Read.
    99  	// Successful send on wrTx is always followed by receive on wrRx.
   100  	wrTx chan<- []byte
   101  	wrRx <-chan int
   102  
   103  	once       sync.Once // Protects closing localDone
   104  	localDone  chan struct{}
   105  	remoteDone <-chan struct{}
   106  
   107  	readDeadline  pipeDeadline
   108  	writeDeadline pipeDeadline
   109  
   110  	readWaitOptions N.ReadWaitOptions
   111  }
   112  
   113  // Pipe creates a synchronous, in-memory, full duplex
   114  // network connection; both ends implement the Conn interface.
   115  // Reads on one end are matched with writes on the other,
   116  // copying data directly between the two; there is no internal
   117  // buffering.
   118  func Pipe() (net.Conn, net.Conn) {
   119  	cb1 := make(chan []byte)
   120  	cb2 := make(chan []byte)
   121  	cn1 := make(chan int)
   122  	cn2 := make(chan int)
   123  	done1 := make(chan struct{})
   124  	done2 := make(chan struct{})
   125  
   126  	p1 := &pipe{
   127  		rdRx: cb1, rdTx: cn1,
   128  		wrTx: cb2, wrRx: cn2,
   129  		localDone: done1, remoteDone: done2,
   130  		readDeadline:  makePipeDeadline(),
   131  		writeDeadline: makePipeDeadline(),
   132  	}
   133  	p2 := &pipe{
   134  		rdRx: cb2, rdTx: cn2,
   135  		wrTx: cb1, wrRx: cn1,
   136  		localDone: done2, remoteDone: done1,
   137  		readDeadline:  makePipeDeadline(),
   138  		writeDeadline: makePipeDeadline(),
   139  	}
   140  	return p1, p2
   141  }
   142  
   143  func (*pipe) LocalAddr() net.Addr  { return pipeAddr{} }
   144  func (*pipe) RemoteAddr() net.Addr { return pipeAddr{} }
   145  
   146  func (p *pipe) Read(b []byte) (int, error) {
   147  	n, err := p.read(b)
   148  	if err != nil && err != io.EOF && err != io.ErrClosedPipe {
   149  		err = &net.OpError{Op: "read", Net: "pipe", Err: err}
   150  	}
   151  	return n, err
   152  }
   153  
   154  func (p *pipe) read(b []byte) (n int, err error) {
   155  	switch {
   156  	case isClosedChan(p.localDone):
   157  		return 0, io.ErrClosedPipe
   158  	case isClosedChan(p.remoteDone):
   159  		return 0, io.EOF
   160  	case isClosedChan(p.readDeadline.wait()):
   161  		return 0, os.ErrDeadlineExceeded
   162  	}
   163  
   164  	select {
   165  	case bw := <-p.rdRx:
   166  		nr := copy(b, bw)
   167  		p.rdTx <- nr
   168  		return nr, nil
   169  	case <-p.localDone:
   170  		return 0, io.ErrClosedPipe
   171  	case <-p.remoteDone:
   172  		return 0, io.EOF
   173  	case <-p.readDeadline.wait():
   174  		return 0, os.ErrDeadlineExceeded
   175  	}
   176  }
   177  
   178  func (p *pipe) Write(b []byte) (int, error) {
   179  	n, err := p.write(b)
   180  	if err != nil && err != io.ErrClosedPipe {
   181  		err = &net.OpError{Op: "write", Net: "pipe", Err: err}
   182  	}
   183  	return n, err
   184  }
   185  
   186  func (p *pipe) write(b []byte) (n int, err error) {
   187  	switch {
   188  	case isClosedChan(p.localDone):
   189  		return 0, io.ErrClosedPipe
   190  	case isClosedChan(p.remoteDone):
   191  		return 0, io.ErrClosedPipe
   192  	case isClosedChan(p.writeDeadline.wait()):
   193  		return 0, os.ErrDeadlineExceeded
   194  	}
   195  
   196  	p.wrMu.Lock() // Ensure entirety of b is written together
   197  	defer p.wrMu.Unlock()
   198  	for once := true; once || len(b) > 0; once = false {
   199  		select {
   200  		case p.wrTx <- b:
   201  			nw := <-p.wrRx
   202  			b = b[nw:]
   203  			n += nw
   204  		case <-p.localDone:
   205  			return n, io.ErrClosedPipe
   206  		case <-p.remoteDone:
   207  			return n, io.ErrClosedPipe
   208  		case <-p.writeDeadline.wait():
   209  			return n, os.ErrDeadlineExceeded
   210  		}
   211  	}
   212  	return n, nil
   213  }
   214  
   215  func (p *pipe) SetDeadline(t time.Time) error {
   216  	if isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {
   217  		return io.ErrClosedPipe
   218  	}
   219  	p.readDeadline.set(t)
   220  	p.writeDeadline.set(t)
   221  	return nil
   222  }
   223  
   224  func (p *pipe) SetReadDeadline(t time.Time) error {
   225  	if isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {
   226  		return io.ErrClosedPipe
   227  	}
   228  	p.readDeadline.set(t)
   229  	return nil
   230  }
   231  
   232  func (p *pipe) SetWriteDeadline(t time.Time) error {
   233  	if isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {
   234  		return io.ErrClosedPipe
   235  	}
   236  	p.writeDeadline.set(t)
   237  	return nil
   238  }
   239  
   240  func (p *pipe) Close() error {
   241  	p.once.Do(func() { close(p.localDone) })
   242  	return nil
   243  }