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 }