forked from quic-go/quic-go
committed by
Lucas Clemente
parent
15352e9591
commit
416e3f9e2e
@@ -14,7 +14,8 @@ type streamsMap struct {
|
|||||||
streams map[protocol.StreamID]*stream
|
streams map[protocol.StreamID]*stream
|
||||||
openStreams []protocol.StreamID
|
openStreams []protocol.StreamID
|
||||||
|
|
||||||
highestStreamOpenedByClient protocol.StreamID
|
highestStreamOpenedByClient protocol.StreamID
|
||||||
|
streamsOpenedAfterLastGarbageCollect int
|
||||||
|
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
newStream newStreamLambda
|
newStream newStreamLambda
|
||||||
@@ -50,6 +51,7 @@ func (m *streamsMap) GetOrOpenStream(id protocol.StreamID) (*stream, error) {
|
|||||||
if ok {
|
if ok {
|
||||||
return s, nil // s may be nil
|
return s, nil // s may be nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... we don't have an existing stream, try opening a new one
|
// ... we don't have an existing stream, try opening a new one
|
||||||
m.mutex.Lock()
|
m.mutex.Lock()
|
||||||
defer m.mutex.Unlock()
|
defer m.mutex.Unlock()
|
||||||
@@ -77,6 +79,11 @@ func (m *streamsMap) GetOrOpenStream(id protocol.StreamID) (*stream, error) {
|
|||||||
m.highestStreamOpenedByClient = id
|
m.highestStreamOpenedByClient = id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m.streamsOpenedAfterLastGarbageCollect++
|
||||||
|
if m.streamsOpenedAfterLastGarbageCollect%protocol.MaxNewStreamIDDelta == 0 {
|
||||||
|
m.garbageCollectClosedStreams()
|
||||||
|
}
|
||||||
|
|
||||||
m.putStream(s)
|
m.putStream(s)
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
@@ -182,8 +189,8 @@ func (m *streamsMap) NumberOfStreams() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// garbageCollectClosedStreams deletes nil values in the streams if they are smaller than protocol.MaxNewStreamIDDelta than the highest stream opened by the client
|
// garbageCollectClosedStreams deletes nil values in the streams if they are smaller than protocol.MaxNewStreamIDDelta than the highest stream opened by the client
|
||||||
|
// note that this garbage collection is relatively expensive, since it iterates over the whole streams map. It should not be called every time a stream is openend or closed
|
||||||
func (m *streamsMap) garbageCollectClosedStreams() {
|
func (m *streamsMap) garbageCollectClosedStreams() {
|
||||||
m.mutex.Lock()
|
|
||||||
for id, str := range m.streams {
|
for id, str := range m.streams {
|
||||||
if str != nil {
|
if str != nil {
|
||||||
continue
|
continue
|
||||||
@@ -192,5 +199,5 @@ func (m *streamsMap) garbageCollectClosedStreams() {
|
|||||||
delete(m.streams, id)
|
delete(m.streams, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m.mutex.Unlock()
|
m.streamsOpenedAfterLastGarbageCollect = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,6 +142,26 @@ var _ = Describe("Streams Map", func() {
|
|||||||
Expect(m.streams).To(HaveKey(protocol.StreamID(23)))
|
Expect(m.streams).To(HaveKey(protocol.StreamID(23)))
|
||||||
Expect(m.streams[23]).ToNot(BeNil())
|
Expect(m.streams[23]).ToNot(BeNil())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("runs garbage-collection after a bunch of streams have been opened", func() {
|
||||||
|
numGarbageCollections := 0
|
||||||
|
numSavedStreams := 0
|
||||||
|
for i := 1; i < 4*protocol.MaxNewStreamIDDelta; i += 2 {
|
||||||
|
streamID := protocol.StreamID(i)
|
||||||
|
_, err := m.GetOrOpenStream(streamID)
|
||||||
|
Expect(m.highestStreamOpenedByClient).To(Equal(streamID))
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
err = m.RemoveStream(streamID)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
if len(m.streams) != numSavedStreams+1 {
|
||||||
|
numGarbageCollections++
|
||||||
|
}
|
||||||
|
numSavedStreams = len(m.streams)
|
||||||
|
}
|
||||||
|
Expect(numGarbageCollections).ToNot(BeZero())
|
||||||
|
Expect(numGarbageCollections).To(BeNumerically("<", 4))
|
||||||
|
Expect(len(m.streams)).To(BeNumerically("<", 2*protocol.MaxNewStreamIDDelta))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user