in_threads 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/in_threads.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'in_threads'
5
- s.version = '1.1.0'
5
+ s.version = '1.1.1'
6
6
  s.summary = %q{Execute ruby code in parallel}
7
7
  s.homepage = "http://github.com/toy/#{s.name}"
8
8
  s.authors = ['Ivan Kuchin']
data/lib/in_threads.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'thread'
2
- require 'thwait'
3
2
 
4
3
  module Enumerable
5
4
  # Run enumerable method blocks in threads
@@ -26,8 +25,6 @@ module Enumerable
26
25
  end
27
26
  end
28
27
 
29
- # TODO: all ruby1.9.3 methods
30
-
31
28
  class InThreads
32
29
  (
33
30
  instance_methods.map(&:to_s) -
@@ -53,22 +50,17 @@ class InThreads
53
50
  end
54
51
 
55
52
  class << self
56
- # List of instance_methods of Enumerable
57
- def enumerable_methods
58
- Enumerable.instance_methods.map(&:to_s)
59
- end
60
-
61
53
  # Specify runner to use
62
54
  #
63
55
  # use :run_in_threads_consecutive, :for => %w[all? any? none? one?]
64
56
  #
65
57
  # <tt>:for</tt> is required
66
- # <tt>:ignore_undefined</tt> ignores methods which are not present in list returned by <tt>enumerable_methods</tt>
58
+ # <tt>:ignore_undefined</tt> ignores methods which are not present in <tt>Enumerable.instance_methods</tt>
67
59
  def use(runner, options)
68
60
  methods = Array(options[:for])
69
61
  raise 'no methods provided using :for option' if methods.empty?
70
62
  ignore_undefined = options[:ignore_undefined]
71
- enumerable_methods = self.enumerable_methods
63
+ enumerable_methods = Enumerable.instance_methods.map(&:to_s)
72
64
  methods.each do |method|
73
65
  unless ignore_undefined && !enumerable_methods.include?(method)
74
66
  class_eval <<-RUBY
@@ -81,15 +73,8 @@ class InThreads
81
73
  end
82
74
  end
83
75
 
84
- use :run_in_threads_block_result_irrelevant, :for => %w[each]
85
- use :run_in_threads_consecutive, :for => %w[
86
- all? any? none? one?
87
- detect find find_index drop_while take_while
88
- partition find_all select reject count
89
- collect map group_by max_by min_by minmax_by sort_by
90
- flat_map collect_concat
91
- ], :ignore_undefined => true
92
- use :run_in_threads_block_result_irrelevant, :for => %w[
76
+ use :run_in_threads_return_original_enum, :for => %w[each]
77
+ use :run_in_threads_return_original_enum, :for => %w[
93
78
  reverse_each
94
79
  each_with_index enum_with_index
95
80
  each_cons each_slice enum_cons enum_slice
@@ -97,6 +82,13 @@ class InThreads
97
82
  cycle
98
83
  each_entry
99
84
  ], :ignore_undefined => true
85
+ use :run_in_threads_consecutive, :for => %w[
86
+ all? any? none? one?
87
+ detect find find_index drop_while take_while
88
+ partition find_all select reject count
89
+ collect map group_by max_by min_by minmax_by sort_by
90
+ flat_map collect_concat
91
+ ], :ignore_undefined => true
100
92
  use :run_without_threads, :for => %w[
101
93
  inject reduce
102
94
  max min minmax sort
@@ -119,50 +111,15 @@ class InThreads
119
111
 
120
112
  protected
121
113
 
122
- # Use ThreadsWait to limit number of threads
123
- class ThreadLimiter
124
- # Initialize with limit
125
- def initialize(count)
126
- @count = count
127
- @waiter = ThreadsWait.new
128
- end
129
-
130
- # Without block behaves as <tt>new</tt>
131
- # With block yields it with <tt>self</tt> and ensures running of <tt>finalize</tt>
132
- def self.limit(count, &block)
133
- limiter = new(count)
134
- if block
135
- begin
136
- yield limiter
137
- ensure
138
- limiter.finalize
139
- end
140
- else
141
- limiter
142
- end
143
- end
144
-
145
- # Add thread to <tt>ThreadsWait</tt>, wait for finishing of one thread if limit reached
146
- def add(thread)
147
- if @waiter.threads.length + 1 >= @count
148
- @waiter.join(thread)
149
- else
150
- @waiter.join_nowait(thread)
151
- end
152
- end
153
-
154
- # Wait for waiting threads
155
- def finalize
156
- @waiter.all_waits
157
- end
158
- end
114
+ autoload :ThreadLimiter, 'in_threads/thread_limiter'
115
+ autoload :Filler, 'in_threads/filler'
159
116
 
160
117
  # Use for methods which don't use block result
161
- def run_in_threads_block_result_irrelevant(enumerable, method, *args, &block)
118
+ def run_in_threads_return_original_enum(enumerable, method, *args, &block)
162
119
  if block
163
120
  ThreadLimiter.limit(thread_count) do |limiter|
164
121
  enumerable.send(method, *args) do |*block_args|
165
- limiter.add(Thread.new(*block_args, &block))
122
+ limiter << Thread.new(*block_args, &block)
166
123
  end
167
124
  end
168
125
  else
@@ -174,19 +131,21 @@ protected
174
131
  def run_in_threads_consecutive(enumerable, method, *args, &block)
175
132
  if block
176
133
  begin
177
- queue = Queue.new
134
+ enum_a, enum_b = Filler.new(enumerable, 2).extractors
135
+ results = Queue.new
178
136
  runner = Thread.new do
137
+ Thread.current.priority = -1
179
138
  ThreadLimiter.limit(thread_count) do |limiter|
180
- enumerable.each do |object|
139
+ enum_a.each do |object|
181
140
  break if Thread.current[:stop]
182
141
  thread = Thread.new(object, &block)
183
- queue << thread
184
- limiter.add(thread)
142
+ results << thread
143
+ limiter << thread
185
144
  end
186
145
  end
187
146
  end
188
- enumerable.send(method, *args) do |object|
189
- queue.pop.value
147
+ enum_b.send(method, *args) do |object|
148
+ results.pop.value
190
149
  end
191
150
  ensure
192
151
  runner[:stop] = true
@@ -0,0 +1,55 @@
1
+ require 'thread'
2
+
3
+ class InThreads
4
+ class Filler
5
+ class Extractor
6
+ include Enumerable
7
+
8
+ def initialize(filler)
9
+ @filler = filler
10
+ @queue = []
11
+ end
12
+
13
+ def push(o)
14
+ @queue.push(o)
15
+ end
16
+
17
+ def each
18
+ begin
19
+ loop do
20
+ while @filler.synchronize{ @queue.empty? }
21
+ @filler.run
22
+ end
23
+ yield @filler.synchronize{ @queue.shift }
24
+ end
25
+ rescue ThreadError => e
26
+ end
27
+ nil # non reusable
28
+ end
29
+ end
30
+
31
+ attr_reader :extractors
32
+ def initialize(enum, extractor_count)
33
+ @extractors = Array.new(extractor_count){ Extractor.new(self) }
34
+ @mutex = Mutex.new
35
+ @filler = Thread.new do
36
+ enum.each do |o|
37
+ Thread.stop
38
+ synchronize do
39
+ @extractors.each do |extractor|
40
+ extractor.push(o)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def run
48
+ @filler.run
49
+ end
50
+
51
+ def synchronize(&block)
52
+ @mutex.synchronize(&block)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,41 @@
1
+ require 'thwait'
2
+
3
+ class InThreads
4
+ # Use ThreadsWait to limit number of threads
5
+ class ThreadLimiter
6
+ # Initialize with limit
7
+ def initialize(count)
8
+ @count = count
9
+ @waiter = ThreadsWait.new
10
+ end
11
+
12
+ # Without block behaves as <tt>new</tt>
13
+ # With block yields it with <tt>self</tt> and ensures running of <tt>finalize</tt>
14
+ def self.limit(count, &block)
15
+ limiter = new(count)
16
+ if block
17
+ begin
18
+ yield limiter
19
+ ensure
20
+ limiter.finalize
21
+ end
22
+ else
23
+ limiter
24
+ end
25
+ end
26
+
27
+ # Add thread to <tt>ThreadsWait</tt>, wait for finishing of one thread if limit reached
28
+ def <<(thread)
29
+ if @waiter.threads.length + 1 >= @count
30
+ @waiter.join(thread)
31
+ else
32
+ @waiter.join_nowait(thread)
33
+ end
34
+ end
35
+
36
+ # Wait for waiting threads
37
+ def finalize
38
+ @waiter.all_waits
39
+ end
40
+ end
41
+ end
@@ -1,23 +1,22 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper.rb'
2
2
 
3
3
  class Item
4
+ attr_reader :rand
4
5
  def initialize(i)
5
- @i, @rand = i, rand
6
+ @i = i
7
+ @rand = Kernel.rand
8
+ @sleep = Kernel.rand
6
9
  end
7
10
 
8
11
  class MiddleMatcher
9
12
  def ===(item)
10
13
  raise "#{item.inspect} is not an Item" unless item.is_a?(Item)
11
- (0.25..0.75) === item.instance_variable_get(:@rand)
14
+ (0.25..0.75) === item.rand
12
15
  end
13
16
  end
14
17
 
15
- def work
16
- sleep @rand * 0.008
17
- end
18
-
19
18
  def value
20
- work; @rand
19
+ sleep; rand
21
20
  end
22
21
 
23
22
  def check?
@@ -31,6 +30,12 @@ class Item
31
30
  def touch_n_check?(*args)
32
31
  touch(*args); check?
33
32
  end
33
+
34
+ private
35
+
36
+ def sleep
37
+ Kernel.sleep @sleep * 0.01
38
+ end
34
39
  end
35
40
 
36
41
  class ValueItem < Item
@@ -40,7 +45,7 @@ class ValueItem < Item
40
45
  end
41
46
 
42
47
  def value
43
- work; @value
48
+ sleep; @value
44
49
  end
45
50
 
46
51
  def check?
@@ -70,419 +75,444 @@ describe "in_threads" do
70
75
  Time.now - start
71
76
  end
72
77
 
73
- (Enumerable.instance_methods - 10.times.in_threads.class.instance_methods).each do |method|
74
- pending method
75
- end
78
+ describe "consistency" do
79
+ describe "verifying params" do
80
+ it "should complain about using with non enumerable" do
81
+ proc{ InThreads.new(1) }.should raise_error(ArgumentError)
82
+ end
76
83
 
77
- describe "verifying params" do
78
- it "should complain about using with non enumerable" do
79
- proc{ InThreads.new(1) }.should raise_error(ArgumentError)
80
- end
84
+ [1..10, 10.times, {}, []].each do |o|
85
+ it "should complain about using with #{o.class}" do
86
+ proc{ InThreads.new(o) }.should_not raise_error
87
+ end
88
+ end
81
89
 
82
- [1..10, 10.times, {}, []].each do |o|
83
- it "should complain about using with #{o.class}" do
84
- proc{ InThreads.new(o) }.should_not raise_error
90
+ it "should complain about using less than 2 threads" do
91
+ proc{ 10.times.in_threads(1) }.should raise_error(ArgumentError)
85
92
  end
86
- end
87
93
 
88
- it "should complain about using less than 2 threads" do
89
- proc{ 10.times.in_threads(1) }.should raise_error(ArgumentError)
94
+ it "should not complain about using 2 or more threads" do
95
+ proc{ 10.times.in_threads(2) }.should_not raise_error
96
+ end
90
97
  end
91
98
 
92
- it "should not complain about using 2 or more threads" do
93
- proc{ 10.times.in_threads(2) }.should_not raise_error
94
- end
95
- end
99
+ describe "in_threads" do
100
+ it "should not change existing instance" do
101
+ threaded = enum.in_threads(10)
102
+ proc{ threaded.in_threads(20) }.should_not change(threaded, :thread_count)
103
+ end
96
104
 
97
- describe "in_threads" do
98
- it "should not change existing instance" do
99
- threaded = enum.in_threads(10)
100
- proc{ threaded.in_threads(20) }.should_not change(threaded, :thread_count)
105
+ it "should create new instance with different title when called on WithProgress" do
106
+ threaded = enum.in_threads(10)
107
+ tthreaded = threaded.in_threads(20)
108
+ threaded.thread_count.should == 10
109
+ tthreaded.thread_count.should == 20
110
+ tthreaded.class.should == threaded.class
111
+ tthreaded.object_id.should_not == threaded.object_id
112
+ tthreaded.enumerable.should == threaded.enumerable
113
+ end
101
114
  end
102
115
 
103
- it "should create new instance with different title when called on WithProgress" do
104
- threaded = enum.in_threads(10)
105
- tthreaded = threaded.in_threads(20)
106
- threaded.thread_count.should == 10
107
- tthreaded.thread_count.should == 20
108
- tthreaded.class.should == threaded.class
109
- tthreaded.object_id.should_not == threaded.object_id
110
- tthreaded.enumerable.should == threaded.enumerable
116
+ describe "thread count" do
117
+ let(:enum){ 100.times.map{ |i| ValueItem.new(i, i < 50) } }
118
+
119
+ %w[each map all?].each do |method|
120
+ it "should run in specified number of threads for #{method}" do
121
+ @thread_count = 0
122
+ @max_thread_count = 0
123
+ @mutex = Mutex.new
124
+ enum.in_threads(13).send(method) do |o|
125
+ @mutex.synchronize do
126
+ @thread_count += 1
127
+ @max_thread_count = [@max_thread_count, @thread_count].max
128
+ end
129
+ res = o.check?
130
+ @mutex.synchronize do
131
+ @thread_count -= 1
132
+ end
133
+ res
134
+ end
135
+ @thread_count.should == 0
136
+ @max_thread_count.should == 13
137
+ end
138
+ end
111
139
  end
112
- end
113
140
 
114
- describe "thread count" do
115
- let(:enum){ 100.times.map{ |i| ValueItem.new(i, i < 50) } }
141
+ describe "underlying enumerable usage" do
142
+ class CheckEachCalls
143
+ include Enumerable
116
144
 
117
- %w[each map all?].each do |method|
118
- it "should run in specified number of threads for #{method}" do
119
- @thread_count = 0
120
- @max_thread_count = 0
121
- @mutex = Mutex.new
122
- enum.in_threads(13).send(method) do |o|
123
- @mutex.synchronize do
124
- @thread_count += 1
125
- @max_thread_count = [@max_thread_count, @thread_count].max
126
- end
127
- res = o.check?
128
- @mutex.synchronize do
129
- @thread_count -= 1
145
+ def each
146
+ each_started
147
+ 100.times.each do |i|
148
+ yield ValueItem.new(i, i < 50)
130
149
  end
131
- res
132
150
  end
133
- @thread_count.should == 0
134
- @max_thread_count.should == 13
151
+ end
152
+ let(:enum){ CheckEachCalls.new }
153
+
154
+ %w[each map all?].each do |method|
155
+ it "should call underlying enumerable.each only once for #{method}" do
156
+ enum.should_receive(:each_started).once
157
+ enum.in_threads(13).send(method, &:check?)
158
+ end
135
159
  end
136
160
  end
137
161
  end
138
162
 
139
- describe "each" do
140
- it "should return same enum after running" do
141
- enum.in_threads.each(&:value).should == enum
163
+ describe "methods" do
164
+ (Enumerable.instance_methods - 10.times.in_threads.class.instance_methods).each do |method|
165
+ pending method
142
166
  end
143
167
 
144
- it "should execute block for each element" do
145
- enum.each{ |o| o.should_receive(:touch).once }
146
- enum.in_threads.each(&:touch_n_value)
147
- end
168
+ describe "each" do
169
+ it "should return same enum after running" do
170
+ enum.in_threads.each(&:value).should == enum
171
+ end
148
172
 
149
- it "should run faster with threads" do
150
- measure{ enum.in_threads.each(&:work) }.should < measure{ enum.each(&:work) } * speed_coef
151
- end
173
+ it "should execute block for each element" do
174
+ enum.each{ |o| o.should_receive(:touch).once }
175
+ enum.in_threads.each(&:touch_n_value)
176
+ end
152
177
 
153
- it "should run faster with more threads" do
154
- measure{ enum.in_threads(10).each(&:work) }.should < measure{ enum.in_threads(2).each(&:work) } * speed_coef
155
- end
178
+ it "should run faster with threads" do
179
+ measure{ enum.in_threads.each(&:value) }.should < measure{ enum.each(&:value) } * speed_coef
180
+ end
181
+
182
+ it "should run faster with more threads" do
183
+ measure{ enum.in_threads(10).each(&:value) }.should < measure{ enum.in_threads(2).each(&:value) } * speed_coef
184
+ end
156
185
 
157
- it "should return same enum without block" do
158
- enum.in_threads.each.to_a.should == enum.each.to_a
186
+ it "should return same enum without block" do
187
+ enum.in_threads.each.to_a.should == enum.each.to_a
188
+ end
159
189
  end
160
- end
161
190
 
162
- %w[each_with_index enum_with_index].each do |method|
163
- describe_enum_method method do
164
- let(:runner){ proc{ |o, i| o.value } }
191
+ %w[each_with_index enum_with_index].each do |method|
192
+ describe_enum_method method do
193
+ let(:runner){ proc{ |o, i| o.value } }
194
+
195
+ it "should return same result with threads" do
196
+ enum.in_threads.send(method, &runner).should == enum.send(method, &runner)
197
+ end
198
+
199
+ it "should fire same objects" do
200
+ enum.send(method){ |o, i| o.should_receive(:touch).with(i).once }
201
+ enum.in_threads.send(method){ |o, i| o.touch_n_value(i) }
202
+ end
203
+
204
+ it "should run faster with threads" do
205
+ measure{ enum.in_threads.send(method, &runner) }.should < measure{ enum.send(method, &runner) } * speed_coef
206
+ end
207
+
208
+ it "should return same enum without block" do
209
+ enum.in_threads.send(method).to_a.should == enum.send(method).to_a
210
+ end
211
+ end
212
+ end
165
213
 
214
+ describe "reverse_each" do
166
215
  it "should return same result with threads" do
167
- enum.in_threads.send(method, &runner).should == enum.send(method, &runner)
216
+ enum.in_threads.reverse_each(&:value).should == enum.reverse_each(&:value)
168
217
  end
169
218
 
170
- it "should fire same objects" do
171
- enum.send(method){ |o, i| o.should_receive(:touch).with(i).once }
172
- enum.in_threads.send(method){ |o, i| o.touch_n_value(i) }
219
+ it "should fire same objects in reverse order" do
220
+ @order = mock('order', :notify => nil)
221
+ @order.should_receive(:notify).with(enum.last).ordered
222
+ @order.should_receive(:notify).with(enum[enum.length / 2]).ordered
223
+ @order.should_receive(:notify).with(enum.first).ordered
224
+ enum.reverse_each{ |o| o.should_receive(:touch).once }
225
+ @mutex = Mutex.new
226
+ enum.in_threads.reverse_each do |o|
227
+ @mutex.synchronize{ @order.notify(o) }
228
+ o.touch_n_value
229
+ end
173
230
  end
174
231
 
175
232
  it "should run faster with threads" do
176
- measure{ enum.in_threads.send(method, &runner) }.should < measure{ enum.send(method, &runner) } * speed_coef
233
+ measure{ enum.in_threads.reverse_each(&:value) }.should < measure{ enum.reverse_each(&:value) } * speed_coef
177
234
  end
178
235
 
179
236
  it "should return same enum without block" do
180
- enum.in_threads.send(method).to_a.should == enum.send(method).to_a
237
+ enum.in_threads.reverse_each.to_a.should == enum.reverse_each.to_a
181
238
  end
182
239
  end
183
- end
184
240
 
185
- describe "reverse_each" do
186
- it "should return same result with threads" do
187
- enum.in_threads.reverse_each(&:value).should == enum.reverse_each(&:value)
188
- end
241
+ %w[
242
+ all? any? none? one?
243
+ detect find find_index drop_while take_while
244
+ ].each do |method|
245
+ describe method do
246
+ let(:enum){ 100.times.map{ |i| ValueItem.new(i, i % 2 == 1) } }
189
247
 
190
- it "should fire same objects in reverse order" do
191
- @order = mock('order', :notify => nil)
192
- @order.should_receive(:notify).with(enum.last).ordered
193
- @order.should_receive(:notify).with(enum[enum.length / 2]).ordered
194
- @order.should_receive(:notify).with(enum.first).ordered
195
- enum.reverse_each{ |o| o.should_receive(:touch).once }
196
- @mutex = Mutex.new
197
- enum.in_threads.reverse_each do |o|
198
- @mutex.synchronize{ @order.notify(o) }
199
- o.touch_n_value
200
- end
201
- end
248
+ it "should return same result with threads" do
249
+ enum.in_threads.send(method, &:check?).should == enum.send(method, &:check?)
250
+ end
202
251
 
203
- it "should run faster with threads" do
204
- measure{ enum.in_threads.reverse_each(&:value) }.should < measure{ enum.reverse_each(&:value) } * speed_coef
205
- end
252
+ it "should fire same objects but not all" do
253
+ a = []
254
+ enum.send(method) do |o|
255
+ a << o
256
+ o.check?
257
+ end
206
258
 
207
- it "should return same enum without block" do
208
- enum.in_threads.reverse_each.to_a.should == enum.reverse_each.to_a
209
- end
210
- end
259
+ @a = []
260
+ @mutex = Mutex.new
261
+ enum.in_threads.send(method){ |o| @mutex.synchronize{ @a << o }; o.check? }
211
262
 
212
- %w[
213
- all? any? none? one?
214
- detect find find_index drop_while take_while
215
- ].each do |method|
216
- describe method do
217
- let(:enum){ 100.times.map{ |i| ValueItem.new(i, i % 2 == 1) } }
263
+ @a.length.should >= a.length
264
+ @a.length.should <= enum.length * 0.5
265
+ end
218
266
 
219
- it "should return same result with threads" do
220
- enum.in_threads.send(method, &:check?).should == enum.send(method, &:check?)
267
+ it "should run faster with threads" do
268
+ boolean = %w[all? drop_while take_while].include?(method)
269
+ enum = 30.times.map{ |i| ValueItem.new(i, boolean) }
270
+ measure{ enum.in_threads.send(method, &:check?) }.should < measure{ enum.send(method, &:check?) } * speed_coef
271
+ end
221
272
  end
273
+ end
222
274
 
223
- it "should fire same objects but not all" do
224
- a = []
225
- enum.send(method) do |o|
226
- a << o
227
- o.check?
275
+ %w[partition find_all select reject count].each do |method|
276
+ describe method do
277
+ it "should return same result with threads" do
278
+ enum.in_threads.send(method, &:check?).should == enum.send(method, &:check?)
228
279
  end
229
280
 
230
- @a = []
231
- @mutex = Mutex.new
232
- enum.in_threads.send(method){ |o| @mutex.synchronize{ @a << o }; o.check? }
233
-
234
- @a.length.should >= a.length
235
- @a.length.should <= enum.length / 2
236
- end
281
+ it "should fire same objects" do
282
+ enum.send(method){ |o| o.should_receive(:touch).once }
283
+ enum.in_threads.send(method, &:touch_n_check?)
284
+ end
237
285
 
238
- it "should run faster with threads" do
239
- value = %w[all? drop_while take_while].include?(method)
240
- enum = 30.times.map{ |i| ValueItem.new(i, value) }
241
- measure{ enum.in_threads.send(method, &:check?) }.should < measure{ enum.send(method, &:check?) } * speed_coef
286
+ it "should run faster with threads" do
287
+ measure{ enum.in_threads.send(method, &:check?) }.should < measure{ enum.send(method, &:check?) } * speed_coef
288
+ end
242
289
  end
243
290
  end
244
- end
245
291
 
246
- %w[partition find_all select reject count].each do |method|
247
- describe method do
248
- it "should return same result with threads" do
249
- enum.in_threads.send(method, &:check?).should == enum.send(method, &:check?)
250
- end
292
+ %w[collect map group_by max_by min_by minmax_by sort_by].each do |method|
293
+ describe method do
294
+ it "should return same result with threads" do
295
+ enum.in_threads.send(method, &:value).should == enum.send(method, &:value)
296
+ end
251
297
 
252
- it "should fire same objects" do
253
- enum.send(method){ |o| o.should_receive(:touch).once }
254
- enum.in_threads.send(method, &:touch_n_check?)
255
- end
298
+ it "should fire same objects" do
299
+ enum.send(method){ |o| o.should_receive(:touch).once; 0 }
300
+ enum.in_threads.send(method, &:touch_n_value)
301
+ end
256
302
 
257
- it "should run faster with threads" do
258
- measure{ enum.in_threads.send(method, &:check?) }.should < measure{ enum.send(method, &:check?) } * speed_coef
303
+ it "should run faster with threads" do
304
+ measure{ enum.in_threads.send(method, &:value) }.should < measure{ enum.send(method, &:value) } * speed_coef
305
+ end
259
306
  end
260
307
  end
261
- end
262
308
 
263
- %w[collect map group_by max_by min_by minmax_by sort_by].each do |method|
264
- describe method do
265
- it "should return same result with threads" do
266
- enum.in_threads.send(method, &:value).should == enum.send(method, &:value)
267
- end
309
+ %w[each_cons each_slice enum_slice enum_cons].each do |method|
310
+ describe_enum_method method do
311
+ let(:runner){ proc{ |a| a.each(&:value) } }
268
312
 
269
- it "should fire same objects" do
270
- enum.send(method){ |o| o.should_receive(:touch).once; 0 }
271
- enum.in_threads.send(method, &:touch_n_value)
272
- end
313
+ it "should fire same objects" do
314
+ enum.send(method, 3){ |a| a.first.should_receive(:touch).with(a).once }
315
+ enum.in_threads.send(method, 3){ |a| a.first.touch_n_value(a) }
316
+ end
273
317
 
274
- it "should run faster with threads" do
275
- measure{ enum.in_threads.send(method, &:value) }.should < measure{ enum.send(method, &:value) } * speed_coef
318
+ it "should return same with block" do
319
+ enum.in_threads.send(method, 3, &runner).should == enum.send(method, 3, &runner)
320
+ end
321
+
322
+ it "should run faster with threads" do
323
+ measure{ enum.in_threads.send(method, 3, &runner) }.should < measure{ enum.send(method, 3, &runner) } * speed_coef
324
+ end
325
+
326
+ it "should return same without block" do
327
+ enum.in_threads.send(method, 3).to_a.should == enum.send(method, 3).to_a
328
+ end
276
329
  end
277
330
  end
278
- end
279
331
 
280
- %w[each_cons each_slice enum_slice enum_cons].each do |method|
281
- describe_enum_method method do
332
+ describe "zip" do
282
333
  let(:runner){ proc{ |a| a.each(&:value) } }
283
334
 
284
335
  it "should fire same objects" do
285
- enum.send(method, 3){ |a| a.first.should_receive(:touch).with(a).once }
286
- enum.in_threads.send(method, 3){ |a| a.first.touch_n_value(a) }
336
+ enum.zip(enum, enum){ |a| a.first.should_receive(:touch).with(a).once }
337
+ enum.in_threads.zip(enum, enum){ |a| a.first.touch_n_value(a) }
287
338
  end
288
339
 
289
340
  it "should return same with block" do
290
- enum.in_threads.send(method, 3, &runner).should == enum.send(method, 3, &runner)
341
+ enum.in_threads.zip(enum, enum, &runner).should == enum.zip(enum, enum, &runner)
291
342
  end
292
343
 
293
344
  it "should run faster with threads" do
294
- measure{ enum.in_threads.send(method, 3, &runner) }.should < measure{ enum.send(method, 3, &runner) } * speed_coef
345
+ measure{ enum.in_threads.zip(enum, enum, &runner) }.should < measure{ enum.zip(enum, enum, &runner) } * speed_coef
295
346
  end
296
347
 
297
348
  it "should return same without block" do
298
- enum.in_threads.send(method, 3).to_a.should == enum.send(method, 3).to_a
349
+ enum.in_threads.zip(enum, enum).should == enum.zip(enum, enum)
299
350
  end
300
351
  end
301
- end
302
-
303
- describe "zip" do
304
- let(:runner){ proc{ |a| a.each(&:value) } }
305
-
306
- it "should fire same objects" do
307
- enum.zip(enum, enum){ |a| a.first.should_receive(:touch).with(a).once }
308
- enum.in_threads.zip(enum, enum){ |a| a.first.touch_n_value(a) }
309
- end
310
-
311
- it "should return same with block" do
312
- enum.in_threads.zip(enum, enum, &runner).should == enum.zip(enum, enum, &runner)
313
- end
314
-
315
- it "should run faster with threads" do
316
- measure{ enum.in_threads.zip(enum, enum, &runner) }.should < measure{ enum.zip(enum, enum, &runner) } * speed_coef
317
- end
318
-
319
- it "should return same without block" do
320
- enum.in_threads.zip(enum, enum).should == enum.zip(enum, enum)
321
- end
322
- end
323
-
324
- describe "cycle" do
325
- it "should fire same objects" do
326
- enum.cycle(1){ |o| o.should_receive(:touch).exactly(3).times }
327
- enum.in_threads.cycle(3, &:touch_n_value)
328
- end
329
-
330
- it "should run faster with threads" do
331
- measure{ enum.in_threads.cycle(3, &:work) }.should < measure{ enum.cycle(3, &:work) } * speed_coef
332
- end
333
-
334
- it "should return same enum without block" do
335
- enum.in_threads.cycle(3).to_a.should == enum.cycle(3).to_a
336
- end
337
- end
338
352
 
339
- describe "grep" do
340
- let(:matcher){ Item::MiddleMatcher.new }
341
-
342
- it "should fire same objects" do
343
- enum.each{ |o| o.should_receive(:touch).exactly(matcher === o ? 1 : 0).times }
344
- enum.in_threads.grep(matcher, &:touch_n_value)
345
- end
353
+ describe "cycle" do
354
+ it "should fire same objects" do
355
+ enum.cycle(1){ |o| o.should_receive(:touch).exactly(3).times }
356
+ enum.in_threads.cycle(3, &:touch_n_value)
357
+ end
346
358
 
347
- it "should return same with block" do
348
- enum.in_threads.grep(matcher, &:value).should == enum.grep(matcher, &:value)
349
- end
359
+ it "should run faster with threads" do
360
+ measure{ enum.in_threads.cycle(3, &:value) }.should < measure{ enum.cycle(3, &:value) } * speed_coef
361
+ end
350
362
 
351
- it "should run faster with threads" do
352
- measure{ enum.in_threads.grep(matcher, &:value) }.should < measure{ enum.grep(matcher, &:value) } * speed_coef
363
+ it "should return same enum without block" do
364
+ enum.in_threads.cycle(3).to_a.should == enum.cycle(3).to_a
365
+ end
353
366
  end
354
367
 
355
- it "should return same without block" do
356
- enum.in_threads.grep(matcher).should == enum.grep(matcher)
357
- end
358
- end
368
+ describe "grep" do
369
+ let(:matcher){ Item::MiddleMatcher.new }
359
370
 
360
- describe_enum_method "each_entry" do
361
- class EachEntryYielder
362
- include Enumerable
363
- def each
364
- 10.times{ yield 1 }
365
- 10.times{ yield 2, 3 }
366
- 10.times{ yield }
371
+ it "should fire same objects" do
372
+ enum.each{ |o| o.should_receive(:touch).exactly(matcher === o ? 1 : 0).times }
373
+ enum.in_threads.grep(matcher, &:touch_n_value)
367
374
  end
368
- end
369
-
370
- let(:enum){ EachEntryYielder.new }
371
- let(:runner){ proc{ |o| ValueItem.new(0, o).work } }
372
375
 
373
- it "should return same result with threads" do
374
- enum.in_threads.each_entry(&runner).should == enum.each_entry(&runner)
375
- end
376
+ it "should return same with block" do
377
+ enum.in_threads.grep(matcher, &:value).should == enum.grep(matcher, &:value)
378
+ end
376
379
 
377
- it "should execute block for each element" do
378
- @order = mock('order')
379
- @order.should_receive(:notify).with(1).exactly(10).times.ordered
380
- @order.should_receive(:notify).with([2, 3]).exactly(10).times.ordered
381
- @order.should_receive(:notify).with(nil).exactly(10).times.ordered
382
- @mutex = Mutex.new
383
- enum.in_threads.each_entry do |o|
384
- @mutex.synchronize{ @order.notify(o) }
385
- runner[]
380
+ it "should run faster with threads" do
381
+ measure{ enum.in_threads.grep(matcher, &:value) }.should < measure{ enum.grep(matcher, &:value) } * speed_coef
386
382
  end
387
- end
388
383
 
389
- it "should run faster with threads" do
390
- measure{ enum.in_threads.each_entry(&runner) }.should < measure{ enum.each_entry(&runner) } * speed_coef
384
+ it "should return same without block" do
385
+ enum.in_threads.grep(matcher).should == enum.grep(matcher)
386
+ end
391
387
  end
392
388
 
393
- it "should return same enum without block" do
394
- enum.in_threads.each_entry.to_a.should == enum.each_entry.to_a
395
- end
396
- end
389
+ describe_enum_method "each_entry" do
390
+ class EachEntryYielder
391
+ include Enumerable
392
+ def each
393
+ 10.times{ yield 1 }
394
+ 10.times{ yield 2, 3 }
395
+ 10.times{ yield }
396
+ end
397
+ end
397
398
 
398
- %w[flat_map collect_concat].each do |method|
399
- describe_enum_method method do
400
- let(:enum){ 20.times.map{ |i| Item.new(i) }.each_slice(3) }
401
- let(:runner){ proc{ |a| a.map(&:value) } }
399
+ let(:enum){ EachEntryYielder.new }
400
+ let(:runner){ proc{ |o| ValueItem.new(0, o).value } }
402
401
 
403
402
  it "should return same result with threads" do
404
- enum.in_threads.send(method, &runner).should == enum.send(method, &runner)
403
+ enum.in_threads.each_entry(&runner).should == enum.each_entry(&runner)
405
404
  end
406
405
 
407
- it "should fire same objects" do
408
- enum.send(method){ |a| a.each{ |o| o.should_receive(:touch).with(a).once } }
409
- enum.in_threads.send(method){ |a| a.each{ |o| o.touch_n_value(a) } }
406
+ it "should execute block for each element" do
407
+ @order = mock('order')
408
+ @order.should_receive(:notify).with(1).exactly(10).times.ordered
409
+ @order.should_receive(:notify).with([2, 3]).exactly(10).times.ordered
410
+ @order.should_receive(:notify).with(nil).exactly(10).times.ordered
411
+ @mutex = Mutex.new
412
+ enum.in_threads.each_entry do |o|
413
+ @mutex.synchronize{ @order.notify(o) }
414
+ runner[]
415
+ end
410
416
  end
411
417
 
412
418
  it "should run faster with threads" do
413
- measure{ enum.in_threads.send(method, &runner) }.should < measure{ enum.send(method, &runner) } * speed_coef
419
+ measure{ enum.in_threads.each_entry(&runner) }.should < measure{ enum.each_entry(&runner) } * speed_coef
414
420
  end
415
421
 
416
422
  it "should return same enum without block" do
417
- enum.in_threads.send(method).to_a.should == enum.send(method).to_a
423
+ enum.in_threads.each_entry.to_a.should == enum.each_entry.to_a
418
424
  end
419
425
  end
420
- end
421
426
 
422
- context "unthreaded" do
423
- %w[inject reduce].each do |method|
424
- describe method do
425
- it "should return same result" do
426
- combiner = proc{ |memo, o| memo + o.value }
427
- enum.in_threads.send(method, 0, &combiner).should == enum.send(method, 0, &combiner)
427
+ %w[flat_map collect_concat].each do |method|
428
+ describe_enum_method method do
429
+ let(:enum){ 20.times.map{ |i| Item.new(i) }.each_slice(3) }
430
+ let(:runner){ proc{ |a| a.map(&:value) } }
431
+
432
+ it "should return same result with threads" do
433
+ enum.in_threads.send(method, &runner).should == enum.send(method, &runner)
428
434
  end
429
- end
430
- end
431
435
 
432
- %w[max min minmax sort].each do |method|
433
- describe method do
434
- it "should return same result" do
435
- comparer = proc{ |a, b| a.value <=> b.value }
436
- enum.in_threads.send(method, &comparer).should == enum.send(method, &comparer)
436
+ it "should fire same objects" do
437
+ enum.send(method){ |a| a.each{ |o| o.should_receive(:touch).with(a).once } }
438
+ enum.in_threads.send(method){ |a| a.each{ |o| o.touch_n_value(a) } }
439
+ end
440
+
441
+ it "should run faster with threads" do
442
+ measure{ enum.in_threads.send(method, &runner) }.should < measure{ enum.send(method, &runner) } * speed_coef
443
+ end
444
+
445
+ it "should return same enum without block" do
446
+ enum.in_threads.send(method).to_a.should == enum.send(method).to_a
437
447
  end
438
448
  end
439
449
  end
440
450
 
441
- %w[to_a entries].each do |method|
442
- describe method do
443
- it "should return same result" do
444
- enum.in_threads.send(method).should == enum.send(method)
451
+ context "unthreaded" do
452
+ %w[inject reduce].each do |method|
453
+ describe method do
454
+ it "should return same result" do
455
+ combiner = proc{ |memo, o| memo + o.value }
456
+ enum.in_threads.send(method, 0, &combiner).should == enum.send(method, 0, &combiner)
457
+ end
445
458
  end
446
459
  end
447
- end
448
460
 
449
- %w[drop take].each do |method|
450
- describe method do
451
- it "should return same result" do
452
- enum.in_threads.send(method, 2).should == enum.send(method, 2)
461
+ %w[max min minmax sort].each do |method|
462
+ describe method do
463
+ it "should return same result" do
464
+ comparer = proc{ |a, b| a.value <=> b.value }
465
+ enum.in_threads.send(method, &comparer).should == enum.send(method, &comparer)
466
+ end
453
467
  end
454
468
  end
455
- end
456
469
 
457
- %w[first].each do |method|
458
- describe method do
459
- it "should return same result" do
460
- enum.in_threads.send(method).should == enum.send(method)
461
- enum.in_threads.send(method, 3).should == enum.send(method, 3)
470
+ %w[to_a entries].each do |method|
471
+ describe method do
472
+ it "should return same result" do
473
+ enum.in_threads.send(method).should == enum.send(method)
474
+ end
462
475
  end
463
476
  end
464
- end
465
477
 
466
- %w[include? member?].each do |method|
467
- describe method do
468
- it "should return same result" do
469
- enum.in_threads.send(method, enum[10]).should == enum.send(method, enum[10])
478
+ %w[drop take].each do |method|
479
+ describe method do
480
+ it "should return same result" do
481
+ enum.in_threads.send(method, 2).should == enum.send(method, 2)
482
+ end
470
483
  end
471
484
  end
472
- end
473
485
 
474
- describe_enum_method "each_with_object" do
475
- let(:runner){ proc{ |o, h| h[o.value] = true } }
486
+ %w[first].each do |method|
487
+ describe method do
488
+ it "should return same result" do
489
+ enum.in_threads.send(method).should == enum.send(method)
490
+ enum.in_threads.send(method, 3).should == enum.send(method, 3)
491
+ end
492
+ end
493
+ end
476
494
 
477
- it "should return same result" do
478
- enum.in_threads.each_with_object({}, &runner).should == enum.each_with_object({}, &runner)
495
+ %w[include? member?].each do |method|
496
+ describe method do
497
+ it "should return same result" do
498
+ enum.in_threads.send(method, enum[10]).should == enum.send(method, enum[10])
499
+ end
500
+ end
479
501
  end
480
- end
481
502
 
482
- %w[chunk slice_before].each do |method|
483
- describe_enum_method method do
503
+ describe_enum_method "each_with_object" do
504
+ let(:runner){ proc{ |o, h| h[o.value] = true } }
505
+
484
506
  it "should return same result" do
485
- enum.in_threads.send(method, &:check?).to_a.should == enum.send(method, &:check?).to_a
507
+ enum.in_threads.each_with_object({}, &runner).should == enum.each_with_object({}, &runner)
508
+ end
509
+ end
510
+
511
+ %w[chunk slice_before].each do |method|
512
+ describe_enum_method method do
513
+ it "should return same result" do
514
+ enum.in_threads.send(method, &:check?).to_a.should == enum.send(method, &:check?).to_a
515
+ end
486
516
  end
487
517
  end
488
518
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: in_threads
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 17
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 1
9
- - 0
10
- version: 1.1.0
9
+ - 1
10
+ version: 1.1.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ivan Kuchin
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-12-08 00:00:00 Z
18
+ date: 2011-12-13 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: rspec
@@ -45,6 +45,8 @@ files:
45
45
  - README.markdown
46
46
  - in_threads.gemspec
47
47
  - lib/in_threads.rb
48
+ - lib/in_threads/filler.rb
49
+ - lib/in_threads/thread_limiter.rb
48
50
  - spec/in_threads_spec.rb
49
51
  - spec/spec_helper.rb
50
52
  homepage: http://github.com/toy/in_threads
@@ -76,7 +78,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
78
  requirements: []
77
79
 
78
80
  rubyforge_project: in_threads
79
- rubygems_version: 1.8.11
81
+ rubygems_version: 1.8.12
80
82
  signing_key:
81
83
  specification_version: 3
82
84
  summary: Execute ruby code in parallel