Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stop generating new packets when the send queue is full #2971

Merged
merged 2 commits into from
Jan 17, 2021

Conversation

marten-seemann
Copy link
Member

@marten-seemann marten-seemann commented Dec 31, 2020

Fixes #2941. Depends on #2980.

We shouldn't block the run loop in the session if WriteTo is too slow to send out packets. If we do that, we won't be able to receive any incoming packets, and the receivedPackets channel might overflow, leading to avoidable packet loss.

session.go Outdated
// nothing to see here.
// We do all the interesting stuff after the switch statement, so
// nothing to see here.
case <-sendQueueAvailable:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to drain sendQueueAvailable if we end up taking another path in this switch, and then enter the if checks below? Otherwise it could be that we incorrectly assume there's space available even if there isn't, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The select can unblock for multiple reasons (sending scheduled, receiving a packet, etc.), so we always have to check if there's room in the send queue before sending a packet: https://github.com/lucas-clemente/quic-go/blob/b4ffb97831546fc8336ed5f0c37ed6b6c8c40246/session.go#L621-L629

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I misunderstood your comment. Draining the channel makes sense if WouldBlock() returns true.

Copy link
Member Author

@marten-seemann marten-seemann Jan 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, that would be racy (the send queue could have unblocked between the calls to WouldBlock and Available.

type sendQueue struct {
queue chan *packetBuffer
closeCalled chan struct{} // runStopped when Close() is called
runStopped chan struct{} // runStopped when the run loop returns
available chan struct{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit difficult to follow, and tightly coupled to run loop internals (e.g. if you use it wrong it's inherently racy, see below).

I wonder whether there's a simpler way of achieving similar behavior? Not sure if it's simpler, but possibly a nextPacket member in the session that we try to send in the session's switch statement?

(If we decide to stick with this, we should probably document the reasoning behind this API.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand why it's inherently race. It's true that the sendQueue can only be used from a single Go routine (which we guarantee by just running it from the session), but as long as you adhere to this, there shouldn't be any race here.

@marten-seemann marten-seemann force-pushed the send-queue-non-blocking branch 2 times, most recently from de0da41 to 3b37e44 Compare January 4, 2021 07:33
@codecov
Copy link

codecov bot commented Jan 4, 2021

Codecov Report

Merging #2971 (b81a6f8) into master (e9848fa) will increase coverage by 0.02%.
The diff coverage is 95.83%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2971      +/-   ##
==========================================
+ Coverage   86.06%   86.08%   +0.02%     
==========================================
  Files         135      135              
  Lines        9415     9435      +20     
==========================================
+ Hits         8103     8122      +19     
- Misses        953      954       +1     
  Partials      359      359              
Impacted Files Coverage Δ
send_queue.go 96.67% <90.00%> (-3.33%) ⬇️
session.go 77.62% <100.00%> (+0.30%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e9848fa...b81a6f8. Read the comment docs.

@marten-seemann marten-seemann force-pushed the send-queue-non-blocking branch 2 times, most recently from a90c6e5 to dd13de1 Compare January 5, 2021 03:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

run loop is blocked by sending packets
2 participants