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 }