github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/send_queue.go (about) 1 package quic 2 3 import "github.com/daeuniverse/quic-go/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 }