in_threads 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +36 -24
- data/.travis.yml +9 -7
- data/CHANGELOG.markdown +82 -0
- data/LICENSE.txt +1 -1
- data/README.markdown +11 -3
- data/in_threads.gemspec +3 -3
- data/lib/in_threads.rb +152 -114
- data/spec/in_threads_spec.rb +548 -509
- metadata +7 -8
- data/spec/spec_helper.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88e40a6e4c911af5eb5187d9b7c8ee9a6597d19a
|
4
|
+
data.tar.gz: 93cd8928eaea21fa27779ec68fab8a9d22f71af8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 79915ecf8198c7b8dfd5df0b17147b073f9d3de14e9cfda8ed31e89ed48c2c4f407e5c41d322156628660810a96db2db9a741f26142562525af28d44700201c6
|
7
|
+
data.tar.gz: f83d77ea6f937508b9b33b998091d20d69a2432adaebf37e9749034fef2221dc33ee5cf83f6d6c341f6fab321a34947191508e7978b77fdd84ec965fbad43e56
|
data/.rubocop.yml
CHANGED
@@ -2,49 +2,67 @@ AllCops:
|
|
2
2
|
Exclude:
|
3
3
|
- '*.gemspec'
|
4
4
|
|
5
|
+
Layout/AccessModifierIndentation:
|
6
|
+
EnforcedStyle: outdent
|
7
|
+
|
8
|
+
Layout/CaseIndentation:
|
9
|
+
EnforcedStyle: end
|
10
|
+
|
11
|
+
Layout/DotPosition:
|
12
|
+
EnforcedStyle: trailing
|
13
|
+
|
14
|
+
Layout/IndentHash:
|
15
|
+
EnforcedStyle: consistent
|
16
|
+
|
17
|
+
Layout/SpaceBeforeBlockBraces:
|
18
|
+
EnforcedStyle: no_space
|
19
|
+
|
20
|
+
Layout/SpaceInsideHashLiteralBraces:
|
21
|
+
EnforcedStyle: no_space
|
22
|
+
|
23
|
+
Lint/AmbiguousBlockAssociation:
|
24
|
+
Enabled: false
|
25
|
+
|
5
26
|
Lint/EndAlignment:
|
6
27
|
EnforcedStyleAlignWith: variable
|
7
28
|
|
29
|
+
Lint/EnsureReturn:
|
30
|
+
Enabled: false
|
31
|
+
|
32
|
+
Lint/RescueException:
|
33
|
+
Enabled: false
|
34
|
+
|
8
35
|
Metrics/AbcSize:
|
9
|
-
Max:
|
36
|
+
Max: 35
|
10
37
|
|
11
38
|
Metrics/BlockLength:
|
12
39
|
Exclude:
|
13
40
|
- 'spec/**/*.rb'
|
14
41
|
|
15
42
|
Metrics/ClassLength:
|
16
|
-
Max:
|
43
|
+
Max: 200
|
44
|
+
|
45
|
+
Metrics/CyclomaticComplexity:
|
46
|
+
Max: 10
|
47
|
+
|
48
|
+
Metrics/LineLength:
|
49
|
+
Max: 120
|
17
50
|
|
18
51
|
Metrics/MethodLength:
|
19
|
-
Max:
|
52
|
+
Max: 30
|
20
53
|
|
21
54
|
Performance/RedundantBlockCall:
|
22
55
|
Enabled: false
|
23
56
|
|
24
|
-
Style/AccessModifierIndentation:
|
25
|
-
EnforcedStyle: outdent
|
26
|
-
|
27
|
-
Style/CaseIndentation:
|
28
|
-
EnforcedStyle: end
|
29
|
-
|
30
|
-
Style/DotPosition:
|
31
|
-
EnforcedStyle: trailing
|
32
|
-
|
33
57
|
Style/DoubleNegation:
|
34
58
|
Enabled: false
|
35
59
|
|
36
|
-
Style/Encoding:
|
37
|
-
EnforcedStyle: when_needed
|
38
|
-
|
39
60
|
Style/HashSyntax:
|
40
61
|
EnforcedStyle: hash_rockets
|
41
62
|
|
42
63
|
Style/IfUnlessModifier:
|
43
64
|
MaxLineLength: 40
|
44
65
|
|
45
|
-
Style/IndentHash:
|
46
|
-
EnforcedStyle: consistent
|
47
|
-
|
48
66
|
Style/ParallelAssignment:
|
49
67
|
Enabled: false
|
50
68
|
|
@@ -59,12 +77,6 @@ Style/Semicolon:
|
|
59
77
|
Style/SignalException:
|
60
78
|
EnforcedStyle: semantic
|
61
79
|
|
62
|
-
Style/SpaceBeforeBlockBraces:
|
63
|
-
EnforcedStyle: no_space
|
64
|
-
|
65
|
-
Style/SpaceInsideHashLiteralBraces:
|
66
|
-
EnforcedStyle: no_space
|
67
|
-
|
68
80
|
Style/TrailingCommaInArguments:
|
69
81
|
EnforcedStyleForMultiline: no_comma
|
70
82
|
|
data/.travis.yml
CHANGED
@@ -5,21 +5,23 @@ rvm:
|
|
5
5
|
- '1.9.3-p551'
|
6
6
|
- '2.0.0-p648'
|
7
7
|
- '2.1.10'
|
8
|
-
- '2.2.
|
9
|
-
- '2.3.
|
10
|
-
- '2.4.
|
8
|
+
- '2.2.8'
|
9
|
+
- '2.3.5'
|
10
|
+
- '2.4.2'
|
11
11
|
- 'ruby-head'
|
12
|
-
- 'jruby-1.7.26'
|
13
12
|
- 'jruby-9.0.5.0'
|
14
|
-
- 'jruby-9.1.
|
13
|
+
- 'jruby-9.1.9.0'
|
14
|
+
before_install:
|
15
|
+
- gem update --system
|
16
|
+
- gem update bundler
|
15
17
|
script: bundle exec rspec
|
16
18
|
matrix:
|
17
19
|
include:
|
18
20
|
- env: RUBOCOP=✓
|
19
|
-
rvm: '2.4.
|
21
|
+
rvm: '2.4.2'
|
20
22
|
script: bundle exec rubocop
|
21
23
|
- env: CHECK_RUBIES=✓
|
22
|
-
rvm: '2.4.
|
24
|
+
rvm: '2.4.2'
|
23
25
|
script: bundle exec travis_check_rubies
|
24
26
|
allow_failures:
|
25
27
|
- rvm: 'ruby-head'
|
data/CHANGELOG.markdown
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# ChangeLog
|
2
|
+
|
3
|
+
## unreleased
|
4
|
+
|
5
|
+
## v1.5.0 (2017-11-17)
|
6
|
+
|
7
|
+
* Use thread pool instead of creating a thread for every iteration [@toy](https://github.com/toy)
|
8
|
+
* Handle `break` (also with argument) and exceptions raised in `each` method of enumerable [@toy](https://github.com/toy)
|
9
|
+
* Register `lazy` to run without threads [@toy](https://github.com/toy)
|
10
|
+
|
11
|
+
## v1.4.0 (2017-03-19)
|
12
|
+
|
13
|
+
* Register `sum` and `uniq` to run in threads and `chunk_while` to run without threads [@toy](https://github.com/toy)
|
14
|
+
* Register `grep_v` to call block in threads [@toy](https://github.com/toy)
|
15
|
+
* Fix Segmentation fault in ruby 2.0 [@toy](https://github.com/toy)
|
16
|
+
* Fix not sending all block arguments to methods not returning original enumerable [@toy](https://github.com/toy)
|
17
|
+
* Fix for a [bug](https://bugs.ruby-lang.org/issues/13313) in ruby 2.4.0 causing Segmentation fault [@toy](https://github.com/toy)
|
18
|
+
* Clean up documentation [#1](https://github.com/toy/in_threads/pull/1) [@hollingberry](https://github.com/hollingberry)
|
19
|
+
|
20
|
+
## v1.3.1 (2015-01-09)
|
21
|
+
|
22
|
+
* Register `to_h`, `slice_after` and `slice_when` to run without threads [@toy](https://github.com/toy)
|
23
|
+
* Remove special handling of methods running without threads [@toy](https://github.com/toy)
|
24
|
+
|
25
|
+
## v1.3.0 (2014-10-31)
|
26
|
+
|
27
|
+
* Fix not thread safe code for jruby [@toy](https://github.com/toy)
|
28
|
+
|
29
|
+
## v1.2.2 (2014-08-08)
|
30
|
+
|
31
|
+
* Fix silencing exceptions raised in blocks [@toy](https://github.com/toy)
|
32
|
+
|
33
|
+
## v1.2.1 (2014-04-06)
|
34
|
+
|
35
|
+
* Use explicit requires instead of autoload [@toy](https://github.com/toy)
|
36
|
+
|
37
|
+
## v1.2.0 (2013-08-20)
|
38
|
+
|
39
|
+
* Befriend with [`progress`](https://rubygems.org/gems/progress) gem [@toy](https://github.com/toy)
|
40
|
+
* Use `Delegator` instead of undefining methods [@toy](https://github.com/toy)
|
41
|
+
|
42
|
+
## v1.1.2 (2013-08-02)
|
43
|
+
|
44
|
+
* Fix for jruby stalling [@toy](https://github.com/toy)
|
45
|
+
* Fix for rubinius not raising `NoMethodError` for undefined methods [@toy](https://github.com/toy)
|
46
|
+
* Register `to_set` to run without threads [@toy](https://github.com/toy)
|
47
|
+
|
48
|
+
## v1.1.1 (2011-12-13)
|
49
|
+
|
50
|
+
* Decrease priority of initiating block execution for methods not returning original enumerable [@toy](https://github.com/toy)
|
51
|
+
* Call each of enumerable only once for methods not returning original enumerable [@toy](https://github.com/toy)
|
52
|
+
* Remove class method `enumerable_methods` [@toy](https://github.com/toy)
|
53
|
+
|
54
|
+
## v1.1.0 (2011-12-08)
|
55
|
+
|
56
|
+
* Register `chunk` and `slice_before` to run without threads [@toy](https://github.com/toy)
|
57
|
+
* Register `flat_map` and `collect_concat` to run in threads [@toy](https://github.com/toy)
|
58
|
+
* Register `each_entry` to run in threads [@toy](https://github.com/toy)
|
59
|
+
* Register `each_with_object` to run without threads [@toy](https://github.com/toy)
|
60
|
+
* Add argument checking [@toy](https://github.com/toy)
|
61
|
+
* Use faster method for `each` [@toy](https://github.com/toy)
|
62
|
+
|
63
|
+
## v1.0.0 (2011-12-05)
|
64
|
+
|
65
|
+
* Fix for blocks with multiple arguments (`each_with_index`, `enum_with_index`) [@toy](https://github.com/toy)
|
66
|
+
* Rewrite: properly working with all compatible `Enumerable` methods, otherwise running without threads [@toy](https://github.com/toy)
|
67
|
+
|
68
|
+
## v0.0.4 (2010-12-15)
|
69
|
+
|
70
|
+
* Internal gem changes [@toy](https://github.com/toy)
|
71
|
+
|
72
|
+
## v0.0.3 (2010-07-13)
|
73
|
+
|
74
|
+
* Fix thread count overflow [@toy](https://github.com/toy)
|
75
|
+
|
76
|
+
## v0.0.2 (2009-12-29)
|
77
|
+
|
78
|
+
* Internal gem changes [@toy](https://github.com/toy)
|
79
|
+
|
80
|
+
## v0.0.1 (2009-09-21)
|
81
|
+
|
82
|
+
* Initial [@toy](https://github.com/toy)
|
data/LICENSE.txt
CHANGED
data/README.markdown
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
# in_threads
|
8
8
|
|
9
|
-
|
9
|
+
Run all possible enumerable methods in concurrent/parallel threads.
|
10
10
|
|
11
11
|
```ruby
|
12
12
|
urls.in_threads(20).map do |url|
|
@@ -28,7 +28,7 @@ gem 'in_threads'
|
|
28
28
|
$ bundle install
|
29
29
|
```
|
30
30
|
|
31
|
-
Or
|
31
|
+
Or install globally:
|
32
32
|
|
33
33
|
```sh
|
34
34
|
$ gem install in_threads
|
@@ -94,6 +94,14 @@ You can call any `Enumerable` method, but some (`#inject`, `#reduce`, `#max`,
|
|
94
94
|
`#min`, `#sort`, `#to_a`, and others) cannot run concurrently, and so will
|
95
95
|
simply act as if `in_threads` wasn't used.
|
96
96
|
|
97
|
+
### Break and exceptions
|
98
|
+
|
99
|
+
Exceptions are caught and re-thrown after allowing blocks that are still running to finish.
|
100
|
+
|
101
|
+
**IMPORTANT**: only the first encountered exception is propagated, so it is recommended to handle exceptions in the block.
|
102
|
+
|
103
|
+
`break` is handled in ruby >= 1.9 and should be handled in jruby [after 9.1.9.0](https://github.com/jruby/jruby/issues/4697). Handling is done in special way: as blocks are run outside of original context, calls to `break` cause `LocalJumpError` which is caught and its result is returned.
|
104
|
+
|
97
105
|
## Copyright
|
98
106
|
|
99
|
-
Copyright (c)
|
107
|
+
Copyright (c) 2009-2017 Ivan Kuchin. See LICENSE.txt for details.
|
data/in_threads.gemspec
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'in_threads'
|
5
|
-
s.version = '1.
|
6
|
-
s.summary = %q{
|
5
|
+
s.version = '1.5.0'
|
6
|
+
s.summary = %q{Run all possible enumerable methods in concurrent/parallel threads}
|
7
7
|
s.homepage = "http://github.com/toy/#{s.name}"
|
8
8
|
s.authors = ['Ivan Kuchin']
|
9
9
|
s.license = 'MIT'
|
@@ -18,6 +18,6 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.add_development_dependency 'rspec', '~> 3.0'
|
19
19
|
s.add_development_dependency 'rspec-retry', '~> 0.3'
|
20
20
|
if RUBY_VERSION >= '2.0'
|
21
|
-
s.add_development_dependency 'rubocop', '~> 0.
|
21
|
+
s.add_development_dependency 'rubocop', '~> 0.49'
|
22
22
|
end
|
23
23
|
end
|
data/lib/in_threads.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'thread'
|
2
|
-
require 'thwait'
|
3
2
|
require 'delegate'
|
4
3
|
|
5
4
|
Enumerable.class_eval do
|
@@ -31,84 +30,6 @@ end
|
|
31
30
|
class InThreads < SimpleDelegator
|
32
31
|
protected :__getobj__, :__setobj__
|
33
32
|
|
34
|
-
# Use ThreadsWait to limit number of threads
|
35
|
-
class ThreadLimiter
|
36
|
-
# Initialize with limit
|
37
|
-
def initialize(count)
|
38
|
-
@count = count
|
39
|
-
@waiter = ThreadsWait.new
|
40
|
-
end
|
41
|
-
|
42
|
-
# Without block behaves as `new`
|
43
|
-
# With block yields it with `self` and ensures running of `finalize`
|
44
|
-
def self.limit(count, &block)
|
45
|
-
limiter = new(count)
|
46
|
-
if block
|
47
|
-
begin
|
48
|
-
yield limiter
|
49
|
-
ensure
|
50
|
-
limiter.finalize
|
51
|
-
end
|
52
|
-
else
|
53
|
-
limiter
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
# Add thread to `ThreadsWait`, wait for finishing of one thread if limit
|
58
|
-
# reached
|
59
|
-
def <<(thread)
|
60
|
-
@waiter.join_nowait(thread)
|
61
|
-
@waiter.next_wait.join unless @waiter.threads.length < @count
|
62
|
-
end
|
63
|
-
|
64
|
-
# Wait for waiting threads
|
65
|
-
def finalize
|
66
|
-
@waiter.all_waits(&:join)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
# Yield objects of one enum in multiple places
|
71
|
-
class Splitter
|
72
|
-
# Enumerable using Queue
|
73
|
-
class Transfer
|
74
|
-
include Enumerable
|
75
|
-
|
76
|
-
def initialize
|
77
|
-
@queue = Queue.new
|
78
|
-
end
|
79
|
-
|
80
|
-
def <<(args)
|
81
|
-
@queue << args
|
82
|
-
end
|
83
|
-
|
84
|
-
def finish
|
85
|
-
@queue << nil
|
86
|
-
end
|
87
|
-
|
88
|
-
def each(&block)
|
89
|
-
while (args = @queue.pop)
|
90
|
-
block.call(*args)
|
91
|
-
end
|
92
|
-
nil # non reusable
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
# Enums receiving items
|
97
|
-
attr_reader :enums
|
98
|
-
|
99
|
-
def initialize(enumerable, enum_count)
|
100
|
-
@enums = Array.new(enum_count){ Transfer.new }
|
101
|
-
@filler = Thread.new do
|
102
|
-
enumerable.each do |*args|
|
103
|
-
@enums.each do |enum|
|
104
|
-
enum << args
|
105
|
-
end
|
106
|
-
end
|
107
|
-
@enums.each(&:finish)
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
33
|
attr_reader :enumerable, :thread_count
|
113
34
|
def initialize(enumerable, thread_count = 10, &block)
|
114
35
|
super(enumerable)
|
@@ -130,7 +51,7 @@ class InThreads < SimpleDelegator
|
|
130
51
|
class << self
|
131
52
|
# Specify runner to use
|
132
53
|
#
|
133
|
-
# use :
|
54
|
+
# use :run_in_threads_use_block_result, :for => %w[all? any? none? one?]
|
134
55
|
#
|
135
56
|
# `:for` is required
|
136
57
|
# `:ignore_undefined` ignores methods which are not present in
|
@@ -143,7 +64,11 @@ class InThreads < SimpleDelegator
|
|
143
64
|
next if ignore_undefined && !enumerable_method?(method)
|
144
65
|
class_eval <<-RUBY
|
145
66
|
def #{method}(*args, &block)
|
146
|
-
|
67
|
+
if block
|
68
|
+
#{runner}(:#{method}, *args, &block)
|
69
|
+
else
|
70
|
+
enumerable.#{method}(*args)
|
71
|
+
end
|
147
72
|
end
|
148
73
|
RUBY
|
149
74
|
end
|
@@ -152,13 +77,12 @@ class InThreads < SimpleDelegator
|
|
152
77
|
private
|
153
78
|
|
154
79
|
def enumerable_method?(name)
|
155
|
-
|
156
|
-
@enumerable_methods.include?(name.to_sym)
|
80
|
+
Enumerable.method_defined?(name)
|
157
81
|
end
|
158
82
|
end
|
159
83
|
|
160
|
-
use :
|
161
|
-
use :
|
84
|
+
use :run_in_threads_ignore_block_result, :for => %w[each]
|
85
|
+
use :run_in_threads_ignore_block_result, :for => %w[
|
162
86
|
reverse_each
|
163
87
|
each_with_index enum_with_index
|
164
88
|
each_cons each_slice enum_cons enum_slice
|
@@ -166,7 +90,7 @@ class InThreads < SimpleDelegator
|
|
166
90
|
cycle
|
167
91
|
each_entry
|
168
92
|
], :ignore_undefined => true
|
169
|
-
use :
|
93
|
+
use :run_in_threads_use_block_result, :for => %w[
|
170
94
|
all? any? none? one?
|
171
95
|
detect find find_index drop_while take_while
|
172
96
|
partition find_all select reject count
|
@@ -183,9 +107,10 @@ class InThreads < SimpleDelegator
|
|
183
107
|
include? member?
|
184
108
|
each_with_object
|
185
109
|
chunk chunk_while slice_before slice_after slice_when
|
110
|
+
lazy
|
186
111
|
].map(&:to_sym)
|
187
112
|
|
188
|
-
# Special case method, works by applying `
|
113
|
+
# Special case method, works by applying `run_in_threads_use_block_result` with
|
189
114
|
# map on enumerable returned by blockless run
|
190
115
|
def grep(*args, &block)
|
191
116
|
if block
|
@@ -196,7 +121,7 @@ class InThreads < SimpleDelegator
|
|
196
121
|
end
|
197
122
|
|
198
123
|
if enumerable_method?(:grep_v)
|
199
|
-
# Special case method, works by applying `
|
124
|
+
# Special case method, works by applying `run_in_threads_use_block_result` with
|
200
125
|
# map on enumerable returned by blockless run
|
201
126
|
def grep_v(*args, &block)
|
202
127
|
if block
|
@@ -214,46 +139,159 @@ class InThreads < SimpleDelegator
|
|
214
139
|
|
215
140
|
protected
|
216
141
|
|
142
|
+
# Enum out of queue
|
143
|
+
class QueueEnum
|
144
|
+
include Enumerable
|
145
|
+
|
146
|
+
def initialize(size = nil)
|
147
|
+
@queue = size ? SizedQueue.new(size) : Queue.new
|
148
|
+
end
|
149
|
+
|
150
|
+
def each(&block)
|
151
|
+
while (args = @queue.pop)
|
152
|
+
block.call(*args)
|
153
|
+
end
|
154
|
+
nil # non reusable
|
155
|
+
end
|
156
|
+
|
157
|
+
def push(*args)
|
158
|
+
@queue.push(args) unless @closed
|
159
|
+
end
|
160
|
+
|
161
|
+
def close(clear = false)
|
162
|
+
@closed = true
|
163
|
+
@queue.clear if clear
|
164
|
+
@queue.push(nil)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Thread pool
|
169
|
+
class Pool
|
170
|
+
attr_reader :exception
|
171
|
+
|
172
|
+
def initialize(thread_count)
|
173
|
+
@queue = Queue.new
|
174
|
+
@mutex = Mutex.new
|
175
|
+
@pool = Array.new(thread_count) do
|
176
|
+
Thread.new do
|
177
|
+
while (block = @queue.pop)
|
178
|
+
block.call
|
179
|
+
break if stop?
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def run(&block)
|
186
|
+
@queue.push(block)
|
187
|
+
end
|
188
|
+
|
189
|
+
def stop?
|
190
|
+
@stop || @exception
|
191
|
+
end
|
192
|
+
|
193
|
+
def stop!
|
194
|
+
@stop = true
|
195
|
+
end
|
196
|
+
|
197
|
+
def finalize
|
198
|
+
@pool.
|
199
|
+
each{ @queue.push(nil) }.
|
200
|
+
each(&:join)
|
201
|
+
end
|
202
|
+
|
203
|
+
def catch
|
204
|
+
yield
|
205
|
+
rescue Exception => e
|
206
|
+
@mutex.synchronize{ @exception ||= e } unless @exception
|
207
|
+
nil
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
217
211
|
# Use for methods which don't use block result
|
218
|
-
def
|
219
|
-
|
220
|
-
|
212
|
+
def run_in_threads_ignore_block_result(method, *args, &block)
|
213
|
+
pool = Pool.new(thread_count)
|
214
|
+
wait = SizedQueue.new(thread_count - 1)
|
215
|
+
begin
|
216
|
+
pool.catch do
|
221
217
|
enumerable.send(method, *args) do |*block_args|
|
222
|
-
|
218
|
+
pool.run do
|
219
|
+
pool.catch do
|
220
|
+
block.call(*block_args)
|
221
|
+
end
|
222
|
+
wait.pop
|
223
|
+
end
|
224
|
+
wait.push(nil)
|
225
|
+
break if pool.stop?
|
223
226
|
end
|
224
227
|
end
|
225
|
-
|
226
|
-
|
228
|
+
ensure
|
229
|
+
pool.finalize
|
230
|
+
if (e = pool.exception)
|
231
|
+
return e.exit_value if e.is_a?(LocalJumpError) && e.reason == :break
|
232
|
+
fail e
|
233
|
+
end
|
227
234
|
end
|
228
235
|
end
|
229
236
|
|
230
237
|
# Use for methods which do use block result
|
231
|
-
def
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
238
|
+
def run_in_threads_use_block_result(method, *args, &block)
|
239
|
+
pool = Pool.new(thread_count)
|
240
|
+
enum_a = QueueEnum.new
|
241
|
+
enum_b = QueueEnum.new(thread_count - 1)
|
242
|
+
results = SizedQueue.new(thread_count - 1)
|
243
|
+
filler = filler_thread(pool, [enum_a, enum_b])
|
244
|
+
runner = runner_thread(pool, enum_a, results, &block)
|
245
|
+
|
246
|
+
begin
|
247
|
+
pool.catch do
|
248
|
+
enum_b.send(method, *args) do
|
249
|
+
result = results.pop.pop
|
250
|
+
break if pool.stop?
|
251
|
+
result
|
252
|
+
end
|
253
|
+
end
|
254
|
+
ensure
|
255
|
+
pool.stop!
|
256
|
+
enum_a.close(true)
|
257
|
+
enum_b.close(true)
|
258
|
+
results.clear
|
259
|
+
pool.finalize
|
260
|
+
runner.join
|
261
|
+
filler.join
|
262
|
+
if (e = pool.exception)
|
263
|
+
return e.exit_value if e.is_a?(LocalJumpError) && e.reason == :break
|
264
|
+
fail e
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
private
|
270
|
+
|
271
|
+
def filler_thread(pool, enums)
|
272
|
+
Thread.new do
|
273
|
+
pool.catch do
|
274
|
+
enumerable.each do |*block_args|
|
275
|
+
enums.each do |enum|
|
276
|
+
enum.push(*block_args)
|
243
277
|
end
|
278
|
+
break if pool.stop?
|
244
279
|
end
|
245
280
|
end
|
281
|
+
enums.each(&:close)
|
282
|
+
end
|
283
|
+
end
|
246
284
|
|
247
|
-
|
248
|
-
|
249
|
-
|
285
|
+
def runner_thread(pool, enum, results, &block)
|
286
|
+
Thread.new do
|
287
|
+
enum.each do |*block_args|
|
288
|
+
queue = Queue.new
|
289
|
+
pool.run do
|
290
|
+
queue.push(pool.catch{ block.call(*block_args) })
|
250
291
|
end
|
251
|
-
|
252
|
-
|
253
|
-
runner.join
|
292
|
+
results.push(queue)
|
293
|
+
break if pool.stop?
|
254
294
|
end
|
255
|
-
else
|
256
|
-
enumerable.send(method, *args)
|
257
295
|
end
|
258
296
|
end
|
259
297
|
end
|