-
-
Notifications
You must be signed in to change notification settings - Fork 418
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
Simplify buffering in TCPConnection #3185
Conversation
74ec837
to
68a82a3
Compare
@@ -657,7 +648,7 @@ actor TCPConnection | |||
var bytes_to_send: USize = 0 | |||
var bytes_sent: USize = 0 | |||
while _writeable and (_pending_writev_total > 0) do | |||
if bytes_sent >= _max_size then | |||
if bytes_sent >= _read_buffer_size then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I opened an issue #3186 to address this issue. I think it should be a different PR.
@@ -775,16 +766,13 @@ actor TCPConnection | |||
This occurs only with IOCP on Windows. | |||
""" | |||
ifdef windows then | |||
match len.usize() | |||
| 0 => | |||
if len.usize() == 0 then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
seems like this usize() probably isn't needed but I wanted to do the minimal change.
@@ -879,8 +861,8 @@ actor TCPConnection | |||
end | |||
end | |||
|
|||
if sum >= _max_size then | |||
// If we've read _max_size, yield and read again later. | |||
if sum >= _read_buffer_size then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should consider freeing "yield after" from buffer size in which case #3186 should be updated.
Can you please add a link to PR #3178 in the commit and PR description? |
Prior to this commit, the buffering in TCPConnection was overly complicated. This was a carry over from the old style of buffer handling. However, after Dipin Hora's commits to bring over the "faster tcp" changes from Wallaroo, the buffer handling no longer makes sense. Prior to this commit, a TCPConnection would be provided two variables to control read buffering: init_size, max_size. The read buffer would start at init_size and would grow to max_size. Before "faster tcp", the buffer would only grow in size if we read the entire buffer's worth of data in one recv call. After the data was read, the buffer would be truncated and then handed off to the TCPConnectionNotify. This was very wasteful. If you had a buffer of 128 allocated bytes and only read 4, then the 124 allocated bytes would be truncated away and the buffer would be reallocated. With "faster tcp", we allocate a large buffer and when reading data, we chop it off the buffer as needed. This means, that "grow the buffer" doesn't make much sense anymore. The buffer will continually shrink in size as we slowly chop more and more off. This meant that with the code as it is prior to change, we would always grow to max buffer size. If we are always going to grow to max buffer size, it makes sense to simplify the code and our interfaces and only allow for setting the buffer size. This commit makes that simplication. Now we will: - allocate a buffer of size `read_buffer_size` - reallocate when the buffer is empty - reallocate if the caller is using `expect` and the buffer is too small to hold the expected payload This is a breaking change for TPCConnection and TCPListener as it changes the constructor interfaces. Additional information on the "faster tcp" change can be found in the following PR: #3178
68a82a3
to
2f9fdb2
Compare
@jemc comment amened. |
@SeanTAllen it seems like this change cannot handle the scenario when Can you please clarify/confirm? |
Correct. Expect is capped at whatever the buffer size is. |
And I forgot to open a PR for that change to expect. |
That seems like a significant functional change beyond the "simplify buffering" as the title of this PR suggests. |
I thought I had already opened a PR to address |
Okey dokey. I'll wait to see that PR. Thanks. |
The core change there is fun ref expect(qty: USize = 0) ?=>
"""
A `received` call on the notifier must contain exactly `qty` bytes. If
`qty` is zero, the call can contain any amount of data. This has no effect
if called in the `sent` notifier callback.
"""
if qty <= _read_buffer_size then
if not _in_sent then
_expect = _notify.expect(this, qty)
_read_buf_size()
end
else
error
end |
Prior to this commit, `expect` could be used to create a denial of service on a Pony application. If you used expect to handle a framed protocol, then, you'd by default accept any sized `except` value which could result in huge amounts of memory being allocated. Memory that could vastly outstrip the max buffer size set for controlling memory usage. After this commit, `expect` is partial and will result in an error if attempting to set an expect value that is greater than our max read buffer size. I intended to commit this prior to another PR, but forgot and thought that I had. As it is, this PR works in conjunction with #3185 to prevent expect from being passed that is greater than the max buffer size thereby deadlocking the logic post #3185.
Prior to this commit, `expect` could be used to create a denial of service on a Pony application. If you used expect to handle a framed protocol, then, you'd by default accept any sized `except` value which could result in huge amounts of memory being allocated. Memory that could vastly outstrip the max buffer size set for controlling memory usage. After this commit, `expect` is partial and will result in an error if attempting to set an expect value that is greater than our max read buffer size. I intended to commit this prior to another PR, but forgot and thought that I had. As it is, this PR works in conjunction with #3185 to prevent expect from being passed that is greater than the max buffer size thereby deadlocking the logic post #3185.
Prior to this commit, `expect` could be used to create a denial of service on a Pony application. If you used expect to handle a framed protocol, then, you'd by default accept any sized `except` value which could result in huge amounts of memory being allocated. Memory that could vastly outstrip the max buffer size set for controlling memory usage. After this commit, `expect` is partial and will result in an error if attempting to set an expect value that is greater than our max read buffer size. I intended to commit this prior to another PR, but forgot and thought that I had. As it is, this PR works in conjunction with #3185 to prevent expect from being passed that is greater than the max buffer size thereby deadlocking the logic post #3185.
Prior to this commit, `expect` could be used to create a denial of service on a Pony application. If you used expect to handle a framed protocol, then, you'd by default accept any sized `except` value which could result in huge amounts of memory being allocated. Memory that could vastly outstrip the max buffer size set for controlling memory usage. After this commit, `expect` is partial and will result in an error if attempting to set an expect value that is greater than our max read buffer size. I intended to commit this prior to another PR, but forgot and thought that I had. As it is, this PR works in conjunction with #3185 to prevent expect from being passed that is greater than the max buffer size thereby deadlocking the logic post #3185.
Prior to this commit, `expect` could be used to create a denial of service on a Pony application. If you used expect to handle a framed protocol, then, you'd by default accept any sized `except` value which could result in huge amounts of memory being allocated. Memory that could vastly outstrip the max buffer size set for controlling memory usage. After this commit, `expect` is partial and will result in an error if attempting to set an expect value that is greater than our max read buffer size. I intended to commit this prior to another PR, but forgot and thought that I had. As it is, this PR works in conjunction with ponylang#3185 to prevent expect from being passed that is greater than the max buffer size thereby deadlocking the logic post ponylang#3185.
Prior to this commit, the buffering in TCPConnection was overly
complicated. This was a carry over from the old style of buffer
handling. However, after Dipin Hora's commits to bring over the "faster
tcp" changes from Wallaroo, the buffer handling no longer makes sense.
Prior to this commit, a TCPConnection would be provided two variables to
control read buffering: init_size, max_size. The read buffer would start
at init_size and would grow to max_size.
Before "faster tcp", the buffer would only grow in size if we read the
entire buffer's worth of data in one recv call. After the data was read,
the buffer would be truncated and then handed off to the
TCPConnectionNotify. This was very wasteful. If you had a buffer of 128
allocated bytes and only read 4, then the 124 allocated bytes would be
truncated away and the buffer would be reallocated.
With "faster tcp", we allocate a large buffer and when reading data, we
chop it off the buffer as needed. This means, that "grow the buffer"
doesn't make much sense anymore. The buffer will continually shrink in
size as we slowly chop more and more off. This meant that with the code
as it is prior to change, we would always grow to max buffer size.
If we are always going to grow to max buffer size, it makes sense to
simplify the code and our interfaces and only allow for setting the
buffer size. This commit makes that simplication. Now we will:
read_buffer_size
expect
and the buffer is too smallto hold the expected payload
This is a breaking change for TPCConnection and TCPListener as it
changes the constructor interfaces.