parallel 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +1 -1
- data/Gemfile.lock +12 -2
- data/Rakefile +6 -5
- data/Readme.md +1 -1
- data/VERSION +1 -1
- data/lib/parallel.rb +59 -31
- data/parallel.gemspec +6 -2
- data/spec/cases/map_with_processes_and_exceptions.rb +16 -0
- data/spec/cases/map_with_threads_and_exceptions.rb +16 -0
- data/spec/parallel_spec.rb +13 -5
- metadata +8 -4
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
+
diff-lcs (1.1.2)
|
4
5
|
gemcutter (0.6.1)
|
5
6
|
git (1.2.5)
|
6
7
|
jeweler (1.4.0)
|
@@ -9,7 +10,16 @@ GEM
|
|
9
10
|
rubyforge (>= 2.0.0)
|
10
11
|
json_pure (1.4.6)
|
11
12
|
rake (0.8.7)
|
12
|
-
rspec (
|
13
|
+
rspec (2.0.1)
|
14
|
+
rspec-core (~> 2.0.1)
|
15
|
+
rspec-expectations (~> 2.0.1)
|
16
|
+
rspec-mocks (~> 2.0.1)
|
17
|
+
rspec-core (2.0.1)
|
18
|
+
rspec-expectations (2.0.1)
|
19
|
+
diff-lcs (>= 1.1.2)
|
20
|
+
rspec-mocks (2.0.1)
|
21
|
+
rspec-core (~> 2.0.1)
|
22
|
+
rspec-expectations (~> 2.0.1)
|
13
23
|
rubyforge (2.0.4)
|
14
24
|
json_pure (>= 1.1.7)
|
15
25
|
|
@@ -19,4 +29,4 @@ PLATFORMS
|
|
19
29
|
DEPENDENCIES
|
20
30
|
jeweler
|
21
31
|
rake
|
22
|
-
rspec (~>
|
32
|
+
rspec (~> 2)
|
data/Rakefile
CHANGED
@@ -1,15 +1,16 @@
|
|
1
1
|
task :default => :spec
|
2
|
-
require
|
3
|
-
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
4
|
+
t.rspec_opts = '--backtrace --color'
|
5
|
+
end
|
4
6
|
|
5
7
|
begin
|
6
8
|
require 'jeweler'
|
7
|
-
project_name = 'parallel'
|
8
9
|
Jeweler::Tasks.new do |gem|
|
9
|
-
gem.name =
|
10
|
+
gem.name = 'parallel'
|
10
11
|
gem.summary = "Run any kind of code in parallel processes"
|
11
12
|
gem.email = "grosser.michael@gmail.com"
|
12
|
-
gem.homepage = "http://github.com/grosser/#{
|
13
|
+
gem.homepage = "http://github.com/grosser/#{gem.name}"
|
13
14
|
gem.authors = ["Michael Grosser"]
|
14
15
|
end
|
15
16
|
|
data/Readme.md
CHANGED
@@ -53,6 +53,6 @@ Authors
|
|
53
53
|
- [Jeremy Durham](http://www.jeremydurham.com)
|
54
54
|
- [Nick Gauthier](http://www.ngauthier.com)
|
55
55
|
|
56
|
-
[Michael Grosser](http://
|
56
|
+
[Michael Grosser](http://grosser.it)
|
57
57
|
grosser.michael@gmail.com
|
58
58
|
Hereby placed under public domain, do what you want, just do not hold me accountable...
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.1
|
data/lib/parallel.rb
CHANGED
@@ -16,7 +16,8 @@ class Parallel
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
threads
|
19
|
+
wait_for_threads(threads)
|
20
|
+
|
20
21
|
out
|
21
22
|
end
|
22
23
|
|
@@ -48,20 +49,7 @@ class Parallel
|
|
48
49
|
size = [array.size, size].min
|
49
50
|
|
50
51
|
if method == :in_threads
|
51
|
-
|
52
|
-
results = []
|
53
|
-
current = -1
|
54
|
-
|
55
|
-
in_threads(size) do
|
56
|
-
# as long as there are more items, work on one of them
|
57
|
-
loop do
|
58
|
-
index = Thread.exclusive{ current+=1 }
|
59
|
-
break if index >= array.size
|
60
|
-
results[index] = call_with_index(array, index, options, &block)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
results
|
52
|
+
work_in_threads(array, options.merge(:count => size), &block)
|
65
53
|
else
|
66
54
|
work_in_processes(array, options.merge(:count => size), &block)
|
67
55
|
end
|
@@ -86,6 +74,33 @@ class Parallel
|
|
86
74
|
|
87
75
|
private
|
88
76
|
|
77
|
+
def self.work_in_threads(items, options, &block)
|
78
|
+
results = []
|
79
|
+
current = -1
|
80
|
+
exception = nil
|
81
|
+
|
82
|
+
in_threads(options[:count]) do
|
83
|
+
# as long as there are more items, work on one of them
|
84
|
+
loop do
|
85
|
+
break if exception
|
86
|
+
|
87
|
+
index = Thread.exclusive{ current+=1 }
|
88
|
+
break if index >= items.size
|
89
|
+
|
90
|
+
begin
|
91
|
+
results[index] = call_with_index(items, index, options, &block)
|
92
|
+
rescue Exception => e
|
93
|
+
exception = e
|
94
|
+
break
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
raise exception if exception
|
100
|
+
|
101
|
+
results
|
102
|
+
end
|
103
|
+
|
89
104
|
def self.work_in_processes(items, options, &blk)
|
90
105
|
workers = Array.new(options[:count]).map{ worker(items, options, &blk) }
|
91
106
|
Parallel.kill_on_ctrl_c(workers.map{|worker| worker[:pid] })
|
@@ -100,6 +115,7 @@ class Parallel
|
|
100
115
|
# fetch results and hand out new work
|
101
116
|
listener_threads = []
|
102
117
|
result = Array.new(items.size)
|
118
|
+
exception = nil
|
103
119
|
|
104
120
|
workers.each do |worker|
|
105
121
|
listener_threads << Thread.new do
|
@@ -107,7 +123,13 @@ class Parallel
|
|
107
123
|
while output = worker[:read].gets
|
108
124
|
# store output from worker
|
109
125
|
result_index, output = decode(output.chomp)
|
110
|
-
|
126
|
+
if ExceptionWrapper === output
|
127
|
+
exception = output.exception
|
128
|
+
break
|
129
|
+
elsif exception # some other thread failed
|
130
|
+
break
|
131
|
+
end
|
132
|
+
|
111
133
|
result[result_index] = output
|
112
134
|
|
113
135
|
# give worker next item
|
@@ -127,6 +149,8 @@ class Parallel
|
|
127
149
|
# if they go zombie, rather wait here to be able to debug
|
128
150
|
wait_for_processes(workers.map{|worker| worker[:pid] })
|
129
151
|
|
152
|
+
raise exception if exception
|
153
|
+
|
130
154
|
result
|
131
155
|
end
|
132
156
|
|
@@ -138,21 +162,12 @@ class Parallel
|
|
138
162
|
parent_read, child_write = IO.pipe
|
139
163
|
|
140
164
|
pid = Process.fork do
|
141
|
-
parent_write.close
|
142
|
-
parent_read.close
|
143
|
-
|
144
165
|
begin
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
rescue Exception => e
|
151
|
-
result = ExceptionWrapper.new(e)
|
152
|
-
end
|
153
|
-
write_to_pipe(child_write, [index, result])
|
154
|
-
end
|
155
|
-
rescue Interrupt
|
166
|
+
parent_write.close
|
167
|
+
parent_read.close
|
168
|
+
|
169
|
+
process_incoming_jobs(child_read, child_write, items, options, &block)
|
170
|
+
ensure
|
156
171
|
child_read.close
|
157
172
|
child_write.close
|
158
173
|
end
|
@@ -164,12 +179,25 @@ class Parallel
|
|
164
179
|
{:read => parent_read, :write => parent_write, :pid => pid}
|
165
180
|
end
|
166
181
|
|
182
|
+
def self.process_incoming_jobs(read, write, items, options, &block)
|
183
|
+
while input = read.gets and input != "\n"
|
184
|
+
index = decode(input.chomp)
|
185
|
+
begin
|
186
|
+
result = call_with_index(items, index, options, &block)
|
187
|
+
result = nil if options[:preserve_results] == false
|
188
|
+
rescue Exception => e
|
189
|
+
result = ExceptionWrapper.new(e)
|
190
|
+
end
|
191
|
+
write_to_pipe(write, [index, result])
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
167
195
|
def self.write_to_pipe(pipe, item)
|
168
196
|
pipe.write(encode(item))
|
169
197
|
end
|
170
198
|
|
171
199
|
def self.wait_for_threads(threads)
|
172
|
-
threads.each do |t|
|
200
|
+
threads.compact.each do |t|
|
173
201
|
begin
|
174
202
|
t.join
|
175
203
|
rescue Interrupt
|
data/parallel.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{parallel}
|
8
|
-
s.version = "0.5.
|
8
|
+
s.version = "0.5.1"
|
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{2010-
|
12
|
+
s.date = %q{2010-11-05}
|
13
13
|
s.email = %q{grosser.michael@gmail.com}
|
14
14
|
s.files = [
|
15
15
|
"Gemfile",
|
@@ -25,6 +25,8 @@ Gem::Specification.new do |s|
|
|
25
25
|
"spec/cases/map_with_index.rb",
|
26
26
|
"spec/cases/map_with_index_empty.rb",
|
27
27
|
"spec/cases/map_with_nested_arrays_and_nil.rb",
|
28
|
+
"spec/cases/map_with_processes_and_exceptions.rb",
|
29
|
+
"spec/cases/map_with_threads_and_exceptions.rb",
|
28
30
|
"spec/cases/no_dump_with_each.rb",
|
29
31
|
"spec/cases/parallel_high_fork_rate.rb",
|
30
32
|
"spec/cases/parallel_influence_outside_data.rb",
|
@@ -50,11 +52,13 @@ Gem::Specification.new do |s|
|
|
50
52
|
"spec/spec_helper.rb",
|
51
53
|
"spec/parallel_spec.rb",
|
52
54
|
"spec/cases/parallel_sleeping_2.rb",
|
55
|
+
"spec/cases/map_with_processes_and_exceptions.rb",
|
53
56
|
"spec/cases/no_dump_with_each.rb",
|
54
57
|
"spec/cases/parallel_high_fork_rate.rb",
|
55
58
|
"spec/cases/map_with_index.rb",
|
56
59
|
"spec/cases/parallel_with_set_processes.rb",
|
57
60
|
"spec/cases/parallel_map.rb",
|
61
|
+
"spec/cases/map_with_threads_and_exceptions.rb",
|
58
62
|
"spec/cases/parallel_influence_outside_data.rb",
|
59
63
|
"spec/cases/parallel_start_and_kill.rb",
|
60
64
|
"spec/cases/parallel_map_uneven.rb",
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.expand_path('spec/spec_helper')
|
2
|
+
|
3
|
+
class Parallel
|
4
|
+
def self.wait_for_threads(threads)
|
5
|
+
print ' all joined'
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
begin
|
10
|
+
Parallel.map(1..100, :in_processes => 4) do |x|
|
11
|
+
print x
|
12
|
+
raise 'foo' if x == 1
|
13
|
+
end
|
14
|
+
rescue
|
15
|
+
print ' raised'
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.expand_path('spec/spec_helper')
|
2
|
+
|
3
|
+
class Parallel
|
4
|
+
def self.wait_for_threads(threads)
|
5
|
+
print ' all joined'
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
begin
|
10
|
+
Parallel.map(1..100, :in_threads => 4) do |x|
|
11
|
+
print x
|
12
|
+
raise 'foo' if x == 1
|
13
|
+
end
|
14
|
+
rescue
|
15
|
+
print ' raised'
|
16
|
+
end
|
data/spec/parallel_spec.rb
CHANGED
@@ -2,16 +2,16 @@ require File.expand_path('spec/spec_helper')
|
|
2
2
|
|
3
3
|
describe Parallel do
|
4
4
|
describe :in_processes do
|
5
|
-
|
6
|
-
|
5
|
+
def cpus
|
6
|
+
Parallel.processor_count
|
7
7
|
end
|
8
8
|
|
9
9
|
it "executes with detected cpus" do
|
10
|
-
`ruby spec/cases/parallel_with_detected_cpus.rb`.should == "HELLO\n" *
|
10
|
+
`ruby spec/cases/parallel_with_detected_cpus.rb`.should == "HELLO\n" * cpus
|
11
11
|
end
|
12
12
|
|
13
13
|
it "executes with detected cpus when nil was given" do
|
14
|
-
`ruby spec/cases/parallel_with_nil_uses_detected_cpus.rb`.should == "HELLO\n" *
|
14
|
+
`ruby spec/cases/parallel_with_nil_uses_detected_cpus.rb`.should == "HELLO\n" * cpus
|
15
15
|
end
|
16
16
|
|
17
17
|
it "set amount of parallel processes" do
|
@@ -52,7 +52,7 @@ describe Parallel do
|
|
52
52
|
`ruby spec/cases/parallel_high_fork_rate.rb`.should == 'OK'
|
53
53
|
end
|
54
54
|
|
55
|
-
it '
|
55
|
+
it 'does not leave processes behind while running' do
|
56
56
|
`ruby spec/cases/closes_processes_at_runtime.rb`.should == 'OK'
|
57
57
|
end
|
58
58
|
end
|
@@ -109,6 +109,14 @@ describe Parallel do
|
|
109
109
|
it 'handles nested arrays and nil correctly' do
|
110
110
|
`ruby spec/cases/map_with_nested_arrays_and_nil.rb`.should == '[nil, [2, 2], [[3], [3]]]'
|
111
111
|
end
|
112
|
+
|
113
|
+
it 'joins all workers, when one fails in process' do
|
114
|
+
`ruby spec/cases/map_with_processes_and_exceptions.rb 2>&1`.should =~ /^\d{4} all joined raised$/
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'joins all workers, when one fails in thread' do
|
118
|
+
`ruby spec/cases/map_with_threads_and_exceptions.rb 2>&1`.should =~ /^\d{0,4} all joined raised$/
|
119
|
+
end
|
112
120
|
end
|
113
121
|
|
114
122
|
describe :map_with_index do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parallel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 9
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 5
|
9
|
-
-
|
10
|
-
version: 0.5.
|
9
|
+
- 1
|
10
|
+
version: 0.5.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Michael Grosser
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-11-05 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -41,6 +41,8 @@ files:
|
|
41
41
|
- spec/cases/map_with_index.rb
|
42
42
|
- spec/cases/map_with_index_empty.rb
|
43
43
|
- spec/cases/map_with_nested_arrays_and_nil.rb
|
44
|
+
- spec/cases/map_with_processes_and_exceptions.rb
|
45
|
+
- spec/cases/map_with_threads_and_exceptions.rb
|
44
46
|
- spec/cases/no_dump_with_each.rb
|
45
47
|
- spec/cases/parallel_high_fork_rate.rb
|
46
48
|
- spec/cases/parallel_influence_outside_data.rb
|
@@ -94,11 +96,13 @@ test_files:
|
|
94
96
|
- spec/spec_helper.rb
|
95
97
|
- spec/parallel_spec.rb
|
96
98
|
- spec/cases/parallel_sleeping_2.rb
|
99
|
+
- spec/cases/map_with_processes_and_exceptions.rb
|
97
100
|
- spec/cases/no_dump_with_each.rb
|
98
101
|
- spec/cases/parallel_high_fork_rate.rb
|
99
102
|
- spec/cases/map_with_index.rb
|
100
103
|
- spec/cases/parallel_with_set_processes.rb
|
101
104
|
- spec/cases/parallel_map.rb
|
105
|
+
- spec/cases/map_with_threads_and_exceptions.rb
|
102
106
|
- spec/cases/parallel_influence_outside_data.rb
|
103
107
|
- spec/cases/parallel_start_and_kill.rb
|
104
108
|
- spec/cases/parallel_map_uneven.rb
|