ActionPool 0.2.1 → 0.2.2

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/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