actionpool 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 +13 -0
- data/LICENSE +165 -0
- data/README.rdoc +192 -0
- data/Rakefile +74 -0
- data/actionpool.gemspec +15 -0
- data/lib/actionpool/LogHelper.rb +25 -0
- data/lib/actionpool/Pool.rb +282 -0
- data/lib/actionpool/Pool.rb~ +260 -0
- data/lib/actionpool/Queue.rb +53 -0
- data/lib/actionpool/Queue.rb~ +46 -0
- data/lib/actionpool/Thread.rb +155 -0
- data/lib/actionpool/Thread.rb~ +146 -0
- data/lib/actionpool.rb +7 -0
- data/test.rb +7 -0
- data/tests/cases/general.rb +52 -0
- data/tests/cases/general.rb~ +45 -0
- data/tests/cases/grow.rb +24 -0
- data/tests/cases/nogrow.rb +15 -0
- data/tests/cases/queue.rb +34 -0
- data/tests/cases/resize.rb +35 -0
- data/tests/cases/shutdown.rb +36 -0
- data/tests/cases/thread.rb +27 -0
- data/tests/cases/timeouts.rb +37 -0
- data/tests/run_tests.rb +8 -0
- metadata +83 -0
@@ -0,0 +1,282 @@
|
|
1
|
+
require 'actionpool/Thread'
|
2
|
+
require 'actionpool/Queue'
|
3
|
+
require 'actionpool/LogHelper'
|
4
|
+
require 'thread'
|
5
|
+
|
6
|
+
module ActionPool
|
7
|
+
# Raised when pool is closed
|
8
|
+
class PoolClosed < StandardError
|
9
|
+
end
|
10
|
+
class Pool
|
11
|
+
|
12
|
+
# :min_threads:: minimum number of threads in pool
|
13
|
+
# :max_threads:: maximum number of threads in pool
|
14
|
+
# :t_to:: thread timeout waiting for action to process
|
15
|
+
# :a_to:: maximum time action may be worked on before aborting
|
16
|
+
# :logger:: logger to print logging messages to
|
17
|
+
# Creates a new pool
|
18
|
+
def initialize(args={})
|
19
|
+
raise ArgumentError.new('Hash required for initialization') unless args.is_a?(Hash)
|
20
|
+
@logger = LogHelper.new(args[:logger])
|
21
|
+
@queue = ActionPool::Queue.new
|
22
|
+
@threads = []
|
23
|
+
@lock = Mutex.new
|
24
|
+
@thread_timeout = args[:t_to] ? args[:t_to] : 0
|
25
|
+
@action_timeout = args[:a_to] ? args[:a_to] : 0
|
26
|
+
@max_threads = args[:max_threads] ? args[:max_threads] : 100
|
27
|
+
@min_threads = args[:min_threads] ? args[:min_threads] : 10
|
28
|
+
@min_threads = @max_threads if @max_threads < @min_threads
|
29
|
+
@respond_to = args[:respond_thread] || ::Thread.current
|
30
|
+
@open = true
|
31
|
+
fill_pool
|
32
|
+
end
|
33
|
+
|
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
|
57
|
+
@lock.synchronize do
|
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
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
return threads
|
81
|
+
end
|
82
|
+
|
83
|
+
# force:: force immediate stop
|
84
|
+
# Stop the pool
|
85
|
+
def shutdown(force=false)
|
86
|
+
status(:closed)
|
87
|
+
args = []
|
88
|
+
args.push(:force) if force
|
89
|
+
@logger.info("Pool is now shutting down #{force ? 'using force' : ''}")
|
90
|
+
@queue.clear if force
|
91
|
+
@queue.wait_empty
|
92
|
+
while(t = @threads.pop) do
|
93
|
+
t.stop(*args)
|
94
|
+
end
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
|
98
|
+
# action:: proc to be executed or array of [proc, [*args]]
|
99
|
+
# Add a new proc/lambda to be executed (alias for queue)
|
100
|
+
def <<(action)
|
101
|
+
case action
|
102
|
+
when Proc
|
103
|
+
queue(action)
|
104
|
+
when Array
|
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)
|
106
|
+
queue(action[0], action[1])
|
107
|
+
else
|
108
|
+
raise ArgumentError.new('Actions to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]')
|
109
|
+
end
|
110
|
+
nil
|
111
|
+
end
|
112
|
+
|
113
|
+
# action:: proc to be executed
|
114
|
+
# Add a new proc/lambda to be executed
|
115
|
+
def queue(action, *args)
|
116
|
+
raise PoolClosed.new("Pool #{self} is currently closed") if pool_closed?
|
117
|
+
raise ArgumentError.new('Expecting block') unless action.is_a?(Proc)
|
118
|
+
@queue << [action, args]
|
119
|
+
::Thread.pass
|
120
|
+
create_thread
|
121
|
+
end
|
122
|
+
|
123
|
+
# jobs:: Array of proc/lambdas
|
124
|
+
# Will queue a list of jobs into the pool
|
125
|
+
def add_jobs(jobs)
|
126
|
+
raise PoolClosed.new("Pool #{self} is currently closed") if pool_closed?
|
127
|
+
raise ArgumentError.new("Expecting an array but received: #{jobs.class}") unless jobs.is_a?(Array)
|
128
|
+
@queue.pause
|
129
|
+
begin
|
130
|
+
jobs.each do |job|
|
131
|
+
case job
|
132
|
+
when Proc
|
133
|
+
@queue << [job, []]
|
134
|
+
when Array
|
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)
|
136
|
+
@queue << [job.shift, job]
|
137
|
+
else
|
138
|
+
raise ArgumentError.new('Jobs to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]')
|
139
|
+
end
|
140
|
+
end
|
141
|
+
ensure
|
142
|
+
num = jobs.size - @threads.select{|t|t.waiting?}.size
|
143
|
+
num.times{ create_thread(:nowait) } if num > 0
|
144
|
+
@queue.unpause
|
145
|
+
end
|
146
|
+
true
|
147
|
+
end
|
148
|
+
|
149
|
+
# block:: block to process
|
150
|
+
# Adds a block to be processed
|
151
|
+
def process(*args, &block)
|
152
|
+
queue(block, *args)
|
153
|
+
nil
|
154
|
+
end
|
155
|
+
|
156
|
+
# Current size of pool
|
157
|
+
def size
|
158
|
+
@threads.size
|
159
|
+
end
|
160
|
+
|
161
|
+
# Maximum allowed number of threads
|
162
|
+
def max
|
163
|
+
@max_threads
|
164
|
+
end
|
165
|
+
|
166
|
+
# Minimum allowed number of threads
|
167
|
+
def min
|
168
|
+
@min_threads
|
169
|
+
end
|
170
|
+
|
171
|
+
# m:: new max
|
172
|
+
# Set maximum number of threads
|
173
|
+
def max=(m)
|
174
|
+
m = m.to_i
|
175
|
+
raise ArgumentError.new('Maximum value must be greater than 0') unless m > 0
|
176
|
+
@max_threads = m
|
177
|
+
@min_threads = m if m < @min_threads
|
178
|
+
resize if m < size
|
179
|
+
m
|
180
|
+
end
|
181
|
+
|
182
|
+
# m:: new min
|
183
|
+
# Set minimum number of threads
|
184
|
+
def min=(m)
|
185
|
+
m = m.to_i
|
186
|
+
raise ArgumentError.new("Minimum value must be greater than 0 and less than or equal to maximum (#{max})") unless m > 0 && m <= max
|
187
|
+
@min_threads = m
|
188
|
+
m
|
189
|
+
end
|
190
|
+
|
191
|
+
# t:: ActionPool::Thread to remove
|
192
|
+
# Removes a thread from the pool
|
193
|
+
def remove(t)
|
194
|
+
raise ArgumentError.new('Expecting an ActionPool::Thread object') unless t.is_a?(ActionPool::Thread)
|
195
|
+
t.stop
|
196
|
+
if(@threads.include?(t))
|
197
|
+
@threads.delete(t)
|
198
|
+
return true
|
199
|
+
else
|
200
|
+
return false
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Maximum number of seconds a thread
|
205
|
+
# is allowed to idle in the pool.
|
206
|
+
# (nil means thread life is infinite)
|
207
|
+
def thread_timeout
|
208
|
+
@thread_timeout
|
209
|
+
end
|
210
|
+
|
211
|
+
# Maximum number of seconds a thread
|
212
|
+
# is allowed to work on a given action
|
213
|
+
# (nil means thread is given unlimited
|
214
|
+
# time to work on action)
|
215
|
+
def action_timeout
|
216
|
+
@action_timeout
|
217
|
+
end
|
218
|
+
|
219
|
+
# t:: timeout in seconds (nil for infinite)
|
220
|
+
# Set maximum allowed time thead may idle in pool
|
221
|
+
def thread_timeout=(t)
|
222
|
+
t = t.to_f
|
223
|
+
raise ArgumentError.new('Value must be greater than zero or nil') unless t >= 0
|
224
|
+
@thread_timeout = t
|
225
|
+
@threads.each{|thread|thread.thread_timeout = t}
|
226
|
+
t
|
227
|
+
end
|
228
|
+
|
229
|
+
# t:: timeout in seconds (nil for infinte)
|
230
|
+
# Set maximum allowed time thread may work
|
231
|
+
# on a given action
|
232
|
+
def action_timeout=(t)
|
233
|
+
t = t.to_f
|
234
|
+
raise ArgumentError.new('Value must be greater than zero or nil') unless t >= 0
|
235
|
+
@action_timeout = t
|
236
|
+
@threads.each{|thread|thread.action_timeout = t}
|
237
|
+
t
|
238
|
+
end
|
239
|
+
|
240
|
+
# Returns the next action to be processed
|
241
|
+
def action
|
242
|
+
@queue.pop
|
243
|
+
end
|
244
|
+
|
245
|
+
# Number of actions in the queue
|
246
|
+
def action_size
|
247
|
+
@queue.size
|
248
|
+
end
|
249
|
+
|
250
|
+
# Flush the thread pool. Mainly used for forcibly resizing
|
251
|
+
# the pool if existing threads have a long thread life waiting
|
252
|
+
# for input.
|
253
|
+
def flush
|
254
|
+
lock = Mutex.new
|
255
|
+
guard = ConditionVariable.new
|
256
|
+
@threads.size.times{ queue{ lock.synchronize{ guard.wait(lock) } } }
|
257
|
+
Thread.pass
|
258
|
+
sleep(0.01)
|
259
|
+
lock.synchronize{ guard.broadcast }
|
260
|
+
end
|
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
|
+
|
267
|
+
private
|
268
|
+
|
269
|
+
# Resize the pool
|
270
|
+
def resize
|
271
|
+
@logger.info("Pool is being resized to stated maximum: #{max}")
|
272
|
+
until(size <= max) do
|
273
|
+
t = nil
|
274
|
+
t = @threads.find{|t|t.waiting?}
|
275
|
+
t = @threads.shift unless t
|
276
|
+
t.stop
|
277
|
+
end
|
278
|
+
flush
|
279
|
+
nil
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
@@ -0,0 +1,260 @@
|
|
1
|
+
require 'actionpool/Thread'
|
2
|
+
require 'actionpool/Queue'
|
3
|
+
require 'actionpool/LogHelper'
|
4
|
+
require 'thread'
|
5
|
+
|
6
|
+
module ActionPool
|
7
|
+
# Raised when pool is closed
|
8
|
+
class PoolClosed < StandardError
|
9
|
+
end
|
10
|
+
class Pool
|
11
|
+
|
12
|
+
# :min_threads:: minimum number of threads in pool
|
13
|
+
# :max_threads:: maximum number of threads in pool
|
14
|
+
# :t_to:: thread timeout waiting for action to process
|
15
|
+
# :a_to:: maximum time action may be worked on before aborting
|
16
|
+
# :logger:: logger to print logging messages to
|
17
|
+
# Creates a new pool
|
18
|
+
def initialize(args={})
|
19
|
+
raise ArgumentError.new('Hash required for initialization') unless args.is_a?(Hash)
|
20
|
+
@logger = LogHelper.new(args[:logger])
|
21
|
+
@queue = ActionPool::Queue.new
|
22
|
+
@threads = []
|
23
|
+
@lock = Mutex.new
|
24
|
+
@thread_timeout = args[:t_to] ? args[:t_to] : 0
|
25
|
+
@action_timeout = args[:a_to] ? args[:a_to] : 0
|
26
|
+
@max_threads = args[:max_threads] ? args[:max_threads] : 100
|
27
|
+
@min_threads = args[:min_threads] ? args[:min_threads] : 10
|
28
|
+
@min_threads = @max_threads if @max_threads < @min_threads
|
29
|
+
@respond_to = args[:respond_thread] || ::Thread.current
|
30
|
+
@open = true
|
31
|
+
create_thread
|
32
|
+
end
|
33
|
+
|
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
|
+
end
|
49
|
+
|
50
|
+
# force:: force creation of a new thread
|
51
|
+
# Create a new thread for pool. Returns newly created ActionPool::Thread or
|
52
|
+
# nil if pool has reached maximum threads
|
53
|
+
def create_thread(force=false)
|
54
|
+
return if pool_closed?
|
55
|
+
pt = nil
|
56
|
+
@lock.synchronize do
|
57
|
+
if(@threads.size < @max_threads || force)
|
58
|
+
@logger.info('Pool is creating a new thread')
|
59
|
+
(min - size > 0 ? min - size : 1).times do |i|
|
60
|
+
pt = ActionPool::Thread.new(:pool => self, :respond_thread => @respond_to, :a_timeout => @action_timeout, :t_timeout => @thread_timeout, :logger => @logger)
|
61
|
+
@threads << pt
|
62
|
+
end
|
63
|
+
else
|
64
|
+
@logger.info('Pool is at maximum size. Not creating new thread')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
return pt
|
68
|
+
end
|
69
|
+
|
70
|
+
# force:: force immediate stop
|
71
|
+
# Stop the pool
|
72
|
+
def shutdown(force=false)
|
73
|
+
args = [:wait]
|
74
|
+
args += [:force] if force
|
75
|
+
@logger.info("Pool is now shutting down #{force ? 'using force' : ''}")
|
76
|
+
@queue.wait_empty
|
77
|
+
while(t = @threads.pop) do
|
78
|
+
t.stop(*args)
|
79
|
+
end
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
# action:: proc to be executed or array of [proc, [*args]]
|
84
|
+
# Add a new proc/lambda to be executed (alias for queue)
|
85
|
+
def <<(action)
|
86
|
+
raise PoolClosed.new("Pool #{self} is currently closed") if pool_closed?
|
87
|
+
case action
|
88
|
+
when Proc
|
89
|
+
queue(action)
|
90
|
+
when Array
|
91
|
+
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)
|
92
|
+
queue(action[0], action[1])
|
93
|
+
else
|
94
|
+
raise ArgumentError.new('Actions to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]')
|
95
|
+
end
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
|
99
|
+
# action:: proc to be executed
|
100
|
+
# Add a new proc/lambda to be executed
|
101
|
+
def queue(action, *args)
|
102
|
+
raise ArgumentError.new('Expecting block') unless action.is_a?(Proc)
|
103
|
+
@queue << [action, args]
|
104
|
+
::Thread.pass
|
105
|
+
create_thread if @queue.num_waiting < 1 # only start a new thread if we need it
|
106
|
+
end
|
107
|
+
|
108
|
+
# jobs:: Array of proc/lambdas
|
109
|
+
# Will queue a list of jobs into the pool
|
110
|
+
def add_jobs(jobs)
|
111
|
+
raise ArgumentError.new("Expecting an array but received: #{jobs.class}") unless jobs.is_a?(Array)
|
112
|
+
@queue.pause
|
113
|
+
begin
|
114
|
+
jobs.each do |job|
|
115
|
+
case job
|
116
|
+
when Proc
|
117
|
+
@queue << [job, []]
|
118
|
+
when Array
|
119
|
+
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)
|
120
|
+
@queue << [job.unshift, job]
|
121
|
+
else
|
122
|
+
raise ArgumentError.new('Jobs to be processed by the pool must be a proc/lambda or [proc/lambda, [*args]]')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
ensure
|
126
|
+
create_thread
|
127
|
+
@queue.unpause
|
128
|
+
end
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
# block:: block to process
|
133
|
+
# Adds a block to be processed
|
134
|
+
def process(*args, &block)
|
135
|
+
queue(block, *args)
|
136
|
+
nil
|
137
|
+
end
|
138
|
+
|
139
|
+
# Current size of pool
|
140
|
+
def size
|
141
|
+
@threads.size
|
142
|
+
end
|
143
|
+
|
144
|
+
# Maximum allowed number of threads
|
145
|
+
def max
|
146
|
+
@max_threads
|
147
|
+
end
|
148
|
+
|
149
|
+
# Minimum allowed number of threads
|
150
|
+
def min
|
151
|
+
@min_threads
|
152
|
+
end
|
153
|
+
|
154
|
+
# m:: new max
|
155
|
+
# Set maximum number of threads
|
156
|
+
def max=(m)
|
157
|
+
m = m.to_i
|
158
|
+
raise ArgumentError.new('Maximum value must be greater than 0') unless m > 0
|
159
|
+
@max_threads = m
|
160
|
+
@min_threads = m if m < @min_threads
|
161
|
+
resize if m < size
|
162
|
+
m
|
163
|
+
end
|
164
|
+
|
165
|
+
# m:: new min
|
166
|
+
# Set minimum number of threads
|
167
|
+
def min=(m)
|
168
|
+
m = m.to_i
|
169
|
+
raise ArgumentError.new("Minimum value must be greater than 0 and less than or equal to maximum (#{max})") unless m > 0 && m <= max
|
170
|
+
@min_threads = m
|
171
|
+
m
|
172
|
+
end
|
173
|
+
|
174
|
+
# t:: ActionPool::Thread to remove
|
175
|
+
# Removes a thread from the pool
|
176
|
+
def remove(t)
|
177
|
+
raise ArgumentError.new('Expecting an ActionPool::Thread object') unless t.is_a?(ActionPool::Thread)
|
178
|
+
t.stop
|
179
|
+
if(@threads.include?(t))
|
180
|
+
@threads.delete(t)
|
181
|
+
return true
|
182
|
+
else
|
183
|
+
return false
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Maximum number of seconds a thread
|
188
|
+
# is allowed to idle in the pool.
|
189
|
+
# (nil means thread life is infinite)
|
190
|
+
def thread_timeout
|
191
|
+
@thread_timeout
|
192
|
+
end
|
193
|
+
|
194
|
+
# Maximum number of seconds a thread
|
195
|
+
# is allowed to work on a given action
|
196
|
+
# (nil means thread is given unlimited
|
197
|
+
# time to work on action)
|
198
|
+
def action_timeout
|
199
|
+
@action_timeout
|
200
|
+
end
|
201
|
+
|
202
|
+
# t:: timeout in seconds (nil for infinite)
|
203
|
+
# Set maximum allowed time thead may idle in pool
|
204
|
+
def thread_timeout=(t)
|
205
|
+
t = t.to_f
|
206
|
+
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
207
|
+
@thread_timeout = t
|
208
|
+
@threads.each{|thread|thread.thread_timeout = t}
|
209
|
+
t
|
210
|
+
end
|
211
|
+
|
212
|
+
# t:: timeout in seconds (nil for infinte)
|
213
|
+
# Set maximum allowed time thread may work
|
214
|
+
# on a given action
|
215
|
+
def action_timeout=(t)
|
216
|
+
t = t.to_f
|
217
|
+
raise ArgumentError.new('Value must be great than zero or nil') unless t > 0
|
218
|
+
@action_timeout = t
|
219
|
+
@threads.each{|thread|thread.action_timeout = t}
|
220
|
+
t
|
221
|
+
end
|
222
|
+
|
223
|
+
# Returns the next action to be processed
|
224
|
+
def action
|
225
|
+
@queue.pop
|
226
|
+
end
|
227
|
+
|
228
|
+
# Number of actions in the queue
|
229
|
+
def action_size
|
230
|
+
@queue.size
|
231
|
+
end
|
232
|
+
|
233
|
+
# Flush the thread pool. Mainly used for forcibly resizing
|
234
|
+
# the pool if existing threads have a long thread life waiting
|
235
|
+
# for input.
|
236
|
+
def flush
|
237
|
+
lock = Mutex.new
|
238
|
+
guard = ConditionVariable.new
|
239
|
+
@threads.size.times{ queue{ lock.synchronize{ guard.wait(lock) } } }
|
240
|
+
Thread.pass
|
241
|
+
sleep(0.01)
|
242
|
+
lock.synchronize{ guard.broadcast }
|
243
|
+
end
|
244
|
+
|
245
|
+
private
|
246
|
+
|
247
|
+
# Resize the pool
|
248
|
+
def resize
|
249
|
+
@logger.info("Pool is being resized to stated maximum: #{max}")
|
250
|
+
until(size <= max) do
|
251
|
+
t = nil
|
252
|
+
t = @threads.find{|t|t.waiting?}
|
253
|
+
t = @threads.shift unless t
|
254
|
+
t.stop
|
255
|
+
end
|
256
|
+
flush
|
257
|
+
nil
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module ActionPool
|
4
|
+
# Adds a little bit extra functionality to the Queue class
|
5
|
+
class Queue < ::Queue
|
6
|
+
# Create a new Queue for the ActionPool::Pool
|
7
|
+
def initialize
|
8
|
+
super
|
9
|
+
@wait = false
|
10
|
+
@pause_lock = Mutex.new
|
11
|
+
@empty_lock = Mutex.new
|
12
|
+
@pause_guard = ConditionVariable.new
|
13
|
+
@empty_guard = ConditionVariable.new
|
14
|
+
end
|
15
|
+
# Stop the queue from returning results to requesting
|
16
|
+
# threads. Threads will wait for results until signalled
|
17
|
+
def pause
|
18
|
+
@pause_lock.synchronize{@wait = true}
|
19
|
+
end
|
20
|
+
# Allow the queue to return results. Any threads waiting
|
21
|
+
# will have results given to them.
|
22
|
+
def unpause
|
23
|
+
@pause_lock.synchronize do
|
24
|
+
@wait = false
|
25
|
+
@pause_guard.broadcast
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# Check if queue needs to wait before returning
|
29
|
+
def pop
|
30
|
+
@pause_lock.synchronize do
|
31
|
+
@pause_guard.wait(@pause_lock) if @wait
|
32
|
+
end
|
33
|
+
o = super
|
34
|
+
@empty_lock.synchronize do
|
35
|
+
@empty_guard.broadcast if empty?
|
36
|
+
end
|
37
|
+
return o
|
38
|
+
end
|
39
|
+
# Clear queue
|
40
|
+
def clear
|
41
|
+
super
|
42
|
+
@empty_lock.synchronize do
|
43
|
+
@empty_guard.broadcast
|
44
|
+
end
|
45
|
+
end
|
46
|
+
# Park a thread here until queue is empty
|
47
|
+
def wait_empty
|
48
|
+
@empty_lock.synchronize do
|
49
|
+
@empty_guard.wait(@empty_lock) if size > 0
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module ActionPool
|
4
|
+
# Adds a little bit extra functionality to the Queue class
|
5
|
+
class Queue < ::Queue
|
6
|
+
# Create a new Queue for the ActionPool::Pool
|
7
|
+
def initialize
|
8
|
+
super
|
9
|
+
@wait = false
|
10
|
+
@pause_lock = Mutex.new
|
11
|
+
@emtpy_lock = Mutex.new
|
12
|
+
@pause_guard = ConditionVariable.new
|
13
|
+
@empty_guard = ConditionVariable.new
|
14
|
+
end
|
15
|
+
# Stop the queue from returning results to requesting
|
16
|
+
# threads. Threads will wait for results until signalled
|
17
|
+
def pause
|
18
|
+
@pause_lock.synchronize{@wait = true}
|
19
|
+
end
|
20
|
+
# Allow the queue to return results. Any threads waiting
|
21
|
+
# will have results given to them.
|
22
|
+
def unpause
|
23
|
+
@pause_lock.synchronize do
|
24
|
+
@wait = false
|
25
|
+
@pause_guard.broadcast
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# Check if queue needs to wait before returning
|
29
|
+
def pop
|
30
|
+
@pause_lock.synchronize do
|
31
|
+
@pause_guard.wait(@pause_lock) if @wait
|
32
|
+
end
|
33
|
+
o = super
|
34
|
+
@empty_lock.synchronize do
|
35
|
+
@empty_guard.broadcast if empty?
|
36
|
+
end
|
37
|
+
return o
|
38
|
+
end
|
39
|
+
# Park a thread here until queue is empty
|
40
|
+
def wait_empty
|
41
|
+
@empty_lock.synchronize do
|
42
|
+
@empty_guard.wait(@empty_lock) if size > 0
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|