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  }