threadpool 0.0.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/README.md +11 -0
- data/lib/threadpool.rb +154 -0
- data/threadpool.gemspec +14 -0
- metadata +47 -0
data/README.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
ThreadPool - an implementation that doesn't waste CPU resources
|
2
|
+
===============================================================
|
3
|
+
All the implementations I looked at were either buggy or wasted CPU resources
|
4
|
+
for no apparent reason, for example used a sleep of 0.01 seconds to then check for
|
5
|
+
readiness and stuff like this.
|
6
|
+
|
7
|
+
This implementation uses `IO.select` instead, there is no timed sleep, it just only stays
|
8
|
+
there waiting for input, which will then come from a `#wake_up` call that writes on a pipe.
|
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).
|
data/lib/threadpool.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
#--
|
2
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
3
|
+
# Version 2, December 2004
|
4
|
+
#
|
5
|
+
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
6
|
+
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
7
|
+
#
|
8
|
+
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
9
|
+
#++
|
10
|
+
|
11
|
+
require 'forwardable'
|
12
|
+
require 'thread'
|
13
|
+
|
14
|
+
module Awakenable
|
15
|
+
def sleep (time = nil)
|
16
|
+
@awakenable ||= IO.pipe
|
17
|
+
|
18
|
+
@awakenable.first.read_nonblock 1337 rescue nil
|
19
|
+
|
20
|
+
unless @awakened
|
21
|
+
IO.select([@awakenable.first], nil, nil, time)
|
22
|
+
else
|
23
|
+
@awakened = false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def wake_up
|
28
|
+
@awakenable ||= IO.pipe
|
29
|
+
|
30
|
+
@awakenable.last.write 'x'
|
31
|
+
@awakened = true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class ThreadPool
|
36
|
+
class Worker
|
37
|
+
include Awakenable
|
38
|
+
|
39
|
+
def initialize (watcher = nil)
|
40
|
+
@watcher = watcher
|
41
|
+
@mutex = Mutex.new
|
42
|
+
|
43
|
+
@thread = Thread.new {
|
44
|
+
loop do
|
45
|
+
if @block
|
46
|
+
@block.call(*@args) rescue nil
|
47
|
+
|
48
|
+
@block = nil
|
49
|
+
@args = nil
|
50
|
+
|
51
|
+
@watcher.wake_up if @watcher
|
52
|
+
else
|
53
|
+
sleep
|
54
|
+
|
55
|
+
break if die?
|
56
|
+
end
|
57
|
+
end
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def available?
|
62
|
+
@mutex.synchronize {
|
63
|
+
!@block
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def process (*args, &block)
|
68
|
+
return unless available?
|
69
|
+
|
70
|
+
@mutex.synchronize {
|
71
|
+
@block = block
|
72
|
+
@args = args
|
73
|
+
|
74
|
+
wake_up
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def join
|
79
|
+
@thread.join
|
80
|
+
end
|
81
|
+
|
82
|
+
def kill
|
83
|
+
return if die?
|
84
|
+
|
85
|
+
@die = true
|
86
|
+
wake_up
|
87
|
+
end
|
88
|
+
|
89
|
+
def die?
|
90
|
+
@die
|
91
|
+
end
|
92
|
+
|
93
|
+
def dead?
|
94
|
+
@thread.stop?
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
extend Forwardable
|
99
|
+
|
100
|
+
def_delegators :@watcher, :kill, :die?, :dead?
|
101
|
+
|
102
|
+
def initialize (size = 2)
|
103
|
+
@size = 0
|
104
|
+
@queue = Queue.new
|
105
|
+
@pool = []
|
106
|
+
@watcher = Worker.new
|
107
|
+
|
108
|
+
@watcher.process {
|
109
|
+
loop do
|
110
|
+
@watcher.sleep if @queue.empty?
|
111
|
+
next if @queue.empty?
|
112
|
+
|
113
|
+
begin
|
114
|
+
worker = @pool.find(&:available?) or @watcher.sleep
|
115
|
+
end until worker
|
116
|
+
|
117
|
+
break if @watcher.die?
|
118
|
+
|
119
|
+
args, block = @queue.pop
|
120
|
+
|
121
|
+
worker.process(*args, &block)
|
122
|
+
end
|
123
|
+
|
124
|
+
@pool.each(&:kill)
|
125
|
+
}
|
126
|
+
|
127
|
+
resize(size)
|
128
|
+
end
|
129
|
+
|
130
|
+
def join
|
131
|
+
@watcher.join
|
132
|
+
end; alias wait join
|
133
|
+
|
134
|
+
def resize (size)
|
135
|
+
return if @size == size
|
136
|
+
|
137
|
+
if @size < size
|
138
|
+
1.upto(size - @size) {
|
139
|
+
@pool << Worker.new(@watcher)
|
140
|
+
}
|
141
|
+
else
|
142
|
+
1.upto(@size - size) {
|
143
|
+
@pool.pop.kill
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
@size = size
|
148
|
+
end
|
149
|
+
|
150
|
+
def process (*args, &block)
|
151
|
+
@queue.push([args, block])
|
152
|
+
@watcher.wake_up
|
153
|
+
end; alias do process
|
154
|
+
end
|
data/threadpool.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Gem::Specification.new {|s|
|
2
|
+
s.name = 'threadpool'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.author = 'meh.'
|
5
|
+
s.email = 'meh@paranoici.org'
|
6
|
+
s.homepage = 'http://github.com/meh/threadpool'
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.summary = 'A simple no-wasted-resources thread pool implementation.'
|
9
|
+
|
10
|
+
s.files = `git ls-files`.split("\n")
|
11
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
12
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
13
|
+
s.require_paths = ['lib']
|
14
|
+
}
|
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: threadpool
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- meh.
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-25 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description:
|
15
|
+
email: meh@paranoici.org
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- README.md
|
21
|
+
- lib/threadpool.rb
|
22
|
+
- threadpool.gemspec
|
23
|
+
homepage: http://github.com/meh/threadpool
|
24
|
+
licenses: []
|
25
|
+
post_install_message:
|
26
|
+
rdoc_options: []
|
27
|
+
require_paths:
|
28
|
+
- lib
|
29
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubyforge_project:
|
43
|
+
rubygems_version: 1.8.10
|
44
|
+
signing_key:
|
45
|
+
specification_version: 3
|
46
|
+
summary: A simple no-wasted-resources thread pool implementation.
|
47
|
+
test_files: []
|