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 }