threadpool 0.0.3 → 0.1.0
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/README.md +3 -7
- data/lib/threadpool.rb +105 -108
- data/threadpool.gemspec +2 -2
- metadata +4 -4
data/README.md
CHANGED
@@ -4,11 +4,8 @@ All the implementations I looked at were either buggy or wasted CPU resources
|
|
4
4
|
for no apparent reason, for example used a sleep of 0.01 seconds to then check for
|
5
5
|
readiness and stuff like this.
|
6
6
|
|
7
|
-
This implementation uses
|
8
|
-
|
9
|
-
|
10
|
-
`IO.select` should be present everywhere so this should be cross-platform and doesn't waste
|
11
|
-
CPU resources. Keep in mind that each worker uses 2 file descriptors (reading and writing pipe).
|
7
|
+
This implementation uses standard locking functions to work properly across multiple Ruby
|
8
|
+
implementations.
|
12
9
|
|
13
10
|
Example
|
14
11
|
-------
|
@@ -16,8 +13,7 @@ Example
|
|
16
13
|
```ruby
|
17
14
|
require 'threadpool'
|
18
15
|
|
19
|
-
pool = ThreadPool.new
|
20
|
-
pool.resize(4)
|
16
|
+
pool = ThreadPool.new(4)
|
21
17
|
|
22
18
|
0.upto(10) { pool.process { sleep 2; puts 'lol' } }
|
23
19
|
|
data/lib/threadpool.rb
CHANGED
@@ -8,146 +8,143 @@
|
|
8
8
|
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
9
|
#++
|
10
10
|
|
11
|
-
require 'forwardable'
|
12
11
|
require 'thread'
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
@awakenable ||= IO.pipe
|
13
|
+
class ThreadPool
|
14
|
+
attr_reader :min, :max, :spawned
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
def initialize (min, max = nil, &block)
|
17
|
+
@min = min
|
18
|
+
@max = max || min
|
19
|
+
@block = block
|
21
20
|
|
22
|
-
|
23
|
-
|
21
|
+
@cond = ConditionVariable.new
|
22
|
+
@mutex = Mutex.new
|
24
23
|
|
25
|
-
|
26
|
-
@
|
24
|
+
@todo = []
|
25
|
+
@workers = []
|
27
26
|
|
28
|
-
@
|
29
|
-
|
30
|
-
|
27
|
+
@spawned = 0
|
28
|
+
@waiting = 0
|
29
|
+
@shutdown = false
|
30
|
+
@trim_requests = 0
|
31
|
+
@auto_trim = false
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
@mutex = Mutex.new
|
39
|
-
|
40
|
-
@thread = Thread.new {
|
41
|
-
loop do
|
42
|
-
if @block
|
43
|
-
begin
|
44
|
-
@block.call(*@args)
|
45
|
-
rescue Exception => e
|
46
|
-
@pool.raise(e)
|
47
|
-
end
|
33
|
+
@mutex.synchronize {
|
34
|
+
min.times {
|
35
|
+
spawn_thread
|
36
|
+
}
|
37
|
+
}
|
38
|
+
end
|
48
39
|
|
49
|
-
|
50
|
-
|
40
|
+
def auto_trim?; @auto_trim; end
|
41
|
+
def auto_trim!; @auto_trim = true; end
|
42
|
+
def no_auto_trim!; @auto_trim = false; end
|
51
43
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
break if die?
|
56
|
-
end
|
57
|
-
end
|
58
|
-
}
|
59
|
-
end
|
44
|
+
def resize (min, max = nil)
|
45
|
+
@min = min
|
46
|
+
@max = max || min
|
60
47
|
|
61
|
-
|
62
|
-
|
63
|
-
!@block
|
64
|
-
}
|
65
|
-
end
|
48
|
+
trim!
|
49
|
+
end
|
66
50
|
|
67
|
-
|
68
|
-
|
51
|
+
def backlog
|
52
|
+
@mutex.synchronize {
|
53
|
+
@todo.length
|
54
|
+
}
|
55
|
+
end
|
69
56
|
|
70
|
-
|
71
|
-
|
72
|
-
|
57
|
+
def process (*args, &block)
|
58
|
+
@mutex.synchronize {
|
59
|
+
raise 'unable to add work while shutting down' if @shutdown
|
73
60
|
|
74
|
-
|
75
|
-
}
|
76
|
-
end
|
61
|
+
@todo << [args, block]
|
77
62
|
|
78
|
-
|
79
|
-
|
80
|
-
|
63
|
+
if @waiting == 0 && @spawned < @max
|
64
|
+
spawn_thread
|
65
|
+
end
|
81
66
|
|
82
|
-
|
83
|
-
|
67
|
+
@cond.signal
|
68
|
+
}
|
69
|
+
end
|
84
70
|
|
85
|
-
|
86
|
-
wake_up
|
87
|
-
end
|
71
|
+
alias << process
|
88
72
|
|
89
|
-
|
90
|
-
|
91
|
-
|
73
|
+
def trim (force = false)
|
74
|
+
@mutex.synchronize {
|
75
|
+
if (force || @waiting > 0) && @spawned - @trim_requests > @min
|
76
|
+
@trim_requests -= 1
|
77
|
+
@cond.signal
|
78
|
+
end
|
79
|
+
}
|
80
|
+
end
|
92
81
|
|
93
|
-
|
94
|
-
|
95
|
-
end
|
82
|
+
def trim!
|
83
|
+
trim true
|
96
84
|
end
|
97
85
|
|
98
|
-
|
86
|
+
def shutdown
|
87
|
+
@mutex.synchronize {
|
88
|
+
@shutdown = true
|
89
|
+
@cond.broadcast
|
90
|
+
}
|
91
|
+
|
92
|
+
@workers.first.join until @workers.empty?
|
93
|
+
end
|
99
94
|
|
100
|
-
|
101
|
-
|
102
|
-
|
95
|
+
private
|
96
|
+
def spawn_thread
|
97
|
+
@spawned += 1
|
103
98
|
|
104
|
-
|
105
|
-
@size = 0
|
106
|
-
@queue = Queue.new
|
107
|
-
@pool = []
|
108
|
-
@watcher = Worker.new(self)
|
109
|
-
@current = Thread.current
|
110
|
-
|
111
|
-
@watcher.process {
|
99
|
+
thread = Thread.new {
|
112
100
|
loop do
|
113
|
-
|
114
|
-
|
101
|
+
work = nil
|
102
|
+
continue = true
|
115
103
|
|
116
|
-
|
117
|
-
|
118
|
-
|
104
|
+
@mutex.synchronize {
|
105
|
+
while @todo.empty?
|
106
|
+
if @trim_requests > 0
|
107
|
+
@trim_requests -= 1
|
108
|
+
continue = false
|
119
109
|
|
120
|
-
|
110
|
+
break
|
111
|
+
end
|
121
112
|
|
122
|
-
|
113
|
+
if @shutdown
|
114
|
+
continue = false
|
123
115
|
|
124
|
-
|
125
|
-
|
116
|
+
break
|
117
|
+
end
|
126
118
|
|
127
|
-
|
128
|
-
|
119
|
+
@waiting += 1
|
120
|
+
@cond.wait @mutex
|
121
|
+
@waiting -= 1
|
129
122
|
|
130
|
-
|
131
|
-
|
123
|
+
if @shutdown
|
124
|
+
continue = false
|
132
125
|
|
133
|
-
|
134
|
-
|
126
|
+
break
|
127
|
+
end
|
128
|
+
end
|
135
129
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
130
|
+
work = @todo.shift if continue
|
131
|
+
}
|
132
|
+
|
133
|
+
break unless continue
|
134
|
+
|
135
|
+
(work.last || @block).call(*work.first)
|
136
|
+
|
137
|
+
trim if auto_trim? && @spawned > @min
|
138
|
+
end
|
139
|
+
|
140
|
+
@mutex.synchronize {
|
141
|
+
@spawned -= 1
|
142
|
+
@workers.delete thread
|
143
143
|
}
|
144
|
-
|
144
|
+
}
|
145
145
|
|
146
|
-
@
|
147
|
-
end
|
146
|
+
@workers << thread
|
148
147
|
|
149
|
-
|
150
|
-
|
151
|
-
@watcher.wake_up
|
152
|
-
end; alias do process
|
148
|
+
thread
|
149
|
+
end
|
153
150
|
end
|
data/threadpool.gemspec
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
Gem::Specification.new {|s|
|
2
2
|
s.name = 'threadpool'
|
3
|
-
s.version = '0.0
|
3
|
+
s.version = '0.1.0'
|
4
4
|
s.author = 'meh.'
|
5
5
|
s.email = 'meh@paranoici.org'
|
6
|
-
s.homepage = 'http://github.com/meh/threadpool'
|
6
|
+
s.homepage = 'http://github.com/meh/ruby-threadpool'
|
7
7
|
s.platform = Gem::Platform::RUBY
|
8
8
|
s.summary = 'A simple no-wasted-resources thread pool implementation.'
|
9
9
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: threadpool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-03-29 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description:
|
15
15
|
email: meh@paranoici.org
|
@@ -20,7 +20,7 @@ files:
|
|
20
20
|
- README.md
|
21
21
|
- lib/threadpool.rb
|
22
22
|
- threadpool.gemspec
|
23
|
-
homepage: http://github.com/meh/threadpool
|
23
|
+
homepage: http://github.com/meh/ruby-threadpool
|
24
24
|
licenses: []
|
25
25
|
post_install_message:
|
26
26
|
rdoc_options: []
|
@@ -40,7 +40,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
40
40
|
version: '0'
|
41
41
|
requirements: []
|
42
42
|
rubyforge_project:
|
43
|
-
rubygems_version: 1.8.
|
43
|
+
rubygems_version: 1.8.11
|
44
44
|
signing_key:
|
45
45
|
specification_version: 3
|
46
46
|
summary: A simple no-wasted-resources thread pool implementation.
|