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