What's the Difference Between Process.Fork and Process.Spawn in Ruby 1.9.2

What's the difference between Process.fork and Process.spawn in Ruby 1.9.2


What's the difference between Process.fork and the new Process.spawn methods in Ruby 1.9.2

Process.fork allows you to run ruby code in another process. Process.spawn allows you to run another program in another process. Basically Process.spawn is like using Process.fork and then calling exec in the forked process, except that it gives you more options.

and which one is better to run another program in a subprocess?

If you need backwards compatibility, use fork + exec as spawn is not available in 1.8. Otherwise use spawn since running another program in a subprocess is exactly what spawn is made for.

As far as I understand Process.fork accepts block of code and Process.spawn takes a system command plus some other parameters.

Exactly.

When I should use one instead of the other?

Use fork if you need to run arbitrary ruby code in a separate process (you can't do that with spawn). Use spawn if you need to invoke an application in a subprocess.

Fork child process with timeout and capture output

You can use IO.pipe and tell Process.spawn to use the redirected output without the need of external gem.

Of course, only starting with Ruby 1.9.2 (and I personally recommend 1.9.3)

The following is a simple implementation used by Spinach BDD internally to capture both out and err outputs:

# stdout, stderr pipes
rout, wout = IO.pipe
rerr, werr = IO.pipe

pid = Process.spawn(command, :out => wout, :err => werr)
_, status = Process.wait2(pid)

# close write ends so we could read them
wout.close
werr.close

@stdout = rout.readlines.join("\n")
@stderr = rerr.readlines.join("\n")

# dispose the read ends of the pipes
rout.close
rerr.close

@last_exit_status = status.exitstatus

The original source is in features/support/filesystem.rb

Is highly recommended you read Ruby's own Process.spawn documentation.

Hope this helps.

PS: I left the timeout implementation as homework for you ;-)

TERM signal to forked process doesn't work

The reason why it did not work as expected was that the kill was delivered before the exec commenced. Introducing a little sleep before delivering the kill ensures that the program will behave as expected.

Ruby: running external processes in parallel and keeping track of exit codes

Try:

statuses = pids.map { |pid| Process.wait(pid, 0); $? }

This waits for each of the process ids to finish, and checks for the result status set in $? for each process

spawning a command prompt in a different process and sending/receiving commands on Windows

Here an example using IO.popen in windows, imo if it works with the stdlib don't use gems

IO.popen("other_program", "w+") do |pipe|
pipe.puts "here, have some input"
pipe.close_write # If other_program process doesn't flush its output, you probably need to use this to send an end-of-file, which tells other_program to give us its output. If you don't do this, the program may hang/block, because other_program is waiting for more input.
output = pipe.read
end

# You can also use the return value from your block. (exit code stored in $? as usual)
output = IO.popen("other_program", "w+") do |pipe|
pipe.puts "here, have some input"
pipe.close_write
pipe.read
end

What's the difference between :new, :collection and :member routes?

The difference is the URL generated.

Let's guess three resources :

map.resources :users, :collection => { :rss => :get }
map.resources :users, :member => { :profile => :get }
map.resources :users, :new => { :draft => :get }

The first route will create :

/users/rss

With nothing between the controller name and the action name. We don't need any other parameter to get the user's list rss feed.

The second one will create the action "profile" as a member of the object. So we'll have :

/users/1/profile

The "1" is the user's to_param. We need a user's id to display a profile.

The third one will create the action "draft" as a member of the new action. So we'll have :

/users/new/draft

The "draft" action displays a draft of the user before accepting its creation.

So that's the difference between :collection, :member and :new. Every of them creates different routes, each one with their own purpose.

Kill process launched via thread

You can use Process.spawn instead of threads and backticks:

class TestServer < MiniTest::Unit::TestCase
def setup
@pid = spawn "#{MY_COMMAND}"
end

def test_aaa
# my test code
end

def teardown
Process.kill('TERM', @pid) if @pid
end
end


Related Topics



Leave a reply



Submit