Skip to content
This repository has been archived by the owner on Nov 27, 2020. It is now read-only.

Let Process.spawn handle STDOUT redirection on MRI #855

Merged
merged 1 commit into from
Mar 20, 2017

Conversation

afn
Copy link
Contributor

@afn afn commented Feb 13, 2017

Fixes #854

@afn
Copy link
Contributor Author

afn commented Feb 13, 2017

I couldn't come up with a great spec for this, but the one in the PR does demonstrate the bug. Running the spec without the bugfix causes it to (typically) fail with an error like:

  1) Capybara::Poltergeist::Driver is threadsafe in how it captures console.log
     Failure/Error: puts i

     IOError:
       stream closed
     # ./spec/integration/driver_spec.rb:62:in `write'
     # ./spec/integration/driver_spec.rb:62:in `puts'
     # ./spec/integration/driver_spec.rb:62:in `puts'
     # ./spec/integration/driver_spec.rb:62:in `block (4 levels) in <module:Poltergeist>'
     # ./spec/integration/driver_spec.rb:61:in `upto'
     # ./spec/integration/driver_spec.rb:61:in `block (3 levels) in <module:Poltergeist>'

@afn
Copy link
Contributor Author

afn commented Feb 13, 2017

The failing builds don't appear to be related to this change. (It looks like bundler is picking up a new version of ttfunk, released today, that depends on ruby ~> 2.1)

STDOUT.reopen(prev)
$stdout = STDOUT
prev.close
if Capybara::Poltergeist.jruby?
Copy link
Contributor

Choose a reason for hiding this comment

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

Does rubinius support the :out option?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, I believe JRuby 9000 should support the :out option (could be wrong on that but I think so)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. If not, we could make this test more strict.

