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 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