-
Notifications
You must be signed in to change notification settings - Fork 55
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
Child process erroring out instead of blocking on write to stdout on Ruby 3.0? #90
Comments
Writing a simple C program confirms that for some reason, stdout is set to non-blocking on Ruby 3.0, but not on Ruby 2.7: #include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(void) {
printf("%d\n", fcntl(STDOUT_FILENO, F_GETFL) & O_NONBLOCK);
return 0;
} Indeed, unlike Ruby 2.7, Ruby 3.0 makes all pipes non-blocking by default, and as far as I understand it, manually undoes that when passing stdout etc. to child processes with built-in popen/exec etc. This means that posix-spawn probably has to do the same thing, not only for |
For reference this fork apparently solves the problem for @jhawthorn Is that patch used at GitHub? If so is there any chance to upstream it? Regarding |
@catwell we are no longer using this gem. Ruby has had a built-in and efficient |
OK, thanks for the answer! I was still using it in part because I prefer its API - see this blobpost by @jnunemaker for an example - but I can switch to native Process.spawn in this case. I also suggest the maintainers of posix-spawn mark it as officially deprecated in the README. |
@catwell Why did you suggest that the gem should be deprecated? What changed in the built-in |
@midnight-wonderer I only suggest the gem should be deprecated if the maintainers don't use it anymore and don't intend to fix its bugs. Process.spawn is not what changed, it's IO (and pipes in particular) that is non-blocking by default. But pipes used for stdin / stderr / stdout should not be non-blocking, so they should be made blocking explicitly. |
Hey, thanks a bunch for the explanation; that answered most of my questions. However, I don't quite understand low-level details of OS kinds of stuff, and since you considered switching to |
Not entirely sure that is what he was referring to but Ruby now uses vfork when it can. Like I said that wasn't my reason for using this gem anyway. |
That's it, thank you. I just modified and ran a benchmark of
The open3 implementation is using similar The gem should not be used in new applications because it doesn't provide advantages over native Ruby implementation anymore. And GitHub + the gem maintainers have moved on. For the |
Yes, that is probably what I will do now. In the meantime we monkey patched this gem to have it work on Ruby 3 since we wanted to avoid such changes at the same time as our Ruby 3 upgrade. |
does anyone know where I can find discussion about the changes with Process::spawn in ruby 3? Searching the web, ruby codebase, and ruby changelogs for vfork doesn't come up with anything
This code was committed 9 years ago If anyone has better search terms for me to use, much appreciate, thanks! |
@jjb Yes the vfork change is older than Ruby 3, it shipped with Ruby 2.2.
So since Ruby 2.2 it is no longer necessary to use POSIX:Spawn to get good fork performance, but it still worked well until Ruby 3. The breaking change in Ruby 3 is that pipes, used internally by POSIX:Spawn, became non-blocking by default, as explained earlier in the thread. If you're looking for the commit, that change happened here. |
Thanks for that info! Wow, interesting - I didn't discover posix-spawn until far after 2.2 and used it everywhere I could. I did understand that breaking change prompted the discussion in this ticket - what I was looking for was info about the change in ruby which made posix-spawn irrelevant, so I guess back in 2.2. Looks like the release notes cover it! https://www.ruby-lang.org/en/news/2014/12/25/ruby-2-2-0-released/
and looks like it's never used on macos and solaris: https://github.com/ruby/ruby/blob/master/configure.ac |
The `posix-spawn` gem was created to address performance issues in Ruby when spawning child processes. Those issues have been addressed since Ruby 2.2 so the gem is technically not necessary anymore. The performance of Ruby's `Process.spawn` is equivalent and sometimes a little faster than the custom `POSIX::Spawn#_pspawn` method created by the `posix-spawn` gem. Additionally, the `posix-spawn` C extension is not compatible with Ruby 3.0. In Ruby 3.0, `IO.pipe` and others return nonblocking pipes - see ruby/ruby@6a65f2b. As you can see in the commit above, these are moved back to blocking (at least for `stdin`, `stdout` and `stderr`) inside Ruby's `Process.spawn` implementation. When using `posix-spawn` this results in the child process getting an `EAGAIN` sometimes, which it doesn't expect as it doesn't expect pipes to be non-blocking. This seems to happen when writing more than 64KB on Linux. More details in rtomayko#90
I'm trying to port the application I am working on to Ruby 3.0, and one of the issues I have is some dependency “randomly” failing to run commands with large output. I have tracked down the issue to
posix-spawn
as removing that gem makes the library fall back toProcess.spawn
which seems to work as expected.It seems that for some reason, when using
posix-spawn
on Ruby 3.0, the child process will error out with “Error 11: Resource temporarily unavailable” when filling the pipe's buffer, while on Ruby 2.7, it will simply block as expected.The following simple code demonstrates the issue:
The text was updated successfully, but these errors were encountered: