github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/quic/gquic-go/window_update_queue.go (about)

     1  package gquic
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/quic/gquic-go/internal/flowcontrol"
     7  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/quic/gquic-go/internal/protocol"
     8  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/quic/gquic-go/internal/wire"
     9  )
    10  
    11  type windowUpdateQueue struct {
    12  	mutex sync.Mutex
    13  
    14  	queue      map[protocol.StreamID]bool // used as a set
    15  	queuedConn bool                       // connection-level window update
    16  
    17  	streamGetter       streamGetter
    18  	connFlowController flowcontrol.ConnectionFlowController
    19  	callback           func(wire.Frame)
    20  }
    21  
    22  func newWindowUpdateQueue(
    23  	streamGetter streamGetter,
    24  	connFC flowcontrol.ConnectionFlowController,
    25  	cb func(wire.Frame),
    26  ) *windowUpdateQueue {
    27  	return &windowUpdateQueue{
    28  		queue:              make(map[protocol.StreamID]bool),
    29  		streamGetter:       streamGetter,
    30  		connFlowController: connFC,
    31  		callback:           cb,
    32  	}
    33  }
    34  
    35  func (q *windowUpdateQueue) AddStream(id protocol.StreamID) {
    36  	q.mutex.Lock()
    37  	q.queue[id] = true
    38  	q.mutex.Unlock()
    39  }
    40  
    41  func (q *windowUpdateQueue) AddConnection() {
    42  	q.mutex.Lock()
    43  	q.queuedConn = true
    44  	q.mutex.Unlock()
    45  }
    46  
    47  func (q *windowUpdateQueue) QueueAll() {
    48  	q.mutex.Lock()
    49  	// queue a connection-level window update
    50  	if q.queuedConn {
    51  		q.callback(&wire.MaxDataFrame{ByteOffset: q.connFlowController.GetWindowUpdate()})
    52  		q.queuedConn = false
    53  	}
    54  	// queue all stream-level window updates
    55  	for id := range q.queue {
    56  		str, err := q.streamGetter.GetOrOpenReceiveStream(id)
    57  		if err != nil || str == nil { // the stream can be nil if it was completed before dequeing the window update
    58  			continue
    59  		}
    60  		offset := str.getWindowUpdate()
    61  		if offset == 0 { // can happen if we received a final offset, right after queueing the window update
    62  			continue
    63  		}
    64  		q.callback(&wire.MaxStreamDataFrame{
    65  			StreamID:   id,
    66  			ByteOffset: offset,
    67  		})
    68  		delete(q.queue, id)
    69  	}
    70  	q.mutex.Unlock()
    71  }