Separately, the thread safety issue still exists for JRuby (and Rubinius, if it doesn't support the :out option). I think it's outside the scope of this PR, but it may be worth looking into an alternate solution there, e.g. fork, then redirect STDOUT, then exec (assuming those VMs support fork and exec) rather than Process.spawn.

Copy link
Contributor

Choose a reason for hiding this comment

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

There is no fork for JVM

@twalpole
Copy link
Contributor

I fixed the ttfunk issue - please rebase so the tests can be fully run

@route
Copy link
Contributor

route commented Feb 14, 2017

This code was exactly written to be working across different ruby implementations. I wouldn't change it without strong reason and if I do I'd do it carefully.

@afn
Copy link
Contributor Author

afn commented Feb 15, 2017

@route the current implementation definitely doesn't work correctly in multithreaded applications on MRI, so at a minimum, this should be fixed to use :out on MRI. I strongly suspect that the thread-safety issue exists on most, if not all, other platforms as well, so ideally we should change this patch to use :out everywhere that it's available (and nowhere that it's not).

On platforms that don't support :out, what happens if you pass it in? Is it silently ignored, or does Process.spawn raise an error? If it's the latter, I would argue that the ideal approach is to try passing :out, catch the error if it's thrown, and then fall back on the redirect_stdout approach ... after printing a warning message (one that can be disabled via configuration) informing users that it's not threadsafe.

@route
Copy link
Contributor

route commented Feb 15, 2017

@afn how does your test fail without this patch?

@afn
Copy link
Contributor Author

afn commented Feb 16, 2017

@route our production app intermittently dies with Errno::EPIPE: Broken pipe @ io_write - <STDOUT>. I wasn't able to exactly reproduce that failure in a test, but the one I wrote fails pretty consistently with:

     IOError:
       stream closed
     # ./spec/integration/driver_spec.rb:62:in `write'
     # ./spec/integration/driver_spec.rb:62:in `puts'
     # ./spec/integration/driver_spec.rb:62:in `puts'
     # ./spec/integration/driver_spec.rb:62:in `block (4 levels) in <module:Poltergeist>'
     # ./spec/integration/driver_spec.rb:61:in `upto'
     # ./spec/integration/driver_spec.rb:61:in `block (3 levels) in <module:Poltergeist>'

@@ -56,6 +56,29 @@ def session_url(path)
end
end

unless Capybara::Poltergeist.jruby?
Copy link
Contributor

Choose a reason for hiding this comment

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

instead of of not running the test when jruby, please set the test to pending when jruby is true so that we can see the results in the logs as an accepted failure

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, looks like it doesn't actually fail in jruby. I'd be surprised if the thread-safety issue didn't exist there, though. Let me see if I can come up with a better test.

Copy link
Contributor

Choose a reason for hiding this comment

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

A test that doesn't actually add 100k lines to the log would be good too

Copy link
Contributor Author

Choose a reason for hiding this comment

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

😁

@afn
Copy link
Contributor Author

afn commented Feb 17, 2017

Updated the spec, so that it's deterministic; it's pending on jruby. I guess the only outstanding question is whether we can use :out on other rubies, e.g. Rubinius. What's the right condition for using :out?

@route
Copy link
Contributor

route commented Feb 17, 2017

@afn @twalpole I haven't been following Rubinius for a while is it worth supporting now?

@afn
Copy link
Contributor Author

afn commented Feb 17, 2017

Looks like :out doesn't work on Rubinius either, so I've changed the code to use redirect_stdout on JRuby and Rubinius.

BTW, the build wasn't running at all on Rubinius, but the build failure was ignored. I've updated .travis.yml to get it to work. The build actually passes (with the new test pending), but it segfaults right after finishing running the tests, so unfortunately we still have to leave Rubinius in the allow_failures list.

@twalpole
Copy link
Contributor

@route The biggest issue with rubinius is getting it to run stably on travis (as @afn found out)


it 'is threadsafe in how it captures console.log' do
stdout = capture_stdout do
pending if Capybara::Poltergeist.jruby? || Capybara::Poltergeist.rubinius?
Copy link
Contributor

@twalpole twalpole Feb 17, 2017

Choose a reason for hiding this comment

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

Please add a descriptive text here - pending "blah blah not supported" if ... also this should be the first line in the test

@@ -64,6 +64,7 @@ def start

process_options = {}
process_options[:pgroup] = true unless Capybara::Poltergeist.windows?
process_options[:out] = @write_io unless Capybara::Poltergeist.jruby?
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this check rubinius? too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed

@@ -56,6 +56,54 @@ def session_url(path)
end
end

def capture_stdout
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this can be replaced with RSpec's output matcher - https://www.relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice! I didn't know about that.

@twalpole
Copy link
Contributor

twalpole commented Feb 20, 2017

JRuby 9.1.7.0 appears to pass the correctly if the :out option is passed - so either it supports :out or the test isn't valid (the jruby-19mode build appears to pass the test too)

@afn
Copy link
Contributor Author

afn commented Feb 21, 2017 via email

@twalpole
Copy link
Contributor

There's the global constant JRUBY_VERSION but I haven't been able to find any actual documentation on what version should work correctly with :out and what shouldn't. I'm also curious whether it's platform specific behavior (linux/macos/windows) if anyone knows?

@afn
Copy link
Contributor Author

afn commented Feb 25, 2017

Well how about we merge this PR, which is a conservative fix (in that it fixes the issue on MRI, but not on JRuby or Rubinius; but it also doesn't introduce regressions on those platforms), and then open a separate issue to address the thread-unsafe behavior on JRuby and Rubinius. Then as part of triaging that issue, we can look into whether the bug is OS-specific, whether Rubinius is still of interest, which version of JRuby added support for :out, etc. Does that sound OK?

@twalpole
Copy link
Contributor

My concern here would also be whether it has platform specific issues on MRI - @route?

@route
Copy link
Contributor

route commented Feb 27, 2017

@twalpole I can't recall MRI's issues, but I definitely remember how it failed on jruby or rubinius either. Moreover I tried to fix it different ways including out option and when it worked on jruby then rubinius failed and vice versa haha) But time has passed since then and maybe languages has changed decently. Here's the related PR #405

@twalpole
Copy link
Contributor

twalpole commented Mar 17, 2017

@afn Could you please change this to check for MRI to opt in to the new behavior (since that's the only place we know for sure it works) rather than checking JRuby and rubinius to opt out of it and squash the commits, then I'll merge

@afn
Copy link
Contributor Author

afn commented Mar 20, 2017

@twalpole Done - LMK how that looks. Thanks!

@twalpole twalpole merged commit 7ad41d3 into teampoltergeist:master Mar 20, 2017
@twalpole
Copy link
Contributor

Thanks!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants