Skip to content

Commit

Permalink
Pushed to version 0.0.2, major changes:
Browse files Browse the repository at this point in the history
- Switched to testy for testing
- More tests!
- Fixed an issue on RedHat/CentOS machines where select() did not return an
  expected array from forkify
- Another example illustrating the drawback of non-pool forking
  • Loading branch information
dakrone committed Jun 29, 2009
1 parent 8dd8afb commit 0a2653d
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 22 deletions.
7 changes: 7 additions & 0 deletions History.txt
@@ -1,3 +1,10 @@
=== 0.0.2 / 2009-06-29

* 2 bugfixes

* Forkify now works correctly returns array values on RedHat machines
* More tests!

=== 0.0.1 / 2009-06-23

* 1 major enhancement
Expand Down
1 change: 1 addition & 0 deletions Manifest.txt
Expand Up @@ -6,3 +6,4 @@ lib/forkify.rb
test/test_forkify.rb
examples/a.rb
examples/b.rb
examples/c.rb
2 changes: 1 addition & 1 deletion README.txt
Expand Up @@ -22,7 +22,7 @@

== REQUIREMENTS:

* None
* Testy - only for running the tests

== INSTALL:

Expand Down
5 changes: 3 additions & 2 deletions Rakefile
Expand Up @@ -4,12 +4,13 @@ require 'rubygems'
require 'hoe'
require './lib/forkify.rb'

FORKIFY_VERSION = "0.0.1"
FORKIFY_VERSION = "0.0.2"

Hoe.spec('forkify') do
version = FORKIFY_VERSION
developer('Lee Hinman', 'lee@writequit.org')
self.rubyforge_name = 'hinmanm'
rubyforge_name = 'hinmanm'
end


# vim: syntax=Ruby
15 changes: 15 additions & 0 deletions examples/c.rb
@@ -0,0 +1,15 @@
#!/usr/bin/env ruby
# vim: set ts=2 sw=2 filetype=Ruby
#
# This example shows a problem with the current implementation of forkify:
# if a fork finishes work, it will still wait for the other forks to finish
# in the process pool before forking new processes for work.
#
# I hope to remedy this as soon as I figure out a good solution for it.

require 'forkify'

[1, 1, 1, 1, 5, 1].forkify(5) { |n| puts n; sleep(n); n }

# 0.04s user 0.06s system 1% cpu 6.031 total
# (would be possible to run in a little over 5)
6 changes: 3 additions & 3 deletions forkify.gemspec
Expand Up @@ -2,17 +2,17 @@

