github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/http3/datagram.go (about) 1 package http3 2 3 import ( 4 "context" 5 "sync" 6 ) 7 8 const maxQuarterStreamID = 1<<60 - 1 9 10 const streamDatagramQueueLen = 32 11 12 type datagrammer struct { 13 sendDatagram func([]byte) error 14 15 hasData chan struct{} 16 queue [][]byte // TODO: use a ring buffer 17 18 mx sync.Mutex 19 sendErr error 20 receiveErr error 21 } 22 23 func newDatagrammer(sendDatagram func([]byte) error) *datagrammer { 24 return &datagrammer{ 25 sendDatagram: sendDatagram, 26 hasData: make(chan struct{}, 1), 27 } 28 } 29 30 func (d *datagrammer) SetReceiveError(err error) (isDone bool) { 31 d.mx.Lock() 32 defer d.mx.Unlock() 33 34 d.receiveErr = err 35 d.signalHasData() 36 return d.sendErr != nil 37 } 38 39 func (d *datagrammer) SetSendError(err error) (isDone bool) { 40 d.mx.Lock() 41 defer d.mx.Unlock() 42 43 d.sendErr = err 44 return d.receiveErr != nil 45 } 46 47 func (d *datagrammer) Send(b []byte) error { 48 d.mx.Lock() 49 sendErr := d.sendErr 50 d.mx.Unlock() 51 if sendErr != nil { 52 return sendErr 53 } 54 55 return d.sendDatagram(b) 56 } 57 58 func (d *datagrammer) signalHasData() { 59 select { 60 case d.hasData <- struct{}{}: 61 default: 62 } 63 } 64 65 func (d *datagrammer) enqueue(data []byte) { 66 d.mx.Lock() 67 defer d.mx.Unlock() 68 69 if d.receiveErr != nil { 70 return 71 } 72 if len(d.queue) >= streamDatagramQueueLen { 73 return 74 } 75 d.queue = append(d.queue, data) 76 d.signalHasData() 77 } 78 79 func (d *datagrammer) Receive(ctx context.Context) ([]byte, error) { 80 start: 81 d.mx.Lock() 82 if len(d.queue) >= 1 { 83 data := d.queue[0] 84 d.queue = d.queue[1:] 85 d.mx.Unlock() 86 return data, nil 87 } 88 if d.receiveErr != nil { 89 d.mx.Unlock() 90 return nil, d.receiveErr 91 } 92 d.mx.Unlock() 93 94 select { 95 case <-ctx.Done(): 96 return nil, context.Cause(ctx) 97 case <-d.hasData: 98 } 99 goto start 100 }