forked from quic-go/quic-go
gracefully handle concurrent stream writes and cancellations
If the complete slice passed to Stream.Write() is sent out, and the stream is canceled concurrently (either by calling Stream.CancelWrite() or by receiving a STOP_SENDING frame), we don't need to return an error for the Write() call.
This commit is contained in:
@@ -173,6 +173,9 @@ func (s *sendStream) Write(p []byte) (int, error) {
|
||||
s.mutex.Lock()
|
||||
}
|
||||
|
||||
if bytesWritten == len(p) {
|
||||
return bytesWritten, nil
|
||||
}
|
||||
if s.closeForShutdownErr != nil {
|
||||
return bytesWritten, s.closeForShutdownErr
|
||||
} else if s.cancelWriteErr != nil {
|
||||
|
||||
@@ -695,6 +695,32 @@ var _ = Describe("Send Stream", func() {
|
||||
str.CancelWrite(9876)
|
||||
})
|
||||
|
||||
// This test is inherently racy, as it tests a concurrent call to Write() and CancelRead().
|
||||
// A single successful run of this test therefore doesn't mean a lot,
|
||||
// for reliable results it has to be run many times.
|
||||
It("returns a nil error when the whole slice has been sent out", func() {
|
||||
mockSender.EXPECT().queueControlFrame(gomock.Any()).MaxTimes(1)
|
||||
mockSender.EXPECT().onHasStreamData(streamID).MaxTimes(1)
|
||||
mockSender.EXPECT().onStreamCompleted(streamID).MaxTimes(1)
|
||||
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).MaxTimes(1)
|
||||
mockFC.EXPECT().AddBytesSent(gomock.Any()).MaxTimes(1)
|
||||
errChan := make(chan error)
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
n, err := strWithTimeout.Write(getData(100))
|
||||
if n == 0 {
|
||||
errChan <- nil
|
||||
return
|
||||
}
|
||||
errChan <- err
|
||||
}()
|
||||
|
||||
runtime.Gosched()
|
||||
go str.popStreamFrame(protocol.MaxByteCount)
|
||||
go str.CancelWrite(1234)
|
||||
Eventually(errChan).Should(Receive(Not(HaveOccurred())))
|
||||
})
|
||||
|
||||
It("unblocks Write", func() {
|
||||
mockSender.EXPECT().queueControlFrame(gomock.Any())
|
||||
mockSender.EXPECT().onHasStreamData(streamID)
|
||||
|
||||
Reference in New Issue
Block a user