parallel 0.3.7 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.7
1
+ 0.4.0
@@ -3,7 +3,9 @@ require 'thread' # to get Thread.exclusive
3
3
  class Parallel
4
4
  VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
5
5
 
6
- def self.in_threads(count = 2)
6
+ def self.in_threads(options={:count => 2})
7
+ count, options = extract_count_from_options(options)
8
+
7
9
  out = []
8
10
  threads = []
9
11
 
@@ -17,40 +19,18 @@ class Parallel
17
19
  out
18
20
  end
19
21
 
20
- def self.in_processes(count = nil)
22
+ def self.in_processes(options = {}, &block)
23
+ count, options = extract_count_from_options(options)
21
24
  count ||= processor_count
25
+ preserve_results = (options[:preserve_results] != false)
22
26
 
23
- # Start writing results into n pipes
24
- reads = []
25
- writes = []
26
- pids = []
27
- count.times do |i|
28
- reads[i], writes[i] = IO.pipe
29
- # activate copy on write friendly GC of REE
30
- GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
31
- pids << Process.fork do
32
- Marshal.dump(yield(i), writes[i]) # Serialize result
33
- end
34
- end
35
-
36
- kill_on_ctrl_c(pids)
37
-
38
- # Collect results from pipes simultanously
39
- # otherwise pipes get stuck when to much is written (buffer full)
40
- out = []
41
- in_threads(count) do |i|
42
- writes[i].close
43
- while text = reads[i].gets
44
- out[i] = out[i].to_s + text
45
- end
46
- reads[i].close
47
- end
48
-
49
- out.map{|x| Marshal.load(x) } # Deserialize results
27
+ pipes = fork_and_start_writing(count, :preserve_results => preserve_results, &block)
28
+ out = read_from_pipes(pipes)
29
+ out.map{|x| deserialize(x) } if preserve_results
50
30
  end
51
31
 
52
32
  def self.each(array, options={}, &block)
53
- map(array, options, &block)
33
+ map(array, options.merge(:preserve_results => false), &block)
54
34
  array
55
35
  end
56
36
 
@@ -74,7 +54,7 @@ class Parallel
74
54
  loop do
75
55
  index = Thread.exclusive{ current+=1 }
76
56
  break if index >= array.size
77
- results[index] = *send(method, 1){ yield array[index] }
57
+ results[index] = *send(method, options.merge(:count => 1)){ yield array[index] }
78
58
  end
79
59
  end
80
60
 
@@ -92,7 +72,63 @@ class Parallel
92
72
 
93
73
  private
94
74
 
75
+ # Collect results from pipes simultanously
76
+ # otherwise pipes get stuck when to much is written (buffer full)
77
+ def self.read_from_pipes(reads)
78
+ out = []
79
+ in_threads(reads.size) do |i|
80
+ out[i] = ''
81
+ while text = reads[i].gets
82
+ out[i] += text
83
+ end
84
+ reads[i].close
85
+ end
86
+ out
87
+ end
88
+
89
+ # fork and start writing results into n pipes
90
+ def self.fork_and_start_writing(count, options, &block)
91
+ reads = []
92
+ pids = []
93
+ count.times do |i|
94
+ reads[i], write = IO.pipe
95
+ pids << do_in_new_process(i, options.merge(:write_to => (options[:preserve_results] ? write : nil)), &block)
96
+ write.close
97
+ end
98
+ kill_on_ctrl_c(pids)
99
+ reads
100
+ end
101
+
102
+ def self.do_in_new_process(work_item, options)
103
+ # activate copy on write friendly GC of REE
104
+ GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
105
+ Process.fork do
106
+ result = yield(work_item)
107
+ serialize(result, options) if options[:write_to]
108
+ end
109
+ end
110
+
111
+ def self.serialize(something, options)
112
+ Marshal.dump(something, options[:write_to])
113
+ end
114
+
115
+ def self.deserialize(something)
116
+ Marshal.load(something)
117
+ end
118
+
119
+ # options is either a Interger or a Hash with :count
120
+ def self.extract_count_from_options(options)
121
+ if options.is_a?(Hash)
122
+ count = options[:count]
123
+ else
124
+ count = options
125
+ options = {}
126
+ end
127
+ [count, options]
128
+ end
129
+
95
130
  # split an array into groups of size items
