github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/multiplexing/stream.go (about) 1 package multiplexing 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "net" 8 "os" 9 "sync" 10 "time" 11 12 "github.com/mutagen-io/mutagen/pkg/multiplexing/ring" 13 ) 14 15 var ( 16 // ErrWriteClosed is returned from operations that fail due to a stream 17 // being closed for writing. It is analgous to net.ErrClosed, but indicates 18 // that only the write portion of a stream is closed. 19 ErrWriteClosed = errors.New("closed for writing") 20 // errRemoteClosed is a version of net.ErrorClosed that indicates a stream 21 // was closed on the remote. 22 errRemoteClosed = fmt.Errorf("remote: %w", net.ErrClosed) 23 ) 24 25 // Stream represents a single multiplexed stream. It implements net.Conn but 26 // also provides a CloseWrite method for half-closures. 27 type Stream struct { 28 // multiplexer is the parent multiplexer. 29 multiplexer *Multiplexer 30 // identifier is the stream identifier. 31 identifier uint64 32 33 // established is closed by the multiplexer if and when the stream is fully 34 // established. It may never be closed if the stream is never accepted or is 35 // rejected. 36 established chan struct{} 37 38 // remoteClosedWrite is closed by the multiplexer's reader Goroutine if and 39 // when it receives a close write message for the stream from the remote. 40 remoteClosedWrite chan struct{} 41 // remoteClosed is closed by the multiplexer's reader Goroutine if and when 42 // it receives a close message for the stream from the remote. 43 remoteClosed chan struct{} 44 45 // closeOnce guards closure of closed. 46 closeOnce sync.Once 47 // closed is closed when the stream is closed. 48 closed chan struct{} 49 50 // readDeadline holds the timer used to regulate read deadlines. The timer 51 // itself is used as a semaphor to serialize read operations. The holder of 52 // the timer is responsible for processing deadline set operations on the 53 // readDeadlineSet channel if the timer is to be held in a blocking manner. 54 // The holder is also responsible for setting the readDeadlineExpired field 55 // if the timer is observed to expire. 56 readDeadline chan *time.Timer 57 // readDeadlineSet is used to signal read deadline set operations to the 58 // current holder of the read deadline timer. 59 readDeadlineSet chan time.Time 60 // readDeadlineExpired is used to record that the holder of the read 61 // deadline timer saw it expire. 62 readDeadlineExpired bool 63 64 // receiveBufferLock guards access to receiveBuffer and write access to 65 // receiveBufferReady. 66 receiveBufferLock sync.Mutex 67 // receiveBuffer is the inbound data buffer. 68 receiveBuffer *ring.Buffer 69 // receiveBufferReady is used to signal that receiveBuffer is non-empty. 70 // Read access to this channel is guarded by holding the read deadline timer 71 // (i.e. being the current reader). Write access is guarded by holding 72 // receiveBufferLock. When receiveBufferLock is not held, this channel must 73 // be empty if receiveBuffer is empty. Note that this channel may be empty 74 // if receiveBuffer is non-empty in the case that a reader has drained it 75 // and is now waiting for receiveBufferLock. This channel must be written to 76 // by the holder of receiveBufferLock if receiveBuffer transitions from 77 // empty to non-empty while the lock is held. 78 receiveBufferReady chan struct{} 79 80 // closeWriteOnce guards closure of closedWrite. 81 closeWriteOnce sync.Once 82 // closedWrite is closed when the stream is closed for writing. 83 closedWrite chan struct{} 84 85 // writeDeadline holds the timer used to regulate write deadlines. The timer 86 // itself is used as a semaphor to serialize write operations. The holder of 87 // the timer is responsible for processing deadline set operations on the 88 // writeDeadlineSet channel if the timer is to be held in a blocking manner. 89 // The holder is also responsible for setting the writeDeadlineExpired field 90 // if the timer is observed to expire. 91 writeDeadline chan *time.Timer 92 // writeDeadlineSet is used to signal write deadline set operations to the 93 // current holder of the write deadline timer. 94 writeDeadlineSet chan time.Time 95 // readDeadlineExpired is used to record that the holder of the write 96 // deadline timer saw it expire. 97 writeDeadlineExpired bool 98 99 // sendWindowLock guards access to sendWindow and write access to 100 // sendWindowReady. 101 sendWindowLock sync.Mutex 102 // sendWindow is the current send window. 103 sendWindow uint64 104 // sendWindowReady is used to signal that sendWindow is non-zero. Read 105 // access to this channel is guarded by holding the write deadline timer 106 // (i.e. being the current writer). Write access is guarded by holding 107 // sendWindowLock. When sendWindowLock is not held, this channel must be 108 // empty if sendWindow is zero. Note that this channel may be empty if 109 // sendWindow is non-zero in the case that a writer has drained it and is 110 // now waiting for sendWindowLock. This channel must be written to by the 111 // holder of sendWindowLock if sendWindow transitions from zero to non-zero 112 // while the lock is held. 113 sendWindowReady chan struct{} 114 } 115 116 // newStoppedTimer creates a new stopped and drained timer. 117 func newStoppedTimer() *time.Timer { 118 timer := time.NewTimer(0) 119 if !timer.Stop() { 120 <-timer.C 121 } 122 return timer 123 } 124 125 // newStream constructs a new stream. 126 func newStream(multiplexer *Multiplexer, identifier uint64, receiveWindow int) *Stream { 127 // Create the stream. 128 stream := &Stream{ 129 multiplexer: multiplexer, 130 identifier: identifier, 131 established: make(chan struct{}), 132 remoteClosedWrite: make(chan struct{}), 133 remoteClosed: make(chan struct{}), 134 closed: make(chan struct{}), 135 readDeadline: make(chan *time.Timer, 1), 136 readDeadlineSet: make(chan time.Time), 137 receiveBuffer: ring.NewBuffer(receiveWindow), 138 receiveBufferReady: make(chan struct{}, 1), 139 closedWrite: make(chan struct{}), 140 writeDeadline: make(chan *time.Timer, 1), 141 writeDeadlineSet: make(chan time.Time), 142 sendWindowReady: make(chan struct{}, 1), 143 } 144 stream.readDeadline <- newStoppedTimer() 145 stream.writeDeadline <- newStoppedTimer() 146 147 // Done. 148 return stream 149 } 150 151 // Read implements net.Conn.Read. 152 func (s *Stream) Read(buffer []byte) (int, error) { 153 // Check for persistent pre-existing error conditions that would prevent a 154 // read from succeeding. While we could just allow these to bubble up in the 155 // select operations below, their priority in that case would be random, 156 // whereas we want error conditions to be returned consistently once they 157 // exist. Thus, we cascade these checks in order of reporting priority to 158 // ensure consistent error values on subsequent calls once their respective 159 // error conditions exist and have been observed for the first time. 160 if isClosed(s.closed) { 161 return 0, net.ErrClosed 162 } else if isClosed(s.multiplexer.closed) { 163 return 0, ErrMultiplexerClosed 164 } 165 166 // Acquire the read deadline timer, which gives us exclusive read access. 167 // It's important to monitor for local stream closure here because that 168 // indicates that the read deadline timer has been removed from circulation. 169 var readDeadlineTimer *time.Timer 170 select { 171 case readDeadlineTimer = <-s.readDeadline: 172 case <-s.closed: 173 return 0, net.ErrClosed 174 case <-s.multiplexer.closed: 175 return 0, ErrMultiplexerClosed 176 } 177 178 // Defer return of the read deadline timer. 179 defer func() { 180 s.readDeadline <- readDeadlineTimer 181 }() 182 183 // Check if the read deadline is already expired. 184 if s.readDeadlineExpired { 185 return 0, os.ErrDeadlineExceeded 186 } else if wasPopulatedWithTime(readDeadlineTimer.C) { 187 s.readDeadlineExpired = true 188 return 0, os.ErrDeadlineExceeded 189 } 190 191 // Wait until the read buffer is populated, the remote cleanly closes the 192 // stream, or an error occurs. 193 var bufferReady bool 194 for !bufferReady { 195 select { 196 case <-s.receiveBufferReady: 197 bufferReady = true 198 case <-s.remoteClosedWrite: 199 select { 200 case <-s.receiveBufferReady: 201 bufferReady = true 202 default: 203 return 0, io.EOF 204 } 205 case <-s.remoteClosed: 206 select { 207 case <-s.receiveBufferReady: 208 bufferReady = true 209 default: 210 return 0, io.EOF 211 } 212 case <-s.closed: 213 return 0, net.ErrClosed 214 case <-s.multiplexer.closed: 215 return 0, ErrMultiplexerClosed 216 case <-readDeadlineTimer.C: 217 s.readDeadlineExpired = true 218 return 0, os.ErrDeadlineExceeded 219 case deadline := <-s.readDeadlineSet: 220 setStreamDeadline(readDeadlineTimer, &s.readDeadlineExpired, deadline) 221 if s.readDeadlineExpired { 222 return 0, os.ErrDeadlineExceeded 223 } 224 } 225 } 226 227 // Perform a read from the buffer and ensure that the readiness channel is 228 // left in an appropriate state. 229 s.receiveBufferLock.Lock() 230 count, _ := s.receiveBuffer.Read(buffer) 231 if s.receiveBuffer.Used() > 0 { 232 s.receiveBufferReady <- struct{}{} 233 } 234 s.receiveBufferLock.Unlock() 235 236 // Send a window update corresponding to the amount that we read. 237 select { 238 case s.multiplexer.enqueueWindowIncrement <- windowIncrement{s.identifier, uint64(count)}: 239 case <-s.multiplexer.closed: 240 return count, ErrMultiplexerClosed 241 } 242 243 // Success. 244 return count, nil 245 } 246 247 // min returns the lesser of a or b. 248 func min(a, b uint64) uint64 { 249 if a < b { 250 return a 251 } 252 return b 253 } 254 255 // Write implements net.Conn.Write. 256 func (s *Stream) Write(data []byte) (int, error) { 257 // Check for persistent pre-existing error conditions that would prevent a 258 // write from succeeding. While we could just allow these to bubble up in 259 // the select operations below, their priority in that case would be random, 260 // whereas we want error conditions to be returned consistently once they 261 // exist. Thus, we cascade these checks in order of reporting priority to 262 // ensure consistent error values on subsequent calls once their respective 263 // error conditions exist and have been observed for the first time. 264 if isClosed(s.closed) { 265 return 0, net.ErrClosed 266 } else if isClosed(s.closedWrite) { 267 return 0, ErrWriteClosed 268 } else if isClosed(s.multiplexer.closed) { 269 return 0, ErrMultiplexerClosed 270 } else if isClosed(s.remoteClosed) { 271 return 0, errRemoteClosed 272 } 273 274 // Acquire the write deadline timer, which gives us exclusive write access. 275 // We monitor for the same set of errors as above, though it's particularly 276 // important to monitor for local write closure because that indicates that 277 // the write deadline timer has been removed from circulation. 278 var writeDeadlineTimer *time.Timer 279 select { 280 case writeDeadlineTimer = <-s.writeDeadline: 281 case <-s.closed: 282 return 0, net.ErrClosed 283 case <-s.closedWrite: 284 return 0, ErrWriteClosed 285 case <-s.multiplexer.closed: 286 return 0, ErrMultiplexerClosed 287 case <-s.remoteClosed: 288 return 0, errRemoteClosed 289 } 290 291 // Defer return of the write deadline timer. 292 defer func() { 293 s.writeDeadline <- writeDeadlineTimer 294 }() 295 296 // Check if the write deadline is already expired. 297 if s.writeDeadlineExpired { 298 return 0, os.ErrDeadlineExceeded 299 } else if wasPopulatedWithTime(writeDeadlineTimer.C) { 300 s.writeDeadlineExpired = true 301 return 0, os.ErrDeadlineExceeded 302 } 303 304 // Loop until all data has been written or an error occurs. 305 var count int 306 for len(data) > 0 { 307 // Loop until we have a combination of non-zero send window and a write 308 // buffer to transmit data. We only start polling for a write buffer 309 // once we have at least some non-zero amount of send window capacity. 310 var haveNonZeroSendWindow bool 311 var writeBuffer *messageBuffer 312 for writeBuffer == nil { 313 // Check if we're polling for the write buffer. 314 writeBufferAvailable := s.multiplexer.writeBufferAvailable 315 if !haveNonZeroSendWindow { 316 writeBufferAvailable = nil 317 } 318 319 // Perform polling. If we fail due to deadline expiration while 320 // waiting for a write buffer to become available, then we need to 321 // resignal send window readiness for future writes, because we will 322 // have drained the channel. Any other error condition is terminal, 323 // so there's no need to resginal readiness in those cases. 324 select { 325 case <-s.sendWindowReady: 326 haveNonZeroSendWindow = true 327 case writeBuffer = <-writeBufferAvailable: 328 case <-s.closed: 329 return count, net.ErrClosed 330 case <-s.closedWrite: 331 return count, ErrWriteClosed 332 case <-s.multiplexer.closed: 333 return count, ErrMultiplexerClosed 334 case <-s.remoteClosed: 335 return count, errRemoteClosed 336 case <-writeDeadlineTimer.C: 337 if haveNonZeroSendWindow { 338 s.sendWindowLock.Lock() 339 s.sendWindowReady <- struct{}{} 340 s.sendWindowLock.Unlock() 341 } 342 s.writeDeadlineExpired = true 343 return count, os.ErrDeadlineExceeded 344 case deadline := <-s.writeDeadlineSet: 345 setStreamDeadline(writeDeadlineTimer, &s.writeDeadlineExpired, deadline) 346 if s.writeDeadlineExpired { 347 if haveNonZeroSendWindow { 348 s.sendWindowLock.Lock() 349 s.sendWindowReady <- struct{}{} 350 s.sendWindowLock.Unlock() 351 } 352 return count, os.ErrDeadlineExceeded 353 } 354 } 355 } 356 357 // Compute our write window and ensure the that the readiness channel is 358 // left in an appropriate state. 359 s.sendWindowLock.Lock() 360 window := min(s.sendWindow, min(uint64(len(data)), maximumStreamDataBlockSize)) 361 s.sendWindow -= window 362 if s.sendWindow > 0 { 363 s.sendWindowReady <- struct{}{} 364 } 365 s.sendWindowLock.Unlock() 366 367 // Encode the stream data message and queue it for transmission. 368 writeBuffer.encodeStreamDataMessage(s.identifier, data[:window]) 369 s.multiplexer.writeBufferPending <- writeBuffer 370 371 // Reduce the remaining data slice and update the count. 372 data = data[window:] 373 count += int(window) 374 } 375 376 // Success. 377 return count, nil 378 } 379 380 // closeWrite is the internal write closure method. It makes transmission of the 381 // stream close write message optional. 382 func (s *Stream) closeWrite(sendCloseWriteMessage bool) (err error) { 383 // Perform write closure idempotently. 384 s.closeWriteOnce.Do(func() { 385 // Signal write closure internally. 386 close(s.closedWrite) 387 388 // Wait for all writers to unblock by acquiring the write deadline and 389 // taking it out of circulation (and ensuring that it's stopped). 390 writeDeadlineTimer := <-s.writeDeadline 391 writeDeadlineTimer.Stop() 392 393 // If requested, queue transmission of a close write message. 394 if sendCloseWriteMessage { 395 select { 396 case s.multiplexer.enqueueCloseWrite <- s.identifier: 397 case <-s.multiplexer.closed: 398 err = ErrMultiplexerClosed 399 } 400 } 401 }) 402 403 // Done. 404 return 405 } 406 407 // CloseWrite performs half-closure (write-closure) of the stream. Any blocked 408 // Write or SetWriteDeadline calls will be unblocked. Subsequent calls to 409 // CloseWrite are no-ops and will return nil. 410 func (s *Stream) CloseWrite() error { 411 return s.closeWrite(true) 412 } 413 414 // close is the internal closure method. It makes transmission of the stream 415 // close message optional. 416 func (s *Stream) close(sendCloseMessage bool) (err error) { 417 // Terminate writing if it hasn't been terminated already, but don't queue 418 // a close write message because we're about to send a full close message. 419 s.closeWrite(false) 420 421 // Perform full closure idempotently. 422 s.closeOnce.Do(func() { 423 // Signal closure internally. 424 close(s.closed) 425 426 // Wait for all readers to unblock by acquiring the read deadline and 427 // taking it out of circulation (and ensuring that it's stopped). 428 // Writers will already have unblocked by the time the closeWrite call 429 // above returned. 430 readDeadlineTimer := <-s.readDeadline 431 readDeadlineTimer.Stop() 432 433 // If requested, queue transmission of a close message. 434 if sendCloseMessage { 435 select { 436 case s.multiplexer.enqueueClose <- s.identifier: 437 case <-s.multiplexer.closed: 438 err = ErrMultiplexerClosed 439 } 440 } 441 442 // Deregister the stream from the parent multiplexer. 443 s.multiplexer.streamLock.Lock() 444 delete(s.multiplexer.streams, s.identifier) 445 s.multiplexer.streamLock.Unlock() 446 }) 447 448 // Done. 449 return 450 } 451 452 // Close implements net.Conn.Close. Subsequent calls to Close are no-ops and 453 // will return nil. 454 func (s *Stream) Close() error { 455 return s.close(true) 456 } 457 458 // LocalAddr implements net.Conn.LocalAddr. 459 func (s *Stream) LocalAddr() net.Addr { 460 return &streamAddress{identifier: s.identifier} 461 } 462 463 // RemoteAddr implements net.Conn.RemoteAddr. 464 func (s *Stream) RemoteAddr() net.Addr { 465 return &streamAddress{remote: true, identifier: s.identifier} 466 } 467 468 // SetDeadline implements net.Conn.SetDeadline. 469 func (s *Stream) SetDeadline(deadline time.Time) error { 470 // Set the read deadline. 471 if err := s.SetReadDeadline(deadline); err != nil { 472 return fmt.Errorf("unable to set read deadline: %w", err) 473 } 474 475 // Set the write deadline. 476 if err := s.SetWriteDeadline(deadline); err != nil { 477 return fmt.Errorf("unable to set write deadline: %w", err) 478 } 479 480 // Success. 481 return nil 482 } 483 484 // setStreamDeadline is an internal deadline update function for setting read 485 // and write deadlines for streams. It must only be called by the holder of the 486 // respective timer. 487 func setStreamDeadline(timer *time.Timer, expired *bool, deadline time.Time) { 488 // Ensure that the timer is stopped and drained. We don't know its previous 489 // state (it may have expired without anyone seeing it or may have been 490 // stopped and drained previously), so we perform a non-blocking drain if 491 // it's already stopped or expired. We do know that no drain is necessary if 492 // the timer is successfully stopped while active, because we never reset a 493 // timer without draining it first. 494 if !timer.Stop() { 495 select { 496 case <-timer.C: 497 default: 498 } 499 } 500 501 // Handle the update based on the deadline time. 502 if deadline.IsZero() { 503 *expired = false 504 } else if duration := time.Until(deadline); duration <= 0 { 505 *expired = true 506 } else { 507 timer.Reset(duration) 508 } 509 } 510 511 // SetReadDeadline implements net.Conn.SetReadDeadline. 512 func (s *Stream) SetReadDeadline(deadline time.Time) error { 513 // Block until the read deadline is set (by us or its current holder) or 514 // until the stream is closed for reading (at which point the read deadline 515 // timer is taken out of circulation). 516 select { 517 case readDeadlineTimer := <-s.readDeadline: 518 setStreamDeadline(readDeadlineTimer, &s.readDeadlineExpired, deadline) 519 s.readDeadline <- readDeadlineTimer 520 return nil 521 case s.readDeadlineSet <- deadline: 522 return nil 523 case <-s.closed: 524 return net.ErrClosed 525 } 526 } 527 528 // SetWriteDeadline implements net.Conn.SetWriteDeadline. 529 func (s *Stream) SetWriteDeadline(deadline time.Time) error { 530 // Poll until the write deadline is set (by us or its current holder) or 531 // until the stream is closed for writing (at which point the write deadline 532 // timer is taken out of circulation). 533 select { 534 case writeDeadlineTimer := <-s.writeDeadline: 535 setStreamDeadline(writeDeadlineTimer, &s.writeDeadlineExpired, deadline) 536 s.writeDeadline <- writeDeadlineTimer 537 return nil 538 case s.writeDeadlineSet <- deadline: 539 return nil 540 case <-s.closedWrite: 541 return ErrWriteClosed 542 } 543 }