omg-threadpool 0.2.4

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/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Igor Gunko
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,28 @@
1
+ === Introduction
2
+ O HAI! My name is Pool, Thread Pool. Now in Ruby.
3
+
4
+ === Installation
5
+ gem install omg-threadpool --source http://gems.github.com
6
+
7
+ === Usage
8
+ pool = ThreadPool.new
9
+ ...
10
+ pool.run(...) do |...|
11
+ ...
12
+ end
13
+ ...
14
+ pool.close
15
+
16
+ === More info in docs
17
+ http://rdoc.info/projects/omg/threadpool
18
+
19
+ === Bugs & such
20
+ Please report via Github issue tracking.
21
+
22
+ === See also
23
+ * http://github.com/omg/xmlnuts -- Ruby <-> XML mapping
24
+ * http://github.com/omg/statelogic -- A simple state machine for ActiveRecord
25
+
26
+
27
+ Free hint: If you liek mudkipz^W^Wfeel like generous today you can tip me at http://tipjoy.com/u/pisuka
28
+
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ $KCODE = 'u'
2
+
3
+ require 'rubygems'
4
+ require 'rake'
5
+ require 'rake/clean'
6
+ require 'rake/gempackagetask'
7
+ require 'rake/rdoctask'
8
+ require 'rake/testtask'
9
+
10
+ Rake::GemPackageTask.new(Gem::Specification.load('threadpool.gemspec')) do |p|
11
+ p.need_tar = true
12
+ p.need_zip = true
13
+ end
14
+
15
+ Rake::RDocTask.new do |rdoc|
16
+ files =['README.rdoc', 'MIT-LICENSE', 'lib/**/*.rb']
17
+ rdoc.rdoc_files.add(files)
18
+ rdoc.main = "README.rdoc" # page to start on
19
+ rdoc.title = "XmlNuts Documentation"
20
+ rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
21
+ rdoc.options << '--line-numbers' << '--inline-source'
22
+ end
23
+
24
+ Rake::TestTask.new do |t|
25
+ t.test_files = FileList['test/**/*.rb']
26
+ end
27
+
@@ -0,0 +1 @@
1
+ require 'threadpool'
data/lib/threadpool.rb ADDED
@@ -0,0 +1,191 @@
1
+ require 'thread'
2
+ require 'thwait'
3
+ require 'monitor'
4
+
5
+ # This is unsurprisingly a thread pool.
6
+ # It can run your jobs asynchronously.
7
+ # It can grow and shrink depending on the load.
8
+ # Like any good pool it can be... closed!
9
+ class ThreadPool
10
+ DEFAULT_CORE_WORKERS = 4
11
+ DEFAULT_KEEP_ALIVE_TIME = 5
12
+
13
+ class Job #:nodoc:
14
+ def initialize(*args, &handler)
15
+ @args, @handler = args, handler
16
+ end
17
+
18
+ def run
19
+ @handler.call(*@args)
20
+ end
21
+ end
22
+
23
+ @@controllers = ThreadGroup.new
24
+
25
+ # new([[core_workers[, max_workers[, keep_alive_time]],] options]) [{|pool| ... }]
26
+ #
27
+ # === Arguments
28
+ # [+core_workers+] Number of core worker threads. The pool will never shrink below this point.
29
+ # [+max_workers+] Maximum number of worker threads allowed per this pool.
30
+ # The pool will never expand over this limit.
31
+ # Default is +core_workers * 2+
32
+ # [+keep_alive_time+] Time to keep non-core workers alive. Default is 5 sec.
33
+ # [+options+] +:core =>+ _core_workers_,
34
+ # +:max =>+ _max_workers_,
35
+ # +:keep_alive =>+ _keep_alive_time_,
36
+ # +:init_core => false+ to defer initial setup of core workers.
37
+ #
38
+ # When called with a block the pool will be closed upon exit from the block.
39
+ # Graceful +close+ will be used, a non-bang version.
40
+ #
41
+ # === Example:
42
+ # ThreadPool.new 10, 25, 6.7, :init_core => false do |pool|
43
+ # ...
44
+ # end
45
+ def initialize(*args)
46
+ extend MonitorMixin
47
+
48
+ options = args.last.is_a?(Hash) ? args.pop : {}
49
+
50
+ @core_workers = (args[0] || options[:core] || DEFAULT_CORE_WORKERS).to_i
51
+ raise ArgumentError, "core_workers must be a positive integer" if @core_workers <= 0
52
+
53
+ @max_workers = (args[1] || options[:max] || @core_workers * 2).to_i
54
+ raise ArgumentError, "max_workers must be >= core_workers" if @max_workers < @core_workers
55
+
56
+ @keep_alive_time = (args[2] || options[:keep_alive] || DEFAULT_KEEP_ALIVE_TIME).to_f
57
+ raise ArgumentError, "keep_alive_time must be a non-negative real number" if @keep_alive_time < 0
58
+
59
+ @workers, @jobs = [], Queue.new
60
+
61
+ @controller = Thread.new do
62
+ loop do
63
+ sleep(@keep_alive_time)
64
+ break if @dead
65
+ synchronize do
66
+ n = @jobs.num_waiting - @core_workers
67
+ stop_workers([n / 2, 1].max) if n >= 0
68
+ end
69
+ end
70
+ end
71
+ @@controllers.add(@controller)
72
+
73
+ create_workers(@core_workers) if options.fetch(:init_core, true)
74
+
75
+ begin
76
+ yield self
77
+ ensure
78
+ shutdown
79
+ end if block_given?
80
+ end
81
+
82
+ # alive? => boolean
83
+ #
84
+ # Pool is live when it's not dead.
85
+ # Pool is dead when it's closed.
86
+ def alive?
87
+ synchronize { !@dead }
88
+ end
89
+
90
+ # run([arg1[, arg2[, ...]]]) {|[arg1[, arg2[, ...]]]| ... } -> pool
91
+ #
92
+ # Schedule the block to run asynchronously on a worker thread. Return immediately.
93
+ # Any arguments passed to this method will be passed to the block.
94
+ #
95
+ # When there are no idle workers the pool will grow.
96
+ # When max pool size is reached the job will be queued up until better times.
97
+ #
98
+ # === Example:
99
+ # pool.run('go to hell') do |greeting|
100
+ # puts greeting
101
+ # end
102
+ def run(*args, &block)
103
+ run_core(true, *args, &block)
104
+ end
105
+
106
+ # try_run([arg1[, arg2[, ...]]]) {|[arg1[, arg2[, ...]]]| ... } -> pool or nil
107
+ #
108
+ # Try to run the block asynchronously on a worker thread (see +run+).
109
+ # If there are no idle workers immediately available and the pool reached its maximum size,
110
+ # then do not enqueue the job and return +nil+.
111
+ #
112
+ # === Example:
113
+ # puts 'zomg' unless pool.try_run('go to hell') {|greeting| puts greeting }
114
+ def run?(*args, &block)
115
+ run_core(false, *args, &block)
116
+ end
117
+
118
+ # close
119
+ #
120
+ # Rape me gently. Waits until all the jobs are done and destroys the pool.
121
+ def close
122
+ _sync do
123
+ @dead = true
124
+ @controller.run
125
+ stop_workers(@workers.size)
126
+ end
127
+ ThreadsWait.all_waits(@controller, *@workers)
128
+ self
129
+ end
130
+
131
+ # close!
132
+ #
133
+ # Rape me hard. Instantly kills the workers. Ensure blocks will be called though (last prayer on).
134
+ def close!
135
+ _sync do
136
+ @dead = true
137
+ @controller.run
138
+ @workers.each {|w| w.kill }
139
+ end
140
+ self
141
+ end
142
+
143
+ private
144
+
145
+ def run_core(enqueue, *args, &block) #:nodoc:
146
+ raise ArgumentError, 'block must be provided' unless block_given?
147
+ _sync do
148
+ if @jobs.num_waiting == 0
149
+ if @workers.size < @max_workers
150
+ create_worker
151
+ else
152
+ return nil unless enqueue
153
+ end
154
+ end
155
+ @jobs.push(Job.new(*args, &block))
156
+ end
157
+ self
158
+ end
159
+
160
+ def _sync #:nodoc:
161
+ synchronize do
162
+ check_state
163
+ yield
164
+ end
165
+ end
166
+
167
+ def check_state #:nodoc:
168
+ raise "pool's closed" if @dead
169
+ end
170
+
171
+ def create_worker #:nodoc:
172
+ @workers << Thread.new(&method(:worker_routine))
173
+ end
174
+
175
+ def create_workers(n) #:nodoc:
176
+ n.times { create_worker }
177
+ end
178
+
179
+ def stop_workers(n) #:nodoc:
180
+ n.times { @jobs << nil }
181
+ end
182
+
183
+ def worker_routine #:nodoc:
184
+ while job = @jobs.pop
185
+ job.run rescue nil
186
+ end
187
+ ensure
188
+ synchronize { @workers.delete(Thread.current) unless @dead }
189
+ end
190
+ end
191
+
@@ -0,0 +1,18 @@
1
+ require 'test/unit'
2
+ require 'lib/threadpool'
3
+
4
+
5
+ class ThreadPoolTest < Test::Unit::TestCase
6
+ def setup
7
+ end
8
+
9
+ def test_me
10
+ @pool = ThreadPool.new(2, 15, 1)
11
+ n = 0
12
+ p = proc {|x| n += x }
13
+ 100.times {|i| @pool.run(i, &p) }
14
+ @pool.close
15
+ assert_equal 4950, n
16
+ end
17
+ end
18
+
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omg-threadpool
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.4
5
+ platform: ruby
6
+ authors:
7
+ - Igor Gunko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-21 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: tekmon@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - MIT-LICENSE
25
+ files:
26
+ - README.rdoc
27
+ - MIT-LICENSE
28
+ - Rakefile
29
+ - lib/threadpool.rb
30
+ - lib/omg-threadpool.rb
31
+ has_rdoc: true
32
+ homepage: http://github.com/omg/threadpool
33
+ post_install_message:
34
+ rdoc_options:
35
+ - --line-numbers
36
+ - --main
37
+ - README.rdoc
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ version:
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ requirements: []
53
+
54
+ rubyforge_project:
55
+ rubygems_version: 1.2.0
56
+ signing_key:
57
+ specification_version: 2
58
+ summary: Thread pool for Ruby
59
+ test_files:
60
+ - test/threadpool_test.rb