github.com/tumi8/quic-go@v0.37.4-tum/send_queue.go (about)

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