work_queue 2.0.1 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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.