ActionPool 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ 0.2.2
2
+ - Better thread management within the pool
3
+ - Restart thread timeouts when modified
4
+ - Fix argument passing
1
5
  0.2.1
2
6
  - Set minimum pool size properly when initialized to avoid
3
7
  creating extra threads (thanks to Roger Pack)
data/actionpool.gemspec CHANGED
@@ -2,7 +2,7 @@ spec = Gem::Specification.new do |s|
2
2
  s.name = 'ActionPool'
3
3
  s.author = %q(spox)
4
4
  s.email = %q(spox@modspox.com)
5
- s.version = '0.2.1'
5
+ s.version = '0.2.2'
6
6
  s.summary = %q(Thread Pool)
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.files = Dir['**/*']
@@ -11,13 +11,5 @@ spec = Gem::Specification.new do |s|
11
11
  s.require_paths = %w(lib)
12
12
  s.required_ruby_version = '>= 1.8.6'
13
13
  s.homepage = %q(http://github.com/spox/actionpool)
14
- description = []
15
- File.open("README.rdoc") do |file|
16
- file.each do |line|
17
- line.chomp!
18
- break if line.empty?
19
- description << "#{line.gsub(/\[\d\]/, '')}"
20
- end
21
- end
22
- s.description = description[1..-1].join(" ")
14
+ s.description = "The ActionPool is an easy to use thread pool for ruby."
23
15
  end
@@ -4,6 +4,9 @@ require 'actionpool/LogHelper'
4
4
  require 'thread'
5
5
 
6
6
  module ActionPool
7
+ # Raised when pool is closed
8
+ class PoolClosed < StandardError
9
+ end
7
10
  class Pool
8
11
 
9
12
  # :min_threads:: minimum number of threads in pool
@@ -23,35 +26,68 @@ module ActionPool
23
26
  @max_threads = args[:max_threads] ? args[:max_threads] : 100
24
27
  @min_threads = args[:min_threads] ? args[:min_threads] : 10
25
28
  @min_threads = @max_threads if @max_threads < @min_threads
26
- @respond_to = ::Thread.current
27
- create_thread
29
+ @respond_to = args[:respond_thread] || ::Thread.current
30
+ @open = true
31
+ fill_pool
28
32
  end
29
33
 
30
- # force:: force creation of a new thread
31
- # Create a new thread for pool. Returns newly created ActionPool::Thread or
32
- # nil if pool has reached maximum threads
33
- def create_thread(force=false)
34
- pt = nil
34
+ # Pool is closed
35
+ def pool_closed?
36
+ !@open
37
+ end
38
+
39
+ # Pool is open
40
+ def pool_open?
41
+ @open
42
+ end
43
+
44
+ # arg:: :open or :closed
45
+ # Set pool status
46
+ def status(arg)
47
+ @open = arg == :open
48
+ fill_pool if @open
49
+ end
50
+
51
+ # args:: :force forces a new thread. :nowait will create a thread if threads are waiting
52
+ # Create a new thread for pool.
53
+ # Returns newly created thread of nil if pool is at maximum size
54
+ def create_thread(*args)
55
+ return if pool_closed?
56
+ thread = nil
35
57
  @lock.synchronize do
36
- if(@threads.size < @max_threads || force)
37
- @logger.info('Pool is creating a new thread')
38
- (min - size > 0 ? min - size : 1).times do |i|
39
- pt = ActionPool::Thread.new(:pool => self, :respond_thread => @respond_to, :a_timeout => @action_timeout, :t_timeout => @thread_timeout, :logger => @logger)
40
- @threads << pt
58
+ if(((size == working || args.include?(:nowait)) && @threads.size < @max_threads) || args.include?(:force))
59
+ thread = ActionPool::Thread.new(:pool => self, :respond_thread => @respond_to, :a_timeout => @action_timeout, :t_timeout => @thread_timeout, :logger => @logger)
60
+ @threads << thread
61
+ end
62
+ end
63
+ return thread
64
+ end
65
+
66
+ # Fills the pool with the minimum number of threads
67
+ # Returns array of created threads
68
+ def fill_pool
69
+ threads = []
70
+ @lock.synchronize do
71
+ required = min - size
72
+ if(required > 0)
73
+ required.times do
74
+ thread = ActionPool::Thread.new(:pool => self, :respond_thread => @respond_to, :a_timeout => @action_timeout, :t_timeout => @thread_timeout, :logger => @logger)
75
+ @threads << thread
76
+ threads << thread
41
77
  end
42
- else
43
- @logger.info('Pool is at maximum size. Not creating new thread')
44
78
  end
45
79
  end
46
- return pt
80
+ return threads
47
81
  end
48
82
 
49
83
  # force:: force immediate stop
50
84
  # Stop the pool
51
85
  def shutdown(force=false)
52
- args = [:wait]
53
- args += [:force] if force
86
+ status(:closed)
87
+ args = []
88
+ args.push(:force) if force
54
89
  @logger.info("Pool is now shutting down #{force ? 'using force' : ''}")
90
+ @queue.clear if force
55
91
  @queue.wait_empty
56
92
  while(t = @threads.pop) do
57
93
  t.stop(*args)
@@ -67,7 +103,7 @@ module ActionPool
67
103
  queue(action)
68
104
  when Array
69
105
  raise ArgumentError.new('Actions to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]') unless action.size == 2 and action[0].is_a?(Proc) and action[1].is_a?(Array)
70
- queue(*action.flatten)
106
+ queue(action[0], action[1])
71
107
  else
72
108
  raise ArgumentError.new('Actions to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]')
73
109
  end
@@ -77,15 +113,17 @@ module ActionPool
77
113
  # action:: proc to be executed
78
114
  # Add a new proc/lambda to be executed
79
115
  def queue(action, *args)
116
+ raise PoolClosed.new("Pool #{self} is currently closed") if pool_closed?
80
117
  raise ArgumentError.new('Expecting block') unless action.is_a?(Proc)
81
118
  @queue << [action, args]
82
119
  ::Thread.pass
83
- create_thread if @queue.num_waiting < 1 # only start a new thread if we need it
120
+ create_thread
84
121
  end
85
122
 
86
123
  # jobs:: Array of proc/lambdas
87
124
  # Will queue a list of jobs into the pool
88
125
  def add_jobs(jobs)
126
+ raise PoolClosed.new("Pool #{self} is currently closed") if pool_closed?
89
127
  raise ArgumentError.new("Expecting an array but received: #{jobs.class}") unless jobs.is_a?(Array)
90
128
  @queue.pause
91
129
  begin
@@ -95,13 +133,14 @@ module ActionPool
95
133
  @queue << [job, []]
96
134
  when Array
97
135
  raise ArgumentError.new('Jobs to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]') unless job.size == 2 and job[0].is_a?(Proc) and job[1].is_a?(Array)
98
- @queue << job
136
+ @queue << [job.shift, job]
99
137
  else
100
138
  raise ArgumentError.new('Jobs to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]')
101
139
  end
102
140
  end
103
141
  ensure
104
- create_thread
142
+ num = jobs.size - @threads.select{|t|t.waiting?}.size
143
+ num.times{ create_thread(:nowait) } if num > 0
105
144
  @queue.unpause
106
145
  end
107
146
  true
@@ -181,7 +220,7 @@ module ActionPool
181
220
  # Set maximum allowed time thead may idle in pool
182
221
  def thread_timeout=(t)
183
222
  t = t.to_f
184
- raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
223
+ raise ArgumentError.new('Value must be greater than zero or nil') unless t >= 0
185
224
  @thread_timeout = t
186
225
  @threads.each{|thread|thread.thread_timeout = t}
187
226
  t
@@ -192,7 +231,7 @@ module ActionPool
192
231
  # on a given action
193
232
  def action_timeout=(t)
194
233
  t = t.to_f
195
- raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
234
+ raise ArgumentError.new('Value must be greater than zero or nil') unless t >= 0
196
235
  @action_timeout = t
197
236
  @threads.each{|thread|thread.action_timeout = t}
198
237
  t
@@ -220,6 +259,11 @@ module ActionPool
220
259
  lock.synchronize{ guard.broadcast }
221
260
  end
222
261
 
262
+ # Returns current number of threads in the pool working
263
+ def working
264
+ @threads.find_all{|t|!t.waiting?}.size
265
+ end
266
+
223
267
  private
224
268
 
225
269
  # Resize the pool
@@ -7,32 +7,47 @@ module ActionPool
7
7
  def initialize
8
8
  super
9
9
  @wait = false
10
- @lock = Mutex.new
11
- @guard = ConditionVariable.new
12
- @elock = Mutex.new
13
- @eguard = ConditionVariable.new
10
+ @pause_lock = Mutex.new
11
+ @empty_lock = Mutex.new
12
+ @pause_guard = ConditionVariable.new
13
+ @empty_guard = ConditionVariable.new
14
14
  end
15
15
  # Stop the queue from returning results to requesting
16
16
  # threads. Threads will wait for results until signalled
17
17
  def pause
18
- @wait = true
18
+ @pause_lock.synchronize{@wait = true}
19
19
  end
20
20
  # Allow the queue to return results. Any threads waiting
21
21
  # will have results given to them.
22
22
  def unpause
23
- @wait = false
24
- @lock.synchronize{ @guard.broadcast }
23
+ @pause_lock.synchronize do
24
+ @wait = false
25
+ @pause_guard.broadcast
26
+ end
25
27
  end
26
28
  # Check if queue needs to wait before returning
27
29
  def pop
28
- @lock.synchronize{ @guard.wait(@lock) } if @wait
30
+ @pause_lock.synchronize do
31
+ @pause_guard.wait(@pause_lock) if @wait
32
+ end
29
33
  o = super
30
- @elock.synchronize{ @eguard.broadcast } if empty?
34
+ @empty_lock.synchronize do
35
+ @empty_guard.broadcast if empty?
36
+ end
31
37
  return o
32
38
  end
39
+ # Clear queue
40
+ def clear
41
+ super
42
+ @empty_lock.synchronize do
43
+ @empty_guard.broadcast
44
+ end
45
+ end
33
46
  # Park a thread here until queue is empty
34
47
  def wait_empty
35
- @elock.synchronize{ @eguard.wait(@elock) } if size > 0
48
+ @empty_lock.synchronize do
49
+ @empty_guard.wait(@empty_lock) if size > 0
50
+ end
36
51
  end
37
52
  end
38
53
  end
@@ -1,7 +1,12 @@
1
1
  require 'timeout'
2
2
 
3
3
  module ActionPool
4
-
4
+ # Exception class used for waking up a thread
5
+ class Wakeup < StandardError
6
+ end
7
+ # Raised within a thread when the timeout is changed
8
+ class Retimeout < StandardError
9
+ end
5
10
  class Thread
6
11
  # :pool:: pool thread is associated with
7
12
  # :t_timeout:: max time a thread is allowed to wait for action
@@ -19,6 +24,7 @@ module ActionPool
19
24
  @action_timeout = args[:a_timeout] ? args[:a_timeout].to_f : 0
20
25
  @kill = false
21
26
  @logger = args[:logger].is_a?(LogHelper) ? args[:logger] : LogHelper.new(args[:logger])
27
+ @lock = Mutex.new
22
28
  @thread = ::Thread.new{ start_thread }
23
29
  end
24
30
 
@@ -27,29 +33,30 @@ module ActionPool
27
33
  # Stop the thread
28
34
  def stop(*args)
29
35
  @kill = true
30
- @thread.kill if args.include?(:force) || waiting?
31
- # killing the thread if it is waiting? is a simple race condition
32
- # in that, if the thread has just finished a task, but hasn't
33
- # dropped by the queue line yet, we will see it as not waiting. we
34
- # can counter that by waiting just a little bit longer than the allowed
35
- # time to work on an action, and kill the thread if it is still around
36
- if(args.include?(:wait))
37
- unless(@thread.join(@action_timeout + 0.01))
38
- @thread.kill
39
- @thread.join
40
- end
41
- end
36
+ @thread.raise Wakeup.new if args.include?(:force) || waiting?
42
37
  nil
43
38
  end
44
39
 
40
+ # Currently waiting
41
+ def waiting?
42
+ @lock.synchronize{@status} == :wait
43
+ end
44
+
45
45
  # Is the thread still alive
46
46
  def alive?
47
47
  @thread.alive?
48
48
  end
49
49
 
50
- # Is the thread currently waiting for input
51
- def waiting?
52
- @thread.status == 'sleep'
50
+ # Current thread status
51
+ def status
52
+ @lock.synchronize{ return @status }
53
+ end
54
+
55
+ # arg:: :wait or :run
56
+ # Set current status
57
+ def status(arg)
58
+ raise InvalidType.new('Status can only be set to :wait or :run') unless arg == :wait || arg == :run
59
+ @lock.synchronize{ @status = arg }
53
60
  end
54
61
 
55
62
  # Seconds thread will wait for input
@@ -68,11 +75,13 @@ module ActionPool
68
75
  t = t.to_f
69
76
  raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
70
77
  @thread_timeout = t
78
+ @thread.raise Retimeout.new if waiting?
71
79
  t
72
80
  end
73
81
 
74
82
  # t:: seconds to work on a task (floats allow for values 0 < t < 1)
75
83
  # Set the maximum amount of time to work on a given task
84
+ # Note: Modification of this will not affect actions already in process
76
85
  def action_timeout=(t)
77
86
  t = t.to_f
78
87
  raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
@@ -87,29 +96,42 @@ module ActionPool
87
96
  begin
88
97
  @logger.info("New pool thread is starting (#{self})")
89
98
  until(@kill) do
99
+ status(:wait)
90
100
  begin
91
101
  action = nil
92
- if(@pool.size > @pool.min)
102
+ if(@pool.size > @pool.min && !@thread_timeout.zero?)
93
103
  Timeout::timeout(@thread_timeout) do
94
104
  action = @pool.action
95
105
  end
96
106
  else
97
107
  action = @pool.action
98
108
  end
99
- run(*action) unless action.nil?
109
+ status(:run)
110
+ run(action[0], action[1]) unless action.nil?
111
+ status(:wait)
100
112
  rescue Timeout::Error => boom
101
113
  @kill = true
114
+ rescue Wakeup
115
+ @logger.info("Thread #{::Thread.current} was woken up.")
116
+ rescue Retimeout
117
+ @logger.warn('Thread was woken up to reset thread timeout')
102
118
  rescue Exception => boom
103
119
  @logger.error("Pool thread caught an exception: #{boom}\n#{boom.backtrace.join("\n")}")
104
120
  @respond_to.raise boom
105
121
  end
106
122
  end
123
+ rescue Retimeout
124
+ @logger.warn('Thread was woken up to reset thread timeout')
125
+ retry
126
+ rescue Wakeup
127
+ @logger.info("Thread #{::Thread.current} was woken up.")
107
128
  rescue Exception => boom
108
129
  @logger.error("Pool thread caught an exception: #{boom}\n#{boom.backtrace.join("\n")}")
109
130
  @respond_to.raise boom
110
131
  ensure
111
132
  @logger.info("Pool thread is shutting down (#{self})")
112
133
  @pool.remove(self)
134
+ @pool.create_thread
113
135
  end
114
136
  end
115
137
 
@@ -118,12 +140,12 @@ module ActionPool
118
140
  # Run the task
119
141
  def run(action, args)
120
142
  begin
121
- if(@action_timeout > 0)
143
+ unless(@action_timeout.zero?)
122
144
  Timeout::timeout(@action_timeout) do
123
- action.call(*args)
145
+ action.call(*args[0])
124
146
  end
125
147
  else
126
- action.call(*args)
148
+ action.call(*args[0])
127
149
  end
128
150
  rescue Timeout::Error => boom
129
151
  @logger.warn("Pool thread reached max execution time for action: #{boom}")
@@ -5,6 +5,9 @@ class GeneralPoolTest < Test::Unit::TestCase
5
5
  def setup
6
6
  @pool = ActionPool::Pool.new
7
7
  end
8
+ def teardown
9
+ @pool.shutdown(true)
10
+ end
8
11
  def test_numbers
9
12
  assert_equal(10, @pool.size)
10
13
  assert_equal(10, @pool.min)
@@ -20,6 +23,7 @@ class GeneralPoolTest < Test::Unit::TestCase
20
23
  100.times{ @pool << run }
21
24
  @pool.shutdown
22
25
  assert_equal(100, a)
26
+ @pool.status :open
23
27
  a = 0
24
28
  jobs = [].fill(run,0,100)
25
29
  @pool.add_jobs(jobs)
@@ -28,15 +32,21 @@ class GeneralPoolTest < Test::Unit::TestCase
28
32
  @pool.shutdown(true)
29
33
  end
30
34
  def test_args
35
+ @pool.status :open
31
36
  output = nil
32
37
  @pool << [lambda{|x| output = x}, [2]]
33
38
  assert(2, output)
34
39
  @pool.add_jobs([[lambda{|x| output = x}, [3]]])
35
40
  assert(3, output)
41
+ @pool << [lambda{|x,y| output = x+y}, [1,2]]
42
+ assert(3, output)
36
43
  output = []
37
44
  @pool.add_jobs([[lambda{|x,y| output << x + y}, [1,1]], [lambda{|x| output << x}, [3]]])
38
45
  assert(output.include?(2))
39
46
  assert(output.include?(3))
47
+ @pool << [lambda{|x,y| output = [x,y]}, ['test', [1,2]]]
48
+ assert_equal(output[0], 'test')
49
+ assert(output[1].is_a?(Array))
40
50
  @pool.shutdown(true)
41
51
  end
42
52
  end
data/tests/cases/grow.rb CHANGED
@@ -5,16 +5,20 @@ class GrowPoolTest < Test::Unit::TestCase
5
5
  def setup
6
6
  @pool = ActionPool::Pool.new
7
7
  end
8
+ def teardown
9
+ @pool.shutdown(true)
10
+ end
8
11
  def test_grow
9
- jobs = [].fill(lambda{}, 0..99)
12
+ jobs = [].fill(lambda{sleep}, 0..20)
10
13
  @pool.add_jobs(jobs)
14
+ Thread.pass
11
15
  assert(@pool.size > 10)
12
16
  @pool.shutdown(true)
13
17
  end
14
18
  def test_max
15
- @pool.create_thread(true) until @pool.size > @pool.max
16
- assert_nil(@pool.create_thread)
17
- assert(@pool.create_thread(true))
19
+ @pool.create_thread(:force) until @pool.size > @pool.max
20
+ assert(@pool.create_thread.nil?)
21
+ assert(!@pool.create_thread(:force).nil?)
18
22
  @pool.shutdown(true)
19
23
  end
20
24
  end
@@ -5,6 +5,9 @@ class NoGrowPoolTest < Test::Unit::TestCase
5
5
  def setup
6
6
  @pool = ActionPool::Pool.new
7
7
  end
8
+ def teardown
9
+ @pool.shutdown(true)
10
+ end
8
11
  def test_nogrow
9
12
  5.times{ @pool << lambda{} }
10
13
  assert_equal(10, @pool.size)
@@ -5,20 +5,26 @@ class ResizePoolTest < Test::Unit::TestCase
5
5
  def setup
6
6
  @pool = ActionPool::Pool.new
7
7
  end
8
+ def teardown
9
+ @pool.shutdown(true)
10
+ end
8
11
  def test_resize
9
12
  stop = false
10
- 20.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
13
+ 20.times{ @pool << lambda{ sleep } }
14
+ Thread.pass
15
+ sleep(0.1)
11
16
  assert(@pool.size > 10)
12
17
  stop = true
13
18
  @pool.shutdown(true)
19
+ @pool.status :open
14
20
  @pool.max = 10
15
21
  assert_equal(10, @pool.max)
16
- assert_equal(0, @pool.size)
17
22
  stop = false
18
23
  20.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
19
24
  assert_equal(10, @pool.size)
20
25
  stop = true
21
26
  @pool.shutdown(true)
27
+ @pool.status :open
22
28
  @pool.max = 20
23
29
  stop = false
24
30
  30.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
@@ -5,12 +5,32 @@ class ShutdownPoolTest < Test::Unit::TestCase
5
5
  def setup
6
6
  @pool = ActionPool::Pool.new
7
7
  end
8
+ def teardown
9
+ @pool.shutdown(true)
10
+ end
11
+ def test_close
12
+ result = 0
13
+ @pool << lambda{ result = 5 }
14
+ sleep(0.01)
15
+ assert(5, result)
16
+ @pool.status :closed
17
+ assert_raise(ActionPool::PoolClosed) do
18
+ @pool << lambda{}
19
+ end
20
+ assert_raise(ActionPool::PoolClosed) do
21
+ @pool.add_jobs [lambda{}, lambda{}]
22
+ end
23
+ @pool.shutdown(true)
24
+ end
8
25
  def test_shutdown
9
26
  assert_equal(10, @pool.size)
10
27
  @pool.shutdown
11
28
  sleep(0.5)
12
29
  assert_equal(0, @pool.size)
13
- @pool << lambda{}
30
+ @pool.status :open
31
+ @pool << lambda{ sleep }
32
+ sleep(0.01)
14
33
  assert_equal(10, @pool.size)
34
+ @pool.shutdown(true)
15
35
  end
16
36
  end
@@ -6,6 +6,9 @@ class ThreadTest < Test::Unit::TestCase
6
6
  @pool = ActionPool::Pool.new(:min_threads => 1, :max_threads => 1)
7
7
  @thread = ActionPool::Thread.new(:pool => @pool, :respond_thread => self, :t_timeout => 60, :a_timeout => 0)
8
8
  end
9
+ def teardown
10
+ @pool.shutdown(true)
11
+ end
9
12
  def test_thread
10
13
  sleep(0.01)
11
14
  assert(@thread.waiting?)
@@ -5,15 +5,33 @@ class TimeoutPoolTest < Test::Unit::TestCase
5
5
  def setup
6
6
  @pool = ActionPool::Pool.new
7
7
  end
8
- def test_threadtimeout
9
- @pool.thread_timeout = 0.01
8
+ def teardown
9
+ @pool.shutdown(true)
10
+ end
11
+ def test_actiontimeout
12
+ @pool.action_timeout = 0.01
10
13
  assert_equal(10, @pool.size)
11
14
  stop = false
12
- 20.times{ @pool << lambda{ a = 0; a += 1 until stop || a > 9999999999 } }
13
- assert(@pool.size > 10)
15
+ @pool.add_jobs [].fill(lambda{loop{ 1+1 }}, 0, 20)
16
+ ::Thread.pass
17
+ assert(@pool.working > 10)
14
18
  stop = true
15
- sleep(2)
16
- assert(@pool.size <= 10)
19
+ sleep(0.5)
20
+ assert(@pool.working == 0)
21
+ @pool.shutdown(true)
22
+ end
23
+ def test_threadtimeout
24
+ @pool.thread_timeout = 0.01
25
+ assert_equal(10, @pool.size)
26
+ lock = Mutex.new
27
+ guard = ConditionVariable.new
28
+ @pool.add_jobs [].fill(lambda{ lock.synchronize{ guard.wait(lock) } }, 0, 20)
29
+ ::Thread.pass
30
+ assert_equal(30, @pool.size)
31
+ lock.synchronize{ guard.broadcast }
32
+ ::Thread.pass
33
+ sleep(0.1)
34
+ assert(10, @pool.size)
17
35
  @pool.shutdown(true)
18
36
  end
19
37
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ActionPool
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - spox
@@ -9,11 +9,11 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-04 00:00:00 -08:00
12
+ date: 2009-12-11 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
16
- description: ""
16
+ description: The ActionPool is an easy to use thread pool for ruby.
17
17
  email: spox@modspox.com
18
18
  executables: []
19
19
 
@@ -25,8 +25,6 @@ extra_rdoc_files:
25
25
  files:
26
26
  - Rakefile
27
27
  - actionpool.gemspec
28
- - tests
29
- - tests/cases
30
28
  - tests/cases/general.rb
31
29
  - tests/cases/resize.rb
32
30
  - tests/cases/nogrow.rb
@@ -36,8 +34,6 @@ files:
36
34
  - tests/cases/queue.rb
37
35
  - tests/cases/grow.rb
38
36
  - tests/run_tests.rb
39
- - lib
40
- - lib/actionpool
41
37
  - lib/actionpool/Pool.rb
42
38
  - lib/actionpool/Thread.rb
43
39
  - lib/actionpool/LogHelper.rb
@@ -46,8 +42,10 @@ files:
46
42
  - CHANGELOG
47
43
  - LICENSE
48
44
  - README.rdoc
49
- has_rdoc: false
45
+ has_rdoc: true
50
46
  homepage: http://github.com/spox/actionpool
47
+ licenses: []
48
+
51
49
  post_install_message:
52
50
  rdoc_options:
53
51
  - --title
@@ -72,9 +70,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
70
  requirements: []
73
71
 
74
72
  rubyforge_project:
75
- rubygems_version: 1.3.1
73
+ rubygems_version: 1.3.5
76
74
  signing_key:
77
- specification_version: 2
75
+ specification_version: 3
78
76
  summary: Thread Pool
79
77
  test_files: []
80
78