return OpenStreamSync calls in the order they were called

This commit is contained in:
Marten Seemann
2019-06-05 23:46:19 +08:00
parent e7f20d427b
commit 061b92ea5c
4 changed files with 308 additions and 111 deletions

View File

@@ -11,7 +11,8 @@ import (
//go:generate genny -in $GOFILE -out streams_map_outgoing_uni.go gen "item=sendStreamI Item=UniStream streamTypeGeneric=protocol.StreamTypeUni"
type outgoingItemsMap struct {
mutex sync.RWMutex
cond sync.Cond
openQueue []chan struct{}
streams map[protocol.StreamNum]item
@@ -29,15 +30,13 @@ func newOutgoingItemsMap(
newStream func(protocol.StreamNum) item,
queueControlFrame func(wire.Frame),
) *outgoingItemsMap {
m := &outgoingItemsMap{
return &outgoingItemsMap{
streams: make(map[protocol.StreamNum]item),
maxStream: protocol.InvalidStreamNum,
nextStream: 1,
newStream: newStream,
queueStreamIDBlocked: func(f *wire.StreamsBlockedFrame) { queueControlFrame(f) },
}
m.cond.L = &m.mutex
return m
}
func (m *outgoingItemsMap) OpenStream() (item, error) {
@@ -48,51 +47,70 @@ func (m *outgoingItemsMap) OpenStream() (item, error) {
return nil, m.closeErr
}
str, err := m.openStreamImpl()
if err != nil {
return nil, streamOpenErr{err}
// if there are OpenStreamSync calls waiting, return an error here
if len(m.openQueue) > 0 || m.nextStream > m.maxStream {
m.maybeSendBlockedFrame()
return nil, streamOpenErr{errTooManyOpenStreams}
}
return str, nil
return m.openStream(), nil
}
func (m *outgoingItemsMap) OpenStreamSync() (item, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
if m.closeErr != nil {
return nil, m.closeErr
}
if len(m.openQueue) == 0 && m.nextStream <= m.maxStream {
return m.openStream(), nil
}
waitChan := make(chan struct{}, 1)
m.openQueue = append(m.openQueue, waitChan)
m.maybeSendBlockedFrame()
for {
m.mutex.Unlock()
<-waitChan
m.mutex.Lock()
if m.closeErr != nil {
return nil, m.closeErr
}
str, err := m.openStreamImpl()
if err == nil {
return str, nil
if m.nextStream > m.maxStream {
// no stream available. Continue waiting
continue
}
if err != nil && err != errTooManyOpenStreams {
return nil, streamOpenErr{err}
}
m.cond.Wait()
str := m.openStream()
m.openQueue = m.openQueue[1:]
m.unblockOpenSync()
return str, nil
}
}
func (m *outgoingItemsMap) openStreamImpl() (item, error) {
if m.nextStream > m.maxStream {
if !m.blockedSent {
var streamNum protocol.StreamNum
if m.maxStream != protocol.InvalidStreamNum {
streamNum = m.maxStream
}
m.queueStreamIDBlocked(&wire.StreamsBlockedFrame{
Type: streamTypeGeneric,
StreamLimit: streamNum,
})
m.blockedSent = true
}
return nil, errTooManyOpenStreams
}
func (m *outgoingItemsMap) openStream() item {
s := m.newStream(m.nextStream)
m.streams[m.nextStream] = s
m.nextStream++
return s, nil
return s
}
func (m *outgoingItemsMap) maybeSendBlockedFrame() {
if m.blockedSent {
return
}
var streamNum protocol.StreamNum
if m.maxStream != protocol.InvalidStreamNum {
streamNum = m.maxStream
}
m.queueStreamIDBlocked(&wire.StreamsBlockedFrame{
Type: streamTypeGeneric,
StreamLimit: streamNum,
})
m.blockedSent = true
}
func (m *outgoingItemsMap) GetStream(num protocol.StreamNum) (item, error) {
@@ -125,12 +143,24 @@ func (m *outgoingItemsMap) DeleteStream(num protocol.StreamNum) error {
func (m *outgoingItemsMap) SetMaxStream(num protocol.StreamNum) {
m.mutex.Lock()
if num > m.maxStream {
m.maxStream = num
m.blockedSent = false
m.cond.Broadcast()
defer m.mutex.Unlock()
if num <= m.maxStream {
return
}
m.maxStream = num
m.blockedSent = false
m.unblockOpenSync()
}
func (m *outgoingItemsMap) unblockOpenSync() {
if len(m.openQueue) == 0 {
return
}
select {
case m.openQueue[0] <- struct{}{}:
default:
}
m.mutex.Unlock()
}
func (m *outgoingItemsMap) CloseWithError(err error) {
@@ -139,6 +169,8 @@ func (m *outgoingItemsMap) CloseWithError(err error) {
for _, str := range m.streams {
str.closeForShutdown(err)
}
m.cond.Broadcast()
for _, c := range m.openQueue {
close(c)
}
m.mutex.Unlock()
}