github.com/sagernet/quic-go@v0.43.1-beta.1/ech/send_queue.go (about)

     1  package quic
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/sagernet/quic-go/internal/protocol"
     7  )
     8  
     9  type sender interface {
    10  	Send(p *packetBuffer, gsoSize uint16, ecn protocol.ECN)
    11  	Run() error
    12  	WouldBlock() bool
    13  	Available() <-chan struct{}
    14  	Close()
    15  }
    16  
    17  type queueEntry struct {
    18  	buf     *packetBuffer
    19  	gsoSize uint16
    20  	ecn     protocol.ECN
    21  }
    22  
    23  type sendQueue struct {
    24  	queue       chan queueEntry
    25  	closeCalled chan struct{} // runStopped when Close() is called
    26  	runStopped  chan struct{} // runStopped when the run loop returns
    27  	available   chan struct{}
    28  	conn        sendConn
    29  }
    30  
    31  var _ sender = &sendQueue{}
    32  
    33  const sendQueueCapacity = 8
    34  
    35  func newSendQueue(conn sendConn) sender {
    36  	return &sendQueue{
    37  		conn:        conn,
    38  		runStopped:  make(chan struct{}),
    39  		closeCalled: make(chan struct{}),
    40  		available:   make(chan struct{}, 1),
    41  		queue:       make(chan queueEntry, sendQueueCapacity),
    42  	}
    43  }
    44  
    45  // Send sends out a packet. It's guaranteed to not block.
    46  // Callers need to make sure that there's actually space in the send queue by calling WouldBlock.
    47  // Otherwise Send will panic.
    48  func (h *sendQueue) Send(p *packetBuffer, gsoSize uint16, ecn protocol.ECN) {
    49  	select {
    50  	case h.queue <- queueEntry{buf: p, gsoSize: gsoSize, ecn: ecn}:
    51  		// clear available channel if we've reached capacity
    52  		if len(h.queue) == sendQueueCapacity {
    53  			select {
    54  			case <-h.available:
    55  			default:
    56  			}
    57  		}
    58  	case <-h.runStopped:
    59  	default:
    60  		panic("sendQueue.Send would have blocked")
    61  	}
    62  }
    63  
    64  func (h *sendQueue) WouldBlock() bool {
    65  	return len(h.queue) == sendQueueCapacity
    66  }
    67  
    68  func (h *sendQueue) Available() <-chan struct{} {
    69  	return h.available
    70  }
    71  
    72  func (h *sendQueue) Run() error {
    73  	defer close(h.runStopped)
    74  	var shouldClose bool
    75  	for {
    76  		if shouldClose && len(h.queue) == 0 {
    77  			return nil
    78  		}
    79  		select {
    80  		case <-h.closeCalled:
    81  			h.closeCalled = nil // prevent this case from being selected again
    82  			// make sure that all queued packets are actually sent out
    83  			shouldClose = true
    84  		case e := <-h.queue:
    85  			if err := h.conn.Write(e.buf.Data, e.gsoSize, e.ecn); err != nil {
    86  				// This additional check enables:
    87  				// 1. Checking for "datagram too large" message from the kernel, as such,
    88  				// 2. Path MTU discovery,and
    89  				// 3. Eventual detection of loss PingFrame.
    90  				var tooLarge *DatagramTooLargeError
    91  				if !isSendMsgSizeErr(err) && !errors.As(err, &tooLarge) {
    92  					return err
    93  				}
    94  			}
    95  			e.buf.Release()
    96  			select {
    97  			case h.available <- struct{}{}:
    98  			default:
    99  			}
   100  		}
   101  	}
   102  }
   103  
   104  func (h *sendQueue) Close() {
   105  	close(h.closeCalled)
   106  	// wait until the run loop returned
   107  	<-h.runStopped
   108  }