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 CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009-2010 Miguel Fonseca
1
+ Copyright (c) 2012 Miguel Fonseca
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
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 resource use, by setting a limit on the maximum number of simultaneously executing threads;
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
- Note that you generally want to bound the resources used:
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(10)
33
+ WorkQueue.new 10, nil
33
34
  # Limit the maximum number of queued tasks
34
- WorkQueue.new(nil,20)
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.0.0
12
+ # 2.5.0
10
13
  #
11
14
  # == Author
12
- # Miguel Fonseca <fmmfonseca@gmail.com>
15
+ # Miguel Fonseca <contact@miguelfonseca.com>
13
16
  #
14
17
  # == Copyright
15
- # Copyright 2009-2011 Miguel Fonseca
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
- VERSION = "2.0.1"
38
-
39
- ##
40
- # Creates a new work queue with the desired parameters.
41
- #
42
- # wq = WorkQueue.new(5,10,20)
43
- #
44
- def initialize(max_threads=nil, max_tasks=nil)
45
- self.max_threads = max_threads
46
- self.max_tasks = max_tasks
47
- @threads = Array.new
48
- @threads.extend(MonitorMixin)
49
- @threads_waiting = 0
50
- @tasks = Array.new
51
- @tasks.extend(MonitorMixin)
52
- @task_enqueued = @tasks.new_cond
53
- @task_completed = @tasks.new_cond
54
- @cur_tasks = 0
55
- end
56
-
57
- ##
58
- # Returns the maximum number of worker threads.
59
- # This value is set upon initialization and cannot be changed afterwards.
60
- #
61
- # wq = WorkQueue.new()
62
- # wq.max_threads #=> Infinity
63
- # wq = WorkQueue.new(1)
64
- # wq.max_threads #=> 1
65
- #
66
- def max_threads
67
- @max_threads
68
- end
69
-
70
- ##
71
- # Returns the current number of worker threads.
72
- # This value is just a snapshot, and may change immediately upon returning.
73
- #
74
- # wq = WorkQueue.new(10)
75
- # wq.cur_threads #=> 0
76
- # wq.enqueue_b {}
77
- # wq.cur_threads #=> 1
78
- #
79
- def cur_threads
80
- @threads.size
81
- end
82
-
83
- ##
84
- # Returns the maximum number of queued tasks.
85
- # This value is set upon initialization and cannot be changed afterwards.
86
- #
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 active tasks.
98
- # This value is just a snapshot, and may change immediately upon returning.
99
- #
100
- # wq = WorkQueue.new(1)
101
- # wq.enqueue_b { sleep(1) }
102
- # wq.cur_tasks #=> 0
103
- # wq.enqueue_b {}
104
- # wq.cur_tasks #=> 1
105
- #
106
- def cur_tasks
107
- @cur_tasks
108
- end
109
-
110
- ##
111
- # Schedules the given Proc for future execution by a worker thread.
112
- # If there is no space left in the queue, waits until space becomes available.
113
- #
114
- # wq = WorkQueue.new(1)
115
- # wq.enqueue_p(Proc.new {})
116
- #
117
- def enqueue_p(proc, *args)
118
- enqueue(proc, args)
119
- end
120
-
121
- ##
122
- # Schedules the given Block for future execution by a worker thread.
123
- # If there is no space left in the queue, waits until space becomes available.
124
- #
125
- # wq = WorkQueue.new(1)
126
- # wq.enqueue_b {}
127
- #
128
- def enqueue_b(*args, &block)
129
- enqueue(block, args)
130
- end
131
-
132
- ##
133
- # Waits until the tasks queue is empty and all worker threads have finished.
134
- #
135
- # wq = WorkQueue.new(1)
136
- # wq.enqueue_b { sleep(1) }
137
- # wq.join
138
- #
139
- def join
140
- @tasks.synchronize do
141
- @task_completed.wait_while { cur_tasks > 0 }
142
- end
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
- # Stops all worker threads immediately, aborting any ongoing tasks.
147
- #
148
- # wq = WorkQueue.new(1)
149
- # wq.enqueue_b { sleep(1) }
150
- # wq.kill
151
- #
152
- def kill
153
- @threads.dup.each { |thread| thread.exit.join }
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
- @tasks.clear
157
- @cur_tasks = 0
158
- end
159
-
160
- private
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
- # Sets the maximum number of worker threads.
177
- #
178
- def max_threads=(value)
179
- raise ArgumentError, "the maximum number of threads must be positive" if value and value <= 0
180
- @max_threads = value || 1.0/0
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
- # Sets the maximum number of queued tasks.
185
- #
186
- def max_tasks=(value)
187
- raise ArgumentError, "the maximum number of tasks must be positive" if value and value <= 0
188
- @max_tasks = value || 1.0/0
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
- # Enrolls a new worker thread.
193
- # The request is only carried out if necessary.
194
- #
195
- def spawn_thread
196
- @threads.synchronize do
197
- if cur_threads < max_threads and @threads_waiting <= 0 and @tasks.size > 0
198
- @threads << Thread.new do
199
- begin
200
- work
201
- ensure
202
- @threads.synchronize do
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
- # Repeatedly process the tasks queue.
214
- #
215
- def work
216
- loop do
217
- begin
218
- proc, args = @tasks.synchronize do
219
- @threads_waiting += 1
220
- @task_enqueued.wait_while { @tasks.size <= 0 }
221
- @threads_waiting -= 1
222
- @tasks.shift
223
- end
224
- proc.call(*args)
225
- rescue Exception => e
226
- # Suppress Exception
227
- ensure
228
- @tasks.synchronize do
229
- @cur_tasks -= 1
230
- @task_completed.broadcast
231
- end
232
- end
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
@@ -6,10 +6,10 @@
6
6
  # This file contains unit tests for the WorkQueue class.
