parallel 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,5 +1,5 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
3
  gem 'rake'
4
- gem 'rspec', '~>1'
4
+ gem 'rspec', '~>2'
5
5
  gem 'jeweler'
@@ -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 (1.3.1)
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 (~> 1)
32
+ rspec (~> 2)
data/Rakefile CHANGED
@@ -1,15 +1,16 @@
1
1
  task :default => :spec
2
- require 'spec/rake/spectask'
3
- Spec::Rake::SpecTask.new {|t| t.spec_opts = ['--color'] }
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 = project_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/#{project_name}"
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://pragmatig.wordpress.com)
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.0
1
+ 0.5.1
@@ -16,7 +16,8 @@ class Parallel
16
16
  end
17
17
  end
18
18
 
19
- threads.each{|t| t.join }
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
- # work in #{size} threads that use threads/processes
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
- raise output.exception if ExceptionWrapper === output
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
- while input = child_read.gets and input != "\n"
146
- index = decode(input.chomp)
147
- begin
148
- result = Parallel.call_with_index(items, index, options, &block)
149
- result = nil if options[:preserve_results] == false
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
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{parallel}
8
- s.version = "0.5.0"
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-10-24}
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
@@ -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
- before do
6
- @cpus = Parallel.processor_count
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" * @cpus
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" * @cpus
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 'it does not leave processes behind while running' do
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: 11
4
+ hash: 9
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 5
9
- - 0
10
- version: 0.5.0
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-10-24 00:00:00 +02:00
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