Andrei Botalov
Andrei Botalov

Reputation: 21096

Process stucks when invoke childprocess from a forked process in Ruby 1.9.3

The following code stucks in MRI 1.9.3 but seems to work in 2.0.0 and 2.1.2.

def run_in_isolation
  # From http://stackoverflow.com/a/1076445/841064
  read, write = IO.pipe

  pid = fork do
    read.close
    result = yield
    Marshal.dump(result, write)
    exit
  end

  write.close
  result = read.read
  Process.wait(pid)
  raise 'child failed' if result.empty?
  Marshal.load(result)
end

run_in_isolation do
  require 'childprocess' # http://rubygems.org/gems/childprocess
  process = ChildProcess.build("ruby", "-e", "sleep")
  process.start
end

Why? And how can I make it working in 1.9.3?

Upvotes: 0

Views: 82

Answers (1)

jarib
jarib

Reputation: 6058

Your code has two problems:

  1. You're trying to Marshal.load a ChildProcess process, i.e. the return value from process.start in the parent where ChildProcess was never loaded. Since the module (ChildProcess) does not exist in the parent process, this will raise an ArgumentError on any Ruby version.

  2. On 1.9, the write side of the created pipe will be inherited by the sleeping Ruby process started through ChildProcess. Since the write side isn't closed, the read() call will hang. In Ruby 2.X, the default behaviour was changed so that non-standard file descriptors are closed on exec. To solve this (in a cross-platform way) you can call ChildProcess.close_on_exec(write) just before you yield.

Upvotes: 1

Related Questions