Gem::Specification.new do |s|
s.name = %q{forkify}
s.version = "0.0.1"
s.version = "0.0.2"

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Lee Hinman"]
s.date = %q{2009-06-23}
s.date = %q{2009-06-29}
s.description = %q{forkify.rb makes it easy to process a bunch of data using 'n'
worker processes. It is based off of forkoff and threadify by Ara Howard.
It aims to be safe to use on Ruby 1.8.6+ and Ruby 1.9.1+}
s.email = ["lee@writequit.org"]
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.txt"]
s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "lib/forkify.rb", "test/test_forkify.rb", "examples/a.rb", "examples/b.rb"]
s.files = ["History.txt", "Manifest.txt", "README.txt", "Rakefile", "lib/forkify.rb", "test/test_forkify.rb", "examples/a.rb", "examples/b.rb", "examples/c.rb"]
s.homepage = %q{http://github.com/dakrone/forkify}
s.rdoc_options = ["--main", "README.txt"]
s.require_paths = ["lib"]
Expand Down
59 changes: 43 additions & 16 deletions lib/forkify.rb
@@ -1,9 +1,27 @@
#require 'pp'
FORKIFY_DEBUG = false
require 'pp' if FORKIFY_DEBUG

module Enumerable

#
# Forkify will process _block_'s actions using processes. If no number of processes is
# given, the default of 5 will be used. If there are less than _procs_ number of items
# in the +Enumerable+ type, less processes will be spawned.
#
# It should be noted that forkify will *always* return an +Array+ at this time, so be
# careful with +Hash+ objects.
#
# = Examples
#
# [1, 2, 3].forkify { |n| n*2 } => [2, 4, 6]
#
# {:a => 1, :b => 2, :c => 3}.forkify { |k, v| [v, k] } => [[1, :a], [2, :b], [3, :c]]
#
# 10.times.forkify(10) { sleep(1) } => [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] (runs for less than 2 seconds)
#
def forkify procs = 5, &block
#puts "Class: #{self.class}"
if Array === self
puts "Forkify Class: #{self.class}" if FORKIFY_DEBUG
if self === Array
items = self
else
begin
Expand All @@ -27,9 +45,9 @@ def forkify procs = 5, &block
rpipes = []

num_procs.times do |i|
#puts "Fork # #{i}"
puts "Fork # #{i}" if FORKIFY_DEBUG
r, w = IO.pipe
#pp "r, w: #{r} #{w}"
pp "r, w: #{r} #{w}" if FORKIFY_DEBUG
wpipes << w
rpipes << r
pid = fork
Expand All @@ -52,26 +70,35 @@ def forkify procs = 5, &block

offset += num_procs

#pp "Waiting for pids: #{pids}"
pp "Waiting for pids: #{pids.inspect}" if FORKIFY_DEBUG
pids.each { |p| Process.waitpid(p) }

datawaiting_pipes = Kernel.select(rpipes, wpipes, nil, 2)
readwaiting_pipes = datawaiting_pipes[0]
writewaiting_pipes = datawaiting_pipes[1]
#pp "data: #{datawaiting_pipes}"
#pp "read: #{readwaiting_pipes}"
#pp "write: #{writewaiting_pipes}"
unless readwaiting_pipes.size != writewaiting_pipes.size
readwaiting_pipes.size.times do |i|
r = readwaiting_pipes[i]
w = writewaiting_pipes[i]
# 1 select version
#datawaiting_pipes = Kernel.select(rpipes, wpipes, nil, 2)
#readwaiting_pipes = datawaiting_pipes[0]
#writewaiting_pipes = datawaiting_pipes[1]

# Switch to 2 selects instead of 1
#readwaiting_pipes = Kernel.select(rpipes, nil, nil, 2)[0]
#writewaiting_pipes = Kernel.select(nil, wpipes, nil, 2)[1]

# Finally settled on going through the pipes instead of select for Linux bug
unless rpipes.size != wpipes.size
rpipes.size.times do |i|
r = rpipes[i]
w = wpipes[i]

pp "read: #{readwaiting_pipes}" if FORKIFY_DEBUG
pp "write: #{writewaiting_pipes}" if FORKIFY_DEBUG

w.close
data = ''
while ( buf = r.read(8192) )
data << buf
end
result = Marshal.load( data )
r.close
pp "Pushing result: #{result}" if FORKIFY_DEBUG
results << result
end
end
Expand Down
13 changes: 13 additions & 0 deletions test/test_forkify.rb
Expand Up @@ -15,4 +15,17 @@
r = [1, 2, 3].forkify { |n| n * 2 }
t.check :array_results, :expect => [2, 4, 6], :actual => r
end

test 'hash results' do |t|
r = {:a => 1, :b => 2, :c => 3}.forkify { |k, v| [k, v*2] }
t.check :hash_contains_a, :expect => true, :actual => r.include?([:a, 2])
t.check :hash_contains_b, :expect => true, :actual => r.include?([:b, 4])
t.check :hash_contains_c, :expect => true, :actual => r.include?([:c, 6])
t.check :hash_length, :expect => 3, :actual => r.size
end

test 'array of nils' do |t|
r = [nil, nil].forkify { |n| n }
t.check :nil_array, :expect => [nil, nil], :actual => r
end
end

0 comments on commit 0a2653d

Please sign in to comment.