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

Improve wait callback and timeout handling #110

Merged
merged 5 commits into from
Aug 4, 2023

Conversation

jhawthorn
Copy link
Member

This makes a couple improvements to how we wait for the socket to become ready for I/O.

First this refactors TRILOGY_RB_TIMEOUT to a "real" error status in the C library (though currently nothing in the C library raises it), which allows it to be negative like all the other errors (and avoids being confused with a successful read/write of size 1).

Next this makes _cb_ruby_wait return this new status code instead of SYSERR when there is a timeout, allowing it to propogate through trilogy_sock_upgrade_ssl and similar correctly. This allows differentiating between actually syserrors which set errno and timeouts. Also this can show the difference between rb_wait_for_single_fd returning -1 (a syscall error, which I think in practice will be extremely rare) and 0 (a timeout).

Finally the last two commits ensure that the socket is shut down on either a socket timeout we see, or from an external exception (like Timeout.timeout). For the latter we must wrap the waiting in an rb_protect, which previously we were doing correctly for queries, but not for other operations (ex. ping, change_db). We have to shut down the socket because we've interrupted normal control flow and are likely either in the middle of a write (we've partly written our packet) or a read (there will be data sent from the server that we need to handle) so any further operations are invalid.

All other errors are negative so that they can be returned from
functions like read/write which return a positive number for success.
This allows handling and raising TimeoutError in the same way as our
other errors.

This also allows for us to report syscall errors which occur in the
socket wait callback (which I believe are very unlikely).
When we call rb_wait_for_single_fd we release the GVL and allow our
Ruby thread to receive interrupts. The most obvious case of this is
using Timeout.timeout.

If we see an exception in this way we can't safely use our socket
anymore as it was likely in the middle of another operation, so it
should be shut down.
return TRILOGY_SYSERR;
if (args.rc == 0)
Copy link
Contributor

Choose a reason for hiding this comment

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

I got confused for a second because in most other places rc == 0 means TRILOGY_OK. I understand now that this is the return value of rb_wait_for_single_fd, where 0 means a timeout: https://github.com/ruby/ruby/blob/1642e0c39220e95ddb16b4cbbbe78f24507dfd48/include/ruby/io.h#L902. Possibly worth an inline comment in addition to what you have in the commit message?

@jhawthorn jhawthorn merged commit a1f46bb into trilogy-libraries:main Aug 4, 2023
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.

2 participants