in_threads 1.1.0 → 1.1.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/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