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.
Files changed (4) hide show
  1. data/README.md +11 -0
  2. data/lib/threadpool.rb +154 -0
  3. data/threadpool.gemspec +14 -0
  4. 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
@@ -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: []