work_queue 2.0.1 → 2.5.0
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/LICENSE +1 -1
- data/README.rdoc +32 -4
- data/lib/work_queue.rb +239 -208
- data/tasks/test.rake +0 -2
- data/test/tc_work_queue.rb +66 -48
- metadata +9 -9
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -10,7 +10,8 @@ Work queues are useful for several reasons:
|
|
10
10
|
* To easily perform tasks asynchronously and concurrently in your application;
|
11
11
|
* To let you focus on the work you actually want to perform without having to worry about the thread creation and management;
|
12
12
|
* To minimize overhead, by reusing previously constructed threads rather than creating new ones;
|
13
|
-
* To bound
|
13
|
+
* To bound the resources used, by setting a limit on the maximum number of simultaneously executing threads;
|
14
|
+
|
14
15
|
|
15
16
|
= Usage
|
16
17
|
|
@@ -26,9 +27,36 @@ Run the code:
|
|
26
27
|
wq.enqueue_b { puts "Hello from the WorkQueue" }
|
27
28
|
wq.join
|
28
29
|
|
29
|
-
|
30
|
+
It's generally recommended to bound the resources used:
|
30
31
|
|
31
32
|
# Limit the maximum number of simultaneous worker threads
|
32
|
-
WorkQueue.new
|
33
|
+
WorkQueue.new 10, nil
|
33
34
|
# Limit the maximum number of queued tasks
|
34
|
-
WorkQueue.new
|
35
|
+
WorkQueue.new nil, 20
|
36
|
+
|
37
|
+
|
38
|
+
= Example(s)
|
39
|
+
|
40
|
+
Download multiple files in parallel:
|
41
|
+
|
42
|
+
require 'open-uri'
|
43
|
+
require 'rubygems'
|
44
|
+
require 'work_queue'
|
45
|
+
|
46
|
+
wq = WorkQueue.new 5
|
47
|
+
|
48
|
+
(1..6605).each do |number|
|
49
|
+
wq.enqueue_b do
|
50
|
+
open("rfc#{number}.txt", "wb") do |file|
|
51
|
+
file.write open("http://www.rfc-editor.org/rfc/rfc#{number}.txt").read
|
52
|
+
puts "rfc#{number} downloaded"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
wq.join
|
58
|
+
|
59
|
+
|
60
|
+
= License
|
61
|
+
|
62
|
+
Released under the MIT license.
|
data/lib/work_queue.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'monitor'
|
3
|
+
|
1
4
|
##
|
2
5
|
# = Name
|
3
6
|
# WorkQueue
|
@@ -6,231 +9,259 @@
|
|
6
9
|
# This file contains an implementation of a work queue structure.
|
7
10
|
#
|
8
11
|
# == Version
|
9
|
-
# 2.
|
12
|
+
# 2.5.0
|
10
13
|
#
|
11
14
|
# == Author
|
12
|
-
# Miguel Fonseca <
|
15
|
+
# Miguel Fonseca <contact@miguelfonseca.com>
|
13
16
|
#
|
14
17
|
# == Copyright
|
15
|
-
# Copyright
|
18
|
+
# Copyright 2012 Miguel Fonseca
|
16
19
|
#
|
17
20
|
# == License
|
18
21
|
# MIT (see LICENSE file)
|
19
22
|
#
|
20
23
|
|
21
|
-
require 'thread'
|
22
|
-
require 'monitor'
|
23
|
-
|
24
|
-
##
|
25
|
-
# = WorkQueue
|
26
|
-
#
|
27
|
-
# == Description
|
28
|
-
# A tunable work queue, designed to coordinate work between a producer and a pool of worker threads.
|
29
|
-
#
|
30
|
-
# == Usage
|
31
|
-
# wq = WorkQueue.new
|
32
|
-
# wq.enqueue_b { puts "Hello from the WorkQueue" }
|
33
|
-
# wq.join
|
34
|
-
#
|
35
24
|
class WorkQueue
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
25
|
+
VERSION = "2.5.0"
|
26
|
+
|
27
|
+
##
|
28
|
+
# Creates a new work queue with the desired parameters.
|
29
|
+
# It's generally recommended to bound the resources used.
|
30
|
+
#
|
31
|
+
# ==== Parameter(s)
|
32
|
+
# * +max_threads+ - Maximum number of worker threads.
|
33
|
+
# * +max_tasks+ - Maximum number of queued tasks.
|
34
|
+
#
|
35
|
+
# ==== Example(s)
|
36
|
+
# wq = WorkQueue.new 10, nil
|
37
|
+
# wq = WorkQueue.new nil, 20
|
38
|
+
# wq = WorkQueue.new 10, 20
|
39
|
+
#
|
40
|
+
def initialize(max_threads=nil, max_tasks=nil)
|
41
|
+
self.max_threads = max_threads
|
42
|
+
self.max_tasks = max_tasks
|
43
|
+
@threads = Array.new
|
44
|
+
@threads_waiting = 0
|
45
|
+
@threads.extend MonitorMixin
|
46
|
+
@tasks = Array.new
|
47
|
+
@tasks.extend MonitorMixin
|
48
|
+
@task_enqueued = @tasks.new_cond
|
49
|
+
@task_dequeued = @tasks.new_cond
|
50
|
+
@task_completed = @tasks.new_cond
|
51
|
+
@tasks_pending = 0
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Returns the maximum number of worker threads.
|
56
|
+
# This value is set upon initialization and cannot be changed afterwards.
|
57
|
+
#
|
58
|
+
# ==== Example(s)
|
59
|
+
# wq = WorkQueue.new
|
60
|
+
# wq.max_threads #=> Infinity
|
61
|
+
# wq = WorkQueue.new 1, nil
|
62
|
+
# wq.max_threads #=> 1
|
63
|
+
#
|
64
|
+
def max_threads
|
65
|
+
@max_threads
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Returns the current number of worker threads.
|
70
|
+
# This value is just a snapshot, and may change immediately upon returning.
|
71
|
+
#
|
72
|
+
# ==== Example(s)
|
73
|
+
# wq = WorkQueue.new 1
|
74
|
+
# wq.cur_threads #=> 0
|
75
|
+
# wq.enqueue_b { ... }
|
76
|
+
# wq.cur_threads #=> 1
|
77
|
+
#
|
78
|
+
def cur_threads
|
79
|
+
@threads.size
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Returns the maximum number of queued tasks.
|
84
|
+
# This value is set upon initialization and cannot be changed afterwards.
|
85
|
+
#
|
86
|
+
# ==== Example(s)
|
87
|
+
# wq = WorkQueue.new
|
88
|
+
# wq.max_tasks #=> Infinity
|
89
|
+
# wq = WorkQueue.new nil, 1
|
90
|
+
# wq.max_tasks #=> 1
|
91
|
+
#
|
92
|
+
def max_tasks
|
93
|
+
@max_tasks
|
94
|
+
end
|
95
|
+
|
96
|
+
##
|
97
|
+
# Returns the current number of queued tasks.
|
98
|
+
# This value is just a snapshot, and may change immediately upon returning.
|
99
|
+
#
|
100
|
+
# ==== Example(s)
|
101
|
+
# wq = WorkQueue.new 1
|
102
|
+
# wq.enqueue_b { ... }
|
103
|
+
# wq.cur_tasks #=> 0
|
104
|
+
# wq.enqueue_b { ... }
|
105
|
+
# wq.cur_tasks #=> 1
|
106
|
+
#
|
107
|
+
def cur_tasks
|
108
|
+
@tasks.size
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Schedules the given Block for future execution by a worker thread.
|
113
|
+
# If there is no space left in the queue, waits until space becomes available.
|
114
|
+
#
|
115
|
+
# ==== Parameter(s)
|
116
|
+
# * +params+ - Parameters passed to the given block.
|
117
|
+
#
|
118
|
+
# ==== Example(s)
|
119
|
+
# wq = WorkQueue.new
|
120
|
+
# wq.enqueue_b("Parameter") { |obj| ... }
|
121
|
+
#
|
122
|
+
def enqueue_b(*params, &block)
|
123
|
+
enqueue block, params
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# Schedules the given Proc for future execution by a worker thread.
|
128
|
+
# If there is no space left in the queue, waits until space becomes available.
|
129
|
+
#
|
130
|
+
# ==== Parameter(s)
|
131
|
+
# * +proc+ - Proc to be executed.
|
132
|
+
# * +params+ - Parameters passed to the given proc.
|
133
|
+
#
|
134
|
+
# ==== Example(s)
|
135
|
+
# wq = WorkQueue.new
|
136
|
+
# wq.enqueue_p(Proc.new { |obj| ... }, "Parameter")
|
137
|
+
#
|
138
|
+
def enqueue_p(proc, *params)
|
139
|
+
enqueue proc, params
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# Waits until the tasks queue is empty and all worker threads have finished.
|
144
|
+
#
|
145
|
+
# ==== Example(s)
|
146
|
+
# wq = WorkQueue.new
|
147
|
+
# wq.enqueue_b { ... }
|
148
|
+
# wq.join
|
149
|
+
#
|
150
|
+
def join
|
151
|
+
@tasks.synchronize do
|
152
|
+
@task_completed.wait_while { @tasks_pending > 0 }
|
143
153
|
end
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
+
end
|
155
|
+
|
156
|
+
##
|
157
|
+
# Halt all worker threads immediately, aborting any ongoing tasks.
|
158
|
+
# Resets all
|
159
|
+
#
|
160
|
+
# ==== Example(s)
|
161
|
+
# wq = WorkQueue.new
|
162
|
+
# wq.enqueue_b { ... }
|
163
|
+
# wq.kill
|
164
|
+
#
|
165
|
+
def kill
|
166
|
+
@tasks.synchronize do
|
167
|
+
@threads.synchronize do
|
168
|
+
@threads.each { |thread| thread.exit }
|
154
169
|
@threads.clear
|
155
170
|
@threads_waiting = 0
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
##
|
163
|
-
# Generic
|
164
|
-
#
|
165
|
-
def enqueue(proc, args)
|
166
|
-
@tasks.synchronize do
|
167
|
-
@task_completed.wait_while { cur_tasks >= max_tasks }
|
168
|
-
@tasks << [proc, args]
|
169
|
-
@cur_tasks += 1
|
170
|
-
@task_enqueued.signal
|
171
|
-
spawn_thread
|
172
|
-
end
|
171
|
+
end
|
172
|
+
@tasks.clear
|
173
|
+
@tasks_pending = 0
|
174
|
+
@task_dequeued.broadcast
|
175
|
+
@task_completed.broadcast
|
173
176
|
end
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
##
|
182
|
+
# Sets the maximum number of worker threads.
|
183
|
+
#
|
184
|
+
def max_threads=(value)
|
185
|
+
raise ArgumentError, "the maximum number of threads must be positive" if value and value <= 0
|
186
|
+
@max_threads = value || 1.0/0
|
187
|
+
end
|
188
|
+
|
189
|
+
##
|
190
|
+
# Sets the maximum number of queued tasks.
|
191
|
+
#
|
192
|
+
def max_tasks=(value)
|
193
|
+
raise ArgumentError, "the maximum number of tasks must be positive" if value and value <= 0
|
194
|
+
@max_tasks = value || 1.0/0
|
195
|
+
end
|
196
|
+
|
197
|
+
##
|
198
|
+
# Schedules the given Proc for future execution by a worker thread.
|
199
|
+
#
|
200
|
+
def enqueue(proc, params)
|
201
|
+
@tasks.synchronize do
|
202
|
+
@task_dequeued.wait_until { @tasks.size < @max_tasks }
|
203
|
+
@tasks << [proc, params]
|
204
|
+
@tasks_pending += 1
|
205
|
+
@task_enqueued.signal
|
181
206
|
end
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
207
|
+
spawn_thread
|
208
|
+
end
|
209
|
+
|
210
|
+
##
|
211
|
+
# Enrolls a new worker thread. The request is only carried out if necessary.
|
212
|
+
#
|
213
|
+
def spawn_thread
|
214
|
+
@tasks.synchronize do
|
215
|
+
@threads.synchronize do
|
216
|
+
if @threads.size < @max_threads && @threads_waiting <= 0 && @tasks.size > 0
|
217
|
+
@threads << Thread.new { run }
|
218
|
+
end
|
219
|
+
end
|
189
220
|
end
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
@threads.delete(Thread.current)
|
204
|
-
end
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# Repeatedly process the tasks queue.
|
225
|
+
#
|
226
|
+
def run
|
227
|
+
begin
|
228
|
+
loop do
|
229
|
+
proc, params = dequeue
|
230
|
+
begin
|
231
|
+
proc.call(*params)
|
232
|
+
rescue Exception => e
|
233
|
+
# Suppress Exception
|
208
234
|
end
|
235
|
+
conclude
|
236
|
+
end
|
237
|
+
ensure
|
238
|
+
@threads.synchronize do
|
239
|
+
@threads.delete Thread.current
|
240
|
+
end
|
209
241
|
end
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
end
|
242
|
+
end
|
243
|
+
|
244
|
+
##
|
245
|
+
# Retrieves a task from the queue.
|
246
|
+
#
|
247
|
+
def dequeue
|
248
|
+
@tasks.synchronize do
|
249
|
+
@threads_waiting += 1
|
250
|
+
@task_enqueued.wait_while { @tasks.empty? }
|
251
|
+
@threads_waiting -= 1
|
252
|
+
task = @tasks.shift
|
253
|
+
@task_dequeued.signal
|
254
|
+
return task
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
##
|
259
|
+
# Wind up a task execution.
|
260
|
+
#
|
261
|
+
def conclude
|
262
|
+
@tasks.synchronize do
|
263
|
+
@tasks_pending -= 1
|
264
|
+
@task_completed.signal
|
234
265
|
end
|
235
|
-
|
266
|
+
end
|
236
267
|
end
|
data/tasks/test.rake
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'rake/testtask'
|
2
2
|
|
3
3
|
namespace(:test) do
|
4
|
-
|
5
4
|
# For a list of all attributes refer to http://rake.rubyforge.org/classes/Rake/TestTask.html
|
6
5
|
Rake::TestTask.new(:unit) do |t|
|
7
6
|
t.libs << "test"
|
@@ -9,5 +8,4 @@ namespace(:test) do
|
|
9
8
|
t.verbose = true
|
10
9
|
t.warning = true
|
11
10
|
end
|
12
|
-
|
13
11
|
end
|
data/test/tc_work_queue.rb
CHANGED
@@ -6,10 +6,10 @@
|
|
6
6
|
# This file contains unit tests for the WorkQueue class.
|
7
7
|
#
|
8
8
|
# == Author
|
9
|
-
# Miguel Fonseca <
|
9
|
+
# Miguel Fonseca <contact@miguelfonseca.com>
|
10
10
|
#
|
11
11
|
# == Copyright
|
12
|
-
# Copyright
|
12
|
+
# Copyright 2012 Miguel Fonseca
|
13
13
|
#
|
14
14
|
# == License
|
15
15
|
# MIT (see LICENSE file)
|
@@ -18,24 +18,20 @@ require 'test/unit'
|
|
18
18
|
require 'lib/work_queue'
|
19
19
|
|
20
20
|
class TC_WorkQueue < Test::Unit::TestCase
|
21
|
-
|
22
|
-
# def setup
|
23
|
-
# end
|
24
|
-
|
25
|
-
# def teardown
|
26
|
-
# end
|
27
|
-
|
28
|
-
def test_enqueue
|
21
|
+
def test_enqueue_proc
|
29
22
|
s = String.new
|
30
23
|
wq = WorkQueue.new
|
31
|
-
|
32
|
-
wq.enqueue_p(Proc.new { |str| str.replace("Hello #1") }, s)
|
24
|
+
wq.enqueue_p(Proc.new { |str| str.replace "Hello Proc" }, s)
|
33
25
|
wq.join
|
34
|
-
assert_equal
|
35
|
-
|
36
|
-
|
26
|
+
assert_equal "Hello Proc", s
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_enqueue_block
|
30
|
+
s = String.new
|
31
|
+
wq = WorkQueue.new
|
32
|
+
wq.enqueue_b(s) { |str| str.replace "Hello Block" }
|
37
33
|
wq.join
|
38
|
-
assert_equal
|
34
|
+
assert_equal "Hello Block", s
|
39
35
|
end
|
40
36
|
|
41
37
|
def test_inner_enqueue
|
@@ -43,69 +39,91 @@ class TC_WorkQueue < Test::Unit::TestCase
|
|
43
39
|
wq = WorkQueue.new
|
44
40
|
wq.enqueue_b do
|
45
41
|
sleep 0.01
|
46
|
-
wq.enqueue_b(s) { |str| str.replace
|
42
|
+
wq.enqueue_b(s) { |str| str.replace "Hello Inner" }
|
47
43
|
sleep 0.01
|
48
44
|
end
|
49
45
|
wq.join
|
50
|
-
assert_equal
|
46
|
+
assert_equal "Hello Inner", s
|
51
47
|
end
|
52
48
|
|
53
49
|
def test_threads_recycle
|
54
50
|
wq = WorkQueue.new
|
55
51
|
wq.enqueue_b { sleep 0.01 }
|
56
52
|
sleep 0.02
|
57
|
-
assert_equal
|
53
|
+
assert_equal 1, wq.cur_threads
|
58
54
|
wq.enqueue_b { sleep 0.01 }
|
59
|
-
assert_equal
|
55
|
+
assert_equal 1, wq.cur_threads
|
60
56
|
wq.join
|
61
57
|
end
|
62
58
|
|
63
59
|
def test_max_threads
|
64
|
-
|
65
|
-
|
66
|
-
wq
|
67
|
-
assert_equal
|
68
|
-
wq.enqueue_b { sleep
|
69
|
-
assert_equal
|
70
|
-
|
71
|
-
assert_equal
|
72
|
-
sleep(0.1)
|
73
|
-
assert_equal(wq.cur_threads, 1)
|
60
|
+
wq = WorkQueue.new 1
|
61
|
+
assert_equal 0, wq.cur_threads
|
62
|
+
wq.enqueue_b { sleep 0.01 }
|
63
|
+
assert_equal 1, wq.cur_threads
|
64
|
+
wq.enqueue_b { sleep 0.01 }
|
65
|
+
assert_equal 1, wq.cur_threads
|
66
|
+
sleep 0.1
|
67
|
+
assert_equal 1, wq.cur_threads
|
74
68
|
wq.join
|
75
69
|
end
|
70
|
+
|
71
|
+
def test_max_threads_validation
|
72
|
+
assert_raise(ArgumentError) { WorkQueue.new 0, nil }
|
73
|
+
assert_raise(ArgumentError) { WorkQueue.new -1, nil }
|
74
|
+
end
|
76
75
|
|
77
76
|
def test_max_tasks
|
78
|
-
|
79
|
-
|
80
|
-
wq
|
81
|
-
|
82
|
-
wq.enqueue_b { sleep
|
83
|
-
assert_equal
|
84
|
-
wq.enqueue_b { sleep(0.01) }
|
85
|
-
assert_equal(wq.cur_tasks, 1)
|
77
|
+
wq = WorkQueue.new 1, 1
|
78
|
+
wq.enqueue_b { sleep 0.01 }
|
79
|
+
wq.enqueue_b { sleep 0.01 }
|
80
|
+
assert_equal 1, wq.cur_tasks
|
81
|
+
wq.enqueue_b { sleep 0.01 }
|
82
|
+
assert_equal 1, wq.cur_tasks
|
86
83
|
wq.join
|
87
84
|
end
|
85
|
+
|
86
|
+
def test_max_tasks_validation
|
87
|
+
assert_raise(ArgumentError) { WorkQueue.new nil, 0 }
|
88
|
+
assert_raise(ArgumentError) { WorkQueue.new nil, -1 }
|
89
|
+
end
|
88
90
|
|
89
91
|
def test_stress
|
90
|
-
|
92
|
+
i = 0
|
91
93
|
m = Mutex.new
|
92
|
-
wq = WorkQueue.new
|
93
|
-
(1..
|
94
|
-
wq.enqueue_b
|
95
|
-
sleep
|
96
|
-
|
94
|
+
wq = WorkQueue.new 100, 200
|
95
|
+
(1..10000).each do
|
96
|
+
wq.enqueue_b {
|
97
|
+
sleep 0.01
|
98
|
+
m.synchronize { i += 1 }
|
97
99
|
}
|
98
100
|
end
|
99
101
|
wq.join
|
100
|
-
assert_equal
|
102
|
+
assert_equal 10000, i
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_stress_prolonged
|
106
|
+
i = 0
|
107
|
+
m = Mutex.new
|
108
|
+
wq = WorkQueue.new 100, 200
|
109
|
+
(1..10000).each do
|
110
|
+
wq.enqueue_b {
|
111
|
+
sleep rand(5)
|
112
|
+
m.synchronize { i += 1 }
|
113
|
+
}
|
114
|
+
end
|
115
|
+
wq.join
|
116
|
+
assert_equal 10000, i
|
101
117
|
end
|
102
118
|
|
103
119
|
def test_kill
|
104
120
|
s = String.new
|
105
121
|
wq = WorkQueue.new
|
106
|
-
wq.enqueue_b(s) { |str|
|
122
|
+
wq.enqueue_b(s) { |str|
|
123
|
+
sleep 0.1
|
124
|
+
str.replace "Hello"
|
125
|
+
}
|
107
126
|
wq.kill
|
108
|
-
|
127
|
+
assert(s.empty?)
|
109
128
|
end
|
110
|
-
|
111
129
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: work_queue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
|
+
- 5
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 2.0.1
|
10
|
+
version: 2.5.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Miguel Fonseca
|
@@ -15,11 +15,11 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2012-04-19 00:00:00 Z
|
19
19
|
dependencies: []
|
20
20
|
|
21
21
|
description:
|
22
|
-
email:
|
22
|
+
email: contact@miguelfonseca.com
|
23
23
|
executables: []
|
24
24
|
|
25
25
|
extensions: []
|
@@ -50,12 +50,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
51
51
|
- - ">="
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
hash:
|
53
|
+
hash: 57
|
54
54
|
segments:
|
55
55
|
- 1
|
56
56
|
- 8
|
57
|
-
-
|
58
|
-
version: 1.8.
|
57
|
+
- 7
|
58
|
+
version: 1.8.7
|
59
59
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
60
|
none: false
|
61
61
|
requirements:
|
@@ -68,7 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
68
|
requirements: []
|
69
69
|
|
70
70
|
rubyforge_project:
|
71
|
-
rubygems_version: 1.8.
|
71
|
+
rubygems_version: 1.8.22
|
72
72
|
signing_key:
|
73
73
|
specification_version: 3
|
74
74
|
summary: A tunable work queue, designed to coordinate work between a producer and a pool of worker threads.
|