-
Notifications
You must be signed in to change notification settings - Fork 74
Make writes non-blocking #242
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
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2d58f23 to
3a2a3c2
Compare
Formerly, writes were checking the size of the write queue, and if there are no items in it, were writing directly to the socket. That is against the nature of the non-blocking API we provide. Also, it restains us from optimizing the writes for non-blocking usage (multiple client messages can be concatenated into single buffer before writing to the socket to decrease the number of send calls). To solve that problem, connections are adding buffers into the write queue. Reactor thread pops elements from that queue and writes into the socket. (The optimization I mentioned above can be implemented later). Of course, this approach will kill the performance of blocking usage since the reactor thread will be in sleep most of the time on asyncore.loop call. We need to wake the reactor thread each time we want to write something if it is not awake. To do this, a pipe is added to the reactor's dispatchers map. Each time we want to write something, we check whether or not it is awake and if not, we write a single byte into the pipe. That wakes the reactor thread and makes it process the message we added to its write queue. The pipe works great on UNIX, but pipes are not selectable or pollable on Windows. We fall back into sockets in case the client is running on Windows. On the reactor, we try to create the loop, check if the added waker (pipe or socket) works or not. If it does, we use the wakable loop. If not, we fall back to the loop that does busy waiting.
It may be the case that client is disconnected while we are in the loop, an no data is sent. In that case, we were adding the data to the write queue again, and try to pop from it within the same loop. That was causing an infinite loop. An early return is added to solve this problem. When disconnected, dispatcher will be closed, and handle_write won't be called again by the asyncore.
3a2a3c2 to
c6685c0
Compare
314c1ce to
980a526
Compare
puzpuzpuz
reviewed
Nov 17, 2020
puzpuzpuz
approved these changes
Nov 17, 2020
puzpuzpuz
left a comment
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.
LGTM in general. Left some comments for further discussion, but there is nothing major
puzpuzpuz
reviewed
Nov 18, 2020
puzpuzpuz
reviewed
Nov 18, 2020
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Formerly, writes were checking the size of the write queue, and
if there are no items in it, were writing directly to the socket.
That is against the nature of the non-blocking API we provide.
Also, it restains us from optimizing the writes for non-blocking
usage (multiple client messages can be concatenated into single
buffer before writing to the socket to decrease the number of
send calls).
To solve that problem, connections are adding buffers into the
write queue. Reactor thread pops elements from that queue and
writes into the socket. (The optimization I mentioned above
can be implemented later). Of course, this approach will kill the
performance of blocking usage since the reactor thread will be
in sleep most of the time on asyncore.loop call. We need to wake
the reactor thread each time we want to write something if it is
not awake.
To do this, a pipe is added to the reactor's dispatchers map. Each
time we want to write something, we check whether or not it is awake
and if not, we write a single byte into the pipe. That wakes the
reactor thread and makes it process the message we added to its
write queue. The pipe works great on UNIX, but pipes are not
selectable or pollable on Windows. We fall back into sockets in case
the client is running on Windows.
On the reactor, we try to create the loop, check if the added waker
(pipe or socket) works or not. If it does, we use the wakable loop.
If not, we fall back to the loop that does busy waiting.