Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning data from forked processes

Tags:

fork

process

ruby

People also ask

What are the possible return values of fork?

RETURN VALUE Upon successful completion, fork() returns 0 to the child process and returns the process ID of the child process to the parent process. Otherwise, -1 is returned to the parent process, no child process is created, and errno is set to indicate the error.

How does fork return two values?

fork does not return two values. Right after a fork system call you simply have two independent processes executing the same code, and the returned pid from fork is the only way to distinguish which process are you in - the parent or the child.

What does PID return?

The line PID = fork(); returns the value of the fork() system call. The if (PID == 0) evaluates the return value. If PID is equal to zero then printf() is executed in the child process, but not in the parent process.

What is the return value of the child process to the parent process?

Write a C program in which the child process takes an input array and send it to the parent process using pipe() and fork() and then print it in the parent process.


We actually just had to handle this problem in Rails isolation testing. I posted about it some on my blog.

Basically, what you want to do is open a pipe in the parent and child, and have the child write to the pipe. Here's a simple way to run the contents of a block in a child process and get back the result:

def do_in_child
  read, write = IO.pipe

  pid = fork do
    read.close
    result = yield
    Marshal.dump(result, write)
    exit!(0) # skips exit handlers.
  end

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

Then you could run:

do_in_child do
  require "some_polluting_library"
  SomePollutingLibrary.some_operation
end

Note that if you do a require in the child, you will not have access to that library in the parent, so you cannot return an object of that type using this method. However, you could return any type that's available in both.

Also note that a lot of the details here (read.close, Process.wait2(pid)) are mostly housekeeping details, so if you use this a lot you should probably move this out into a utility library that you can reuse.

Finally, note that this will not work on Windows or JRuby, since they don't support forking.


Thanks for all the answers, I got my solution up and running, still need to see how to handle non-forking environments, but for now it works :)

read, write = IO.pipe
Process.fork do
  write.puts "test"
end
Process.fork do
  write.puts 'test 2'
end

Process.wait
Process.wait

write.close
puts read.read
read.close

you can see it in action @ parallel_specs Rails plugin


I wrapped all the solutions I found along the way (some other problems like user exiting + piping-buffers) into ruby parallel gem. Now it is as easy as:

results = Parallel.map([1,2,3],:in_processes=>4) do |i|
  execute_something(i)
end

or

results = Parallel.map([1,2,3],:in_threads=>4) do |i|
  execute_something(i)
end

Yes, you can create a subprocess to execute a block inside.

I recommend the aw gem:

Aw.fork! { 6 * 7 } # => 42

Of course, it prevents from side effects:

arr = ['foo']
Aw.fork! { arr << 'FUU' } # => ["foo", "FUU"]
arr # => ["foo"]