131
+ # (copied from ActiveSupport, to not require it)
96
132
  def self.in_groups_of(array, size)
97
133
  results = []
98
134
  loop do
@@ -106,7 +142,7 @@ class Parallel
106
142
  results
107
143
  end
108
144
 
109
- #handle user interrupt (Ctrl+c)
145
+ # kill all these processes (children) if user presses Ctrl+c
110
146
  def self.kill_on_ctrl_c(pids)
111
147
  Signal.trap :SIGINT do
112
148
  $stderr.puts 'Parallel execution interrupted, exiting ...'
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{parallel}
8
- s.version = "0.3.7"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Michael Grosser"]
12
- s.date = %q{2009-12-19}
12
+ s.date = %q{2010-01-17}
13
13
  s.email = %q{grosser.michael@gmail.com}
14
14
  s.extra_rdoc_files = [
15
15
  "README.markdown"
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
20
20
  "VERSION",
21
21
  "lib/parallel.rb",
22
22
  "parallel.gemspec",
23
+ "spec/cases/no_dump_with_each.rb",
23
24
  "spec/cases/parallel_each.rb",
24
25
  "spec/cases/parallel_high_fork_rate.rb",
25
26
  "spec/cases/parallel_influence_outside_data.rb",
@@ -47,6 +48,7 @@ Gem::Specification.new do |s|
47
48
  "spec/cases/parallel_map_uneven.rb",
48
49
  "spec/cases/parallel_map_range.rb",
49
50
  "spec/cases/parallel_with_set_processes.rb",
51
+ "spec/cases/no_dump_with_each.rb",
50
52
  "spec/cases/parallel_start_and_kill.rb",
51
53
  "spec/cases/parallel_raise.rb",
52
54
  "spec/cases/parallel_sleeping_2.rb",
@@ -0,0 +1,16 @@
1
+ require 'spec/spec_helper.rb'
2
+
3
+ class NotDumpable
4
+ def marshal_dump
5
+ raise "NOOOO"
6
+ end
7
+
8
+ def to_s
9
+ 'not dumpable'
10
+ end
11
+ end
12
+
13
+ Parallel.each([NotDumpable.new]) do |x|
14
+ print 'not dumpable'
15
+ x
16
+ end
@@ -106,6 +106,10 @@ describe Parallel do
106
106
  it "returns original array, works like map" do
107
107
  `ruby spec/cases/parallel_each.rb`.should == '-b--c--d--a-a b c d'
108
108
  end
109
+
110
+ it "does not use marshal_dump" do
111
+ `ruby spec/cases/no_dump_with_each.rb 2>&1`.should == 'not dumpable'
112
+ end
109
113
  end
110
114
 
111
115
  describe :in_groups_of do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.7
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Grosser
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-19 00:00:00 +01:00
12
+ date: 2010-01-17 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -27,6 +27,7 @@ files:
27
27
  - VERSION
28
28
  - lib/parallel.rb
29
29
  - parallel.gemspec
30
+ - spec/cases/no_dump_with_each.rb
30
31
  - spec/cases/parallel_each.rb
31
32
  - spec/cases/parallel_high_fork_rate.rb
32
33
  - spec/cases/parallel_influence_outside_data.rb
@@ -76,6 +77,7 @@ test_files:
76
77
  - spec/cases/parallel_map_uneven.rb
77
78
  - spec/cases/parallel_map_range.rb
78
79
  - spec/cases/parallel_with_set_processes.rb
80
+ - spec/cases/no_dump_with_each.rb
79
81
  - spec/cases/parallel_start_and_kill.rb
80
82
  - spec/cases/parallel_raise.rb
81
83
  - spec/cases/parallel_sleeping_2.rb