7
7
  #
8
8
  # == Author
9
- # Miguel Fonseca <fmmfonseca@gmail.com>
9
+ # Miguel Fonseca <contact@miguelfonseca.com>
10
10
  #
11
11
  # == Copyright
12
- # Copyright 2009-2010 Miguel Fonseca
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
- # using proc
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(s, "Hello #1")
35
- # using block
36
- wq.enqueue_b(s) { |str| str.replace("Hello #2") }
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(s, "Hello #2")
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("Hello #1") }
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(s, "Hello #1")
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(wq.cur_threads, 1)
53
+ assert_equal 1, wq.cur_threads
58
54
  wq.enqueue_b { sleep 0.01 }
59
- assert_equal(wq.cur_threads, 1)
55
+ assert_equal 1, wq.cur_threads
60
56
  wq.join
61
57
  end
62
58
 
63
59
  def test_max_threads
64
- assert_raise(ArgumentError) { WorkQueue.new(0) }
65
- assert_raise(ArgumentError) { WorkQueue.new(-1) }
66
- wq = WorkQueue.new(1)
67
- assert_equal(wq.cur_threads, 0)
68
- wq.enqueue_b { sleep(0.01) }
69
- assert_equal(wq.cur_threads, 1)
70
- wq.enqueue_b { sleep(0.01) }
71
- assert_equal(wq.cur_threads, 1)
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
- assert_raise(ArgumentError) { WorkQueue.new(nil,0) }
79
- assert_raise(ArgumentError) { WorkQueue.new(nil,-1) }
80
- wq = WorkQueue.new(1,1)
81
- wq.enqueue_b { sleep(0.01) }
82
- wq.enqueue_b { sleep(0.01) }
83
- assert_equal(wq.cur_tasks, 1)
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
- a = []
92
+ i = 0
91
93
  m = Mutex.new
92
- wq = WorkQueue.new(100,200)
93
- (1..1000).each do
94
- wq.enqueue_b(a,m) { |str,mut|
95
- sleep(0.01)
96
- mut.synchronize { a.push nil }
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(a.size, 1000)
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| sleep 0.1; str.replace("Hello") }
122
+ wq.enqueue_b(s) { |str|
123
+ sleep 0.1
124
+ str.replace "Hello"
125
+ }
107
126
  wq.kill
108
- assert_equal(s, "")
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: 13
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
+ - 5
8
9
  - 0
9
- - 1
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: 2011-11-21 00:00:00 Z
18
+ date: 2012-04-19 00:00:00 Z
19
19
  dependencies: []
20
20
 
21
21
  description:
22
- email: fmmfonseca@gmail.com
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: 59
53
+ hash: 57
54
54
  segments:
55
55
  - 1
56
56
  - 8
57
- - 6
58
- version: 1.8.6
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.11
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.