Skip to content

Commit

Permalink
Avoid bisect command to get stuck
Browse files Browse the repository at this point in the history
From Palkan in benoittgt/rspec_repro_bisect_deadlock#1

First, I've tried to play with the number of specs which led to the
interesting conclusion: **the process hangs only at 1548+ specs**.

```diff
 RSpec.describe "a bunch of nothing" do
   (0...(ENV.fetch('N', 3000).to_i)).each do |t|
     it { expect(t).to eq t }
   end
 end
```

Try to run with `N=1547` and `N=1548`.

Seems suspicious, right?

Let's add `pry-byebug` to the equation (or Gemfile).

In order it to work we need to tweak our runner code a bit:

```diff
- $stdout = $stderr = @spec_output
+ # $stdout = $stderr = @spec_output
```

After a bit of `puts` debugging I localized the problem:
[`@channel.send`](https://github.com/rspec/rspec-core/blob/7b6b9c3f2e2878213f97d6fc9e9eb23c323cfe1c/lib/rspec/core/bisect/fork_runner.rb#L122).

 `Channel#send` calls `IO#write` here
 https://github.com/rspec/rspec-core/blob/7b6b9c3f2e2878213f97d6fc9e9eb23c323cfe1c/lib/rspec/core/bisect/utilities.rb#L41:

 ```ruby
 def send(message)
   packet = Marshal.dump(message)
   @write_io.write("#{packet.bytesize}\n#{packet}")
 end
 ```

 Do you know, what is the `packet.bytesize` for `N=1548`? It's **65548**.
 This number is very important: the pipe size is only **65536** on MacOS
 (see docs for [`IO#write_nonblock`](https://ruby-doc.org/core-2.6.3/IO.html#method-i-write_nonblock)
 for more).

 That makes `@write_io.write` hangs forever, because no one reads the
 buffer: we call `Channel#receive` only after `Process.waitpid(pid)`,
 thus waiting for the write operation to complete.

 -----------

 A basic proposal is to use WNOHANG. From waitpid doc:

 > If WNOHANG was specified in options and there were no children
 > in a waitable state, then waitid() returns 0 immediately (...)

 Related:
 - #2637
  • Loading branch information
benoittgt committed Sep 19, 2019
1 parent 8a287bd commit 8402ccb
Showing 1 changed file with 1 addition and 1 deletion.
2 changes: 1 addition & 1 deletion lib/rspec/core/bisect/fork_runner.rb
Expand Up @@ -92,7 +92,7 @@ def initialize(runner, channel)

def dispatch_specs(run_descriptor)
pid = fork { run_specs(run_descriptor) }
Process.waitpid(pid)
Process.waitpid(pid, Process::WNOHANG)
end

private
Expand Down

0 comments on commit 8402ccb

Please sign in to comment.