github.com/danielpfeifer02/quic-go-prio-packs@v0.41.0-28/send_queue.go (